From 80914437d8ff6822b243b7127fec46ef2656d370 Mon Sep 17 00:00:00 2001 From: Hjort Date: Thu, 15 Feb 2024 17:26:38 +0100 Subject: [PATCH 01/18] Add methods to check if statements are satisfied by an identity --- .../wallet/identityobject/IdentityObject.java | 10 ++++ .../MissingAttributeException.java | 14 +++++ .../wallet/web3Id/CredentialAttribute.java | 59 ++++++++++++++++++- .../web3Id/Statement/AtomicStatement.java | 15 +++++ .../web3Id/Statement/MembershipStatement.java | 13 ++++ .../Statement/NonMembershipStatement.java | 11 ++++ .../web3Id/Statement/RangeStatement.java | 14 ++++- .../web3Id/Statement/RequestStatement.java | 21 +++++++ .../web3Id/Statement/RevealStatement.java | 15 +++++ .../accountinfo/credential/AttributeType.java | 7 ++- 10 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/MissingAttributeException.java diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java index 216cb99b8..104f5ba7a 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java @@ -1,6 +1,9 @@ package com.concordium.sdk.crypto.wallet.identityobject; +import com.concordium.sdk.responses.accountinfo.credential.AttributeType; + import lombok.Getter; +import lombok.val; @Getter public class IdentityObject { @@ -9,4 +12,11 @@ public class IdentityObject { private PreIdentityObject preIdentityObject; private String signature; + public String getChosenAttribute(AttributeType attribute) throws MissingAttributeException { + val chosenAttributes = this.getAttributeList().getChosenAttributes(); + if (!chosenAttributes.containsKey(attribute)) { + throw new MissingAttributeException(attribute); + } + return chosenAttributes.get(attribute); + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/MissingAttributeException.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/MissingAttributeException.java new file mode 100644 index 000000000..f84f0e292 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/MissingAttributeException.java @@ -0,0 +1,14 @@ +package com.concordium.sdk.crypto.wallet.identityobject; + +import com.concordium.sdk.responses.accountinfo.credential.AttributeType; + +import lombok.Getter; + +@Getter +public class MissingAttributeException extends Exception { + private AttributeType attribute; + + public MissingAttributeException(AttributeType attribute) { + this.attribute = attribute; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java index 6505f8a46..6f881dbf7 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java @@ -1,6 +1,8 @@ package com.concordium.sdk.crypto.wallet.web3Id; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParseException; @@ -21,8 +23,8 @@ @JsonDeserialize(using = CredentialAttribute.CredentialAttributieTypeDeserializer.class) @Builder @Getter -public class CredentialAttribute { - enum CredentialAttributeType { +public final class CredentialAttribute { + public enum CredentialAttributeType { INT, STRING, TIMESTAMP; @@ -31,8 +33,59 @@ enum CredentialAttributeType { private String value; private CredentialAttributeType type; - static class CredentialAttributieTypeDeserializer extends JsonDeserializer { + private int compareStringAttributes(byte[] aBytes, byte[] bBytes) { + if (aBytes.length < bBytes.length) return -1; + if (aBytes.length > bBytes.length) return 1; + + for (int i = 0; i < aBytes.length; i++) { + byte aByte = aBytes[i]; + byte bByte = bBytes[i]; + + if (aByte == bByte) continue; + return aByte < bByte ? -1 : 1; + } + + return 0; + } + + public boolean isBetween(CredentialAttribute lower, CredentialAttribute upper) throws Exception { + if (!this.getType().equals(lower.getType()) || !this.getType().equals(upper.getType())) { + return false; // TODO should throw instead? + } + switch (this.type) { + case INT: { + long lowerVal = Long.parseLong(lower.getValue()); + long upperVal = Long.parseLong(upper.getValue()); + long val = Long.parseLong(this.getValue()); + return lowerVal <= val && val < upperVal; + } + case TIMESTAMP: { + LocalDateTime lowerVal = LocalDateTime.parse(lower.getValue()); + LocalDateTime upperVal = LocalDateTime.parse(upper.getValue()); + LocalDateTime val = LocalDateTime.parse(this.getValue()); + return !lowerVal.isAfter(val) && upperVal.isAfter(val); + } + case STRING: { + byte[] lowerVal = lower.getValue().getBytes(StandardCharsets.UTF_8); + byte[] upperVal = upper.getValue().getBytes(StandardCharsets.UTF_8); + byte[] val = this.getValue().getBytes(StandardCharsets.UTF_8); + return this.compareStringAttributes(val, lowerVal) >= 0 && this.compareStringAttributes(val, upperVal) < 0; + } + default: + throw new Exception(); // TODO better type + } + } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CredentialAttribute)) { + return false; + } + CredentialAttribute cred = (CredentialAttribute) obj; + return this.type.equals(cred.type) && this.value.equals(cred.value); + } + + static class CredentialAttributieTypeDeserializer extends JsonDeserializer { @Override public CredentialAttribute deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java index 71359d68e..f0e545093 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java @@ -8,9 +8,24 @@ import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; +import com.concordium.sdk.responses.accountinfo.credential.AttributeType; + @JsonTypeInfo(use = NAME, include = As.PROPERTY, property = "type", visible = true) @JsonSubTypes ({@Type (value = RevealStatement.class), @Type (value = RangeStatement.class), @Type (value = MembershipStatement.class), @Type (value = NonMembershipStatement.class)}) public abstract class AtomicStatement { @JsonProperty("attributeTag") public abstract String getAttributeTag(); + + // TODO: add overload for web3Id credential + protected CredentialAttribute getAttributeValue(IdentityObject identityObject) throws Exception { + AttributeType type = AttributeType.fromJSON(this.getAttributeTag()); + String raw = identityObject.getAttributeList().getChosenAttributes().get(type); + return CredentialAttribute.builder().value(raw).type(CredentialAttribute.CredentialAttributeType.STRING).build(); + + } + + // TODO: add overload for web3Id credential + public abstract boolean canBeProvedBy(IdentityObject identityObject) throws Exception; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java index b2c13f225..bd08ae62b 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java @@ -2,6 +2,8 @@ import java.util.List; +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.fasterxml.jackson.annotation.JsonTypeName; @@ -12,4 +14,15 @@ public class MembershipStatement extends AtomicStatement { private String attributeTag; private List set; + + @Override + public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { + try { + CredentialAttribute value = this.getAttributeValue(identityObject); + return set.contains(value); + } catch (MissingAttributeException e) { + // If the identityObject does not have the relevant attribute, it does not satisfy the statement + return false; + } + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java index 5e391ccbb..c86ea4019 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java @@ -2,6 +2,7 @@ import java.util.List; +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.fasterxml.jackson.annotation.JsonTypeName; @@ -12,4 +13,14 @@ public class NonMembershipStatement extends AtomicStatement { private String attributeTag; private List set; + + @Override + public boolean canBeProvedBy(IdentityObject identityObject) { + try { + CredentialAttribute value = this.getAttributeValue(identityObject); + return !set.contains(value); + } catch (Exception e) { + return false; + } + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java index 3f7ab7e12..4747ddd78 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java @@ -1,7 +1,8 @@ package com.concordium.sdk.crypto.wallet.web3Id.Statement; +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; -import com.concordium.sdk.responses.accountinfo.credential.AttributeType; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.Builder; @@ -16,4 +17,15 @@ public class RangeStatement extends AtomicStatement { private CredentialAttribute lower; private CredentialAttribute upper; private String attributeTag; + @Override + public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { + try { + CredentialAttribute value = getAttributeValue(identityObject); + return value.isBetween(lower, upper); + } catch (MissingAttributeException e) { + // If the identityObject does not have the relevant attribute, it does not satisfy the statement + return false; + } + + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java index 4560c3562..1503e48df 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java @@ -1,6 +1,9 @@ package com.concordium.sdk.crypto.wallet.web3Id.Statement; import java.util.List; +import java.util.stream.Collectors; + +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; import lombok.Builder; import lombok.Getter; @@ -13,4 +16,22 @@ public class RequestStatement { private String id; private List type; private List statement; + + public List getUnsatisfiedStatements(IdentityObject identityObject) { + return statement.stream().filter(s -> { + try { + return !s.canBeProvedBy(identityObject); + } catch (Exception e) { + return false; + } + }).collect(Collectors.toList()); + } + + public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { + for (AtomicStatement s: this.statement) { + if (s.canBeProvedBy(identityObject)) continue; + return false; + } + return true; + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RevealStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RevealStatement.java index 705b1fad7..0ac9b7034 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RevealStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RevealStatement.java @@ -1,5 +1,7 @@ package com.concordium.sdk.crypto.wallet.web3Id.Statement; +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.fasterxml.jackson.annotation.JsonTypeName; import lombok.Getter; @@ -8,4 +10,17 @@ @JsonTypeName("RevealAttribute") public class RevealStatement extends AtomicStatement { private String attributeTag; + + @Override + public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { + try { + // Attempt to get the attribute + this.getAttributeValue(identityObject); + } catch (MissingAttributeException e) { + // If the identityObject does not have the relevant attribute, it does not satisfy the statement + return false; + } + // With a reveal statement, the only requirement is that the identity has the attribute. + return true; + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java index 7901fb1d0..b2b6d4184 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java @@ -1,5 +1,6 @@ package com.concordium.sdk.responses.accountinfo.credential; +import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.annotation.JsonProperty; /** @@ -36,5 +37,9 @@ public enum AttributeType { @JsonProperty("taxIdNo") TAX_ID_NO, @JsonProperty("lei") - LEI + LEI; + + public static AttributeType fromJSON(String jsonStr) throws Exception { + return JsonMapper.INSTANCE.readValue(jsonStr, AttributeType.class); + } } From a96e85087559cb2dc47f492541e8c790f9a3429b Mon Sep 17 00:00:00 2001 From: Hjort Date: Mon, 19 Feb 2024 11:12:44 +0100 Subject: [PATCH 02/18] Add Tests --- .../accountinfo/credential/AttributeType.java | 2 +- .../sdk/crypto/wallet/web3Id/ProofTest.java | 2 +- .../wallet/web3Id/RequestStatementTest.java | 54 +++++++++++++++++++ .../wallet/web3Id/accountRequestUnder18.json | 38 +++++++++++++ 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java create mode 100644 concordium-sdk/src/test/testresources/wallet/web3Id/accountRequestUnder18.json diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java index b2b6d4184..038ce4b2f 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java @@ -40,6 +40,6 @@ public enum AttributeType { LEI; public static AttributeType fromJSON(String jsonStr) throws Exception { - return JsonMapper.INSTANCE.readValue(jsonStr, AttributeType.class); + return JsonMapper.INSTANCE.readValue(String.format("\"%s\"", jsonStr), AttributeType.class); } } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java index 134fbfaf8..e43721611 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java @@ -39,7 +39,7 @@ static String readFile(String path, Charset encoding) return new String(encoded, encoding); } - public Request loadRequest(String fileName) throws Exception { + static public Request loadRequest(String fileName) throws Exception { return JsonMapper.INSTANCE.readValue( readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), Request.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 new file mode 100644 index 000000000..cc20b32ba --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/RequestStatementTest.java @@ -0,0 +1,54 @@ +package com.concordium.sdk.crypto.wallet.web3Id; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.junit.Test; + +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; +import com.concordium.sdk.serializing.JsonMapper; + + +public class RequestStatementTest { + + static String readFile(String path, Charset encoding) + throws IOException { + byte[] encoded = Files.readAllBytes(Paths.get(path)); + return new String(encoded, encoding); + } + + private IdentityObject getIdentityObject() throws Exception { + return JsonMapper.INSTANCE.readValue( + readFile("./src/test/testresources/wallet/id_object.json", Charset.forName("UTF-8")), + IdentityObject.class); + } + + @Test + public void testCanIdentitySatisfyStatement() throws Exception { + IdentityObject identityObject = getIdentityObject(); + + List statements = ProofTest.loadRequest("accountRequest.json").getCredentialStatements(); + RequestStatement requestStatement1 = statements.get(0); + assertTrue(requestStatement1.canBeProvedBy(identityObject)); + } + + @Test + public void testGetsUnsatisfiedStatement() throws Exception { + IdentityObject identityObject = getIdentityObject(); + + List statements = ProofTest.loadRequest("accountRequestUnder18.json").getCredentialStatements(); + RequestStatement requestStatement1 = statements.get(0); + List unsatisfied = requestStatement1.getUnsatisfiedStatements(identityObject); + assertEquals(1, unsatisfied.size()); + assertEquals("dob", unsatisfied.get(0).getAttributeTag()); + assertEquals(requestStatement1.getStatement().get(0), unsatisfied.get(0)); + } +} diff --git a/concordium-sdk/src/test/testresources/wallet/web3Id/accountRequestUnder18.json b/concordium-sdk/src/test/testresources/wallet/web3Id/accountRequestUnder18.json new file mode 100644 index 000000000..90c4d1317 --- /dev/null +++ b/concordium-sdk/src/test/testresources/wallet/web3Id/accountRequestUnder18.json @@ -0,0 +1,38 @@ +{ + "challenge": "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", + "credentialStatements": [ + { + "statement": [ + { + "type": "AttributeInRange", + "attributeTag": "dob", + "upper": "20060215", + "lower": "99990101" + }, + { + "type": "RevealAttribute", + "attributeTag": "firstName" + }, + { + "type": "AttributeInSet", + "attributeTag": "nationality", + "set": [ + "DK", + "FR", + "GB" + ] + }, + { + "type": "AttributeNotInSet", + "attributeTag": "countryOfResidence", + "set": [ + "FR", + "GB", + "DE" + ] + } + ], + "id": "did:ccd:testnet:cred:a88a8214fc7a7f11aeda54661b76a1fd7c67e15278b83a85ec92cb799ef0abaa3b7c61a7e90ea6bb108fa2ca1a3ba217" + } + ] +} From 5278162c3b674b91df6f0ca1274d3a304fbeb420 Mon Sep 17 00:00:00 2001 From: Hjort Date: Mon, 19 Feb 2024 12:52:05 +0100 Subject: [PATCH 03/18] Fix TODOs + formatting --- .../crypto/wallet/web3Id/CredentialAttribute.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java index 6f881dbf7..997242c88 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java @@ -48,9 +48,9 @@ private int compareStringAttributes(byte[] aBytes, byte[] bBytes) { return 0; } - public boolean isBetween(CredentialAttribute lower, CredentialAttribute upper) throws Exception { + public boolean isBetween(CredentialAttribute lower, CredentialAttribute upper) throws IllegalArgumentException { if (!this.getType().equals(lower.getType()) || !this.getType().equals(upper.getType())) { - return false; // TODO should throw instead? + throw new IllegalArgumentException("Attribute types must match"); } switch (this.type) { case INT: { @@ -60,10 +60,10 @@ public boolean isBetween(CredentialAttribute lower, CredentialAttribute upper) t return lowerVal <= val && val < upperVal; } case TIMESTAMP: { - LocalDateTime lowerVal = LocalDateTime.parse(lower.getValue()); - LocalDateTime upperVal = LocalDateTime.parse(upper.getValue()); + LocalDateTime lowerVal = LocalDateTime.parse(lower.getValue()); + LocalDateTime upperVal = LocalDateTime.parse(upper.getValue()); LocalDateTime val = LocalDateTime.parse(this.getValue()); - return !lowerVal.isAfter(val) && upperVal.isAfter(val); + return !lowerVal.isAfter(val) && upperVal.isAfter(val); } case STRING: { byte[] lowerVal = lower.getValue().getBytes(StandardCharsets.UTF_8); @@ -72,7 +72,7 @@ public boolean isBetween(CredentialAttribute lower, CredentialAttribute upper) t return this.compareStringAttributes(val, lowerVal) >= 0 && this.compareStringAttributes(val, upperVal) < 0; } default: - throw new Exception(); // TODO better type + throw new IllegalArgumentException("Unknown attribute type"); } } From 3f8a49c8655b93af6734f3a1d4ed67fd984a4a00 Mon Sep 17 00:00:00 2001 From: Hjort Date: Mon, 19 Feb 2024 13:09:17 +0100 Subject: [PATCH 04/18] cleanup in tests --- .../sdk/crypto/wallet/CredentialTest.java | 38 ++------------- .../sdk/crypto/wallet/FileHelpers.java | 48 +++++++++++++++++++ .../sdk/crypto/wallet/IdentityTest.java | 45 ++--------------- .../sdk/crypto/wallet/web3Id/ProofTest.java | 31 ++---------- .../wallet/web3Id/RequestStatementTest.java | 24 ++-------- 5 files changed, 65 insertions(+), 121 deletions(-) create mode 100644 concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/FileHelpers.java diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java index 443d785be..2c1ad91b3 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java @@ -32,36 +32,6 @@ public class CredentialTest { - static String readFile(String path, Charset encoding) - throws IOException { - byte[] encoded = Files.readAllBytes(Paths.get(path)); - return new String(encoded, encoding); - } - - private CryptographicParameters getCryptographicParameters() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/global.json", Charset.forName("UTF-8")), - CryptographicParameters.class); - } - - private IdentityProviderInfo getIdentityProviderInfo() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/ip_info.json", Charset.forName("UTF-8")), - IdentityProviderInfo.class); - } - - private Map getAnonymityRevokerInfos() throws Exception { - MapType mapType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, - AnonymityRevokerInfo.class); - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/ars_infos.json", Charset.forName("UTF-8")), mapType); - } - - private IdentityObject getIdentityObject() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/id_object.json", Charset.forName("UTF-8")), - IdentityObject.class); } - private static String TEST_SEED = "efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860"; @Test @@ -76,10 +46,10 @@ public void createUnsignedCredential() throws Exception { } UnsignedCredentialInput input = UnsignedCredentialInput.builder() - .ipInfo(getIdentityProviderInfo()) - .globalContext(getCryptographicParameters()) - .arsInfos(getAnonymityRevokerInfos()) - .idObject(getIdentityObject()) + .ipInfo(FileHelpers.getIdentityProviderInfo()) + .globalContext(FileHelpers.getCryptographicParameters()) + .arsInfos(FileHelpers.getAnonymityRevokerInfos()) + .idObject(FileHelpers.getIdentityObject()) .credNumber(credentialCounter) .attributeRandomness(attributeRandomness) .blindingRandomness(wallet.getSignatureBlindingRandomness(0, 0)) diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/FileHelpers.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/FileHelpers.java new file mode 100644 index 000000000..96421ccea --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/FileHelpers.java @@ -0,0 +1,48 @@ +package com.concordium.sdk.crypto.wallet; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; + +import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.responses.blocksummary.updates.queues.AnonymityRevokerInfo; +import com.concordium.sdk.responses.blocksummary.updates.queues.IdentityProviderInfo; +import com.concordium.sdk.responses.cryptographicparameters.CryptographicParameters; +import com.concordium.sdk.serializing.JsonMapper; +import com.fasterxml.jackson.databind.type.MapType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class FileHelpers { + public static String readFile(String path, Charset encoding) + throws IOException { + byte[] encoded = Files.readAllBytes(Paths.get(path)); + return new String(encoded, encoding); + } + + public static CryptographicParameters getCryptographicParameters() throws Exception { + return JsonMapper.INSTANCE.readValue( + readFile("./src/test/testresources/wallet/global.json", Charset.forName("UTF-8")), + CryptographicParameters.class); + } + + public static IdentityProviderInfo getIdentityProviderInfo() throws Exception { + return JsonMapper.INSTANCE.readValue( + readFile("./src/test/testresources/wallet/ip_info.json", Charset.forName("UTF-8")), + IdentityProviderInfo.class); + } + + public static Map getAnonymityRevokerInfos() throws Exception { + MapType mapType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, + AnonymityRevokerInfo.class); + return JsonMapper.INSTANCE.readValue( + readFile("./src/test/testresources/wallet/ars_infos.json", Charset.forName("UTF-8")), mapType); + } + + public static IdentityObject getIdentityObject() throws Exception { + return JsonMapper.INSTANCE.readValue( + readFile("./src/test/testresources/wallet/id_object.json", Charset.forName("UTF-8")), + IdentityObject.class); } + +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/IdentityTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/IdentityTest.java index cef3959ef..0af9ea84e 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/IdentityTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/IdentityTest.java @@ -2,51 +2,14 @@ import static org.junit.Assert.assertTrue; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Map; - import org.junit.Test; import com.concordium.sdk.crypto.bls.BLSSecretKey; -import com.concordium.sdk.responses.blocksummary.updates.queues.AnonymityRevokerInfo; -import com.concordium.sdk.responses.blocksummary.updates.queues.IdentityProviderInfo; -import com.concordium.sdk.responses.cryptographicparameters.CryptographicParameters; -import com.concordium.sdk.serializing.JsonMapper; -import com.fasterxml.jackson.databind.type.MapType; -import com.fasterxml.jackson.databind.type.TypeFactory; public class IdentityTest { private static String TEST_SEED = "efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860"; - static String readFile(String path, Charset encoding) - throws IOException { - byte[] encoded = Files.readAllBytes(Paths.get(path)); - return new String(encoded, encoding); - } - - private CryptographicParameters getCryptographicParameters() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/global.json", Charset.forName("UTF-8")), - CryptographicParameters.class); - } - - private IdentityProviderInfo getIdentityProviderInfo() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/ip_info.json", Charset.forName("UTF-8")), - IdentityProviderInfo.class); - } - - private Map getAnonymityRevokerInfos() throws Exception { - MapType mapType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, - AnonymityRevokerInfo.class); - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/ars_infos.json", Charset.forName("UTF-8")), mapType); - } - @Test public void testCreatingIdentityRequest() throws Exception { ConcordiumHdWallet wallet = ConcordiumHdWallet.fromHex(TEST_SEED, Network.TESTNET); @@ -55,9 +18,9 @@ public void testCreatingIdentityRequest() throws Exception { String blindingRandomness = wallet.getSignatureBlindingRandomness(0, 0); IdentityRequestInput input = IdentityRequestInput.builder() - .globalContext(getCryptographicParameters()) - .ipInfo(getIdentityProviderInfo()) - .arsInfos(getAnonymityRevokerInfos()) + .globalContext(FileHelpers.getCryptographicParameters()) + .ipInfo(FileHelpers.getIdentityProviderInfo()) + .arsInfos(FileHelpers.getAnonymityRevokerInfos()) .arThreshold(2) .idCredSec(idCredSec) .prfKey(prfKey) @@ -73,7 +36,7 @@ public void testCreatingIdentityRequest() throws Exception { @Test public void testCreatingIdentityRecoveryRequest() throws Exception { IdentityRecoveryRequestInput input = IdentityRecoveryRequestInput.builder() - .globalContext(getCryptographicParameters()).ipInfo(getIdentityProviderInfo()).timestamp(0) + .globalContext(FileHelpers.getCryptographicParameters()).ipInfo(FileHelpers.getIdentityProviderInfo()).timestamp(0) .idCredSec(BLSSecretKey.from("7392eb0b4840c8a6f9314e99a8dd3e2c3663a1e615d8820851e3abd2965fab18")) .build(); diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java index e43721611..caabf2159 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java @@ -1,25 +1,17 @@ package com.concordium.sdk.crypto.wallet.web3Id; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.contains; import static org.junit.Assert.assertThat; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; - -import java.io.IOException; import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Iterator; import java.util.List; import org.junit.Test; -import com.concordium.sdk.Range; +import com.concordium.sdk.crypto.wallet.FileHelpers; import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.MembershipStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.NonMembershipStatement; @@ -27,38 +19,25 @@ import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RevealStatement; import com.concordium.sdk.responses.accountinfo.credential.AttributeType; -import com.concordium.sdk.responses.cryptographicparameters.CryptographicParameters; import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.core.type.TypeReference; public class ProofTest { - static String readFile(String path, Charset encoding) - throws IOException { - byte[] encoded = Files.readAllBytes(Paths.get(path)); - return new String(encoded, encoding); - } - static public Request loadRequest(String fileName) throws Exception { return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), + FileHelpers.readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), Request.class ); } public List loadCommitmentInputs(String fileName) throws Exception { return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), + FileHelpers.readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), new TypeReference>(){} ); } - private CryptographicParameters getCryptographicParameters() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/global.json", Charset.forName("UTF-8")), - CryptographicParameters.class); - } - @Test public void testCanParseRequest() throws Exception { Request request = loadRequest("accountRequest.json"); @@ -126,7 +105,7 @@ public void testCanCreateProofForAccountCredential() throws Exception { Web3IdProofInput input = Web3IdProofInput.builder() .request(loadRequest("accountRequest.json")) .commitmentInputs(loadCommitmentInputs("accountCommitments.json")) - .globalContext(getCryptographicParameters()) + .globalContext(FileHelpers.getCryptographicParameters()) .build(); String proof = Web3IdProof.getWeb3IdProof(input); assertNotNull(proof); @@ -170,7 +149,7 @@ public void testCanCreateProofForWeb3IdCredential() throws Exception { Web3IdProofInput input = Web3IdProofInput.builder() .request(loadRequest("web3IdRequest.json")) .commitmentInputs(loadCommitmentInputs("web3IdCommitments.json")) - .globalContext(getCryptographicParameters()) + .globalContext(FileHelpers.getCryptographicParameters()) .build(); String proof = Web3IdProof.getWeb3IdProof(input); assertNotNull(proof); 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 cc20b32ba..79767e452 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 @@ -3,37 +3,21 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; import org.junit.Test; +import com.concordium.sdk.crypto.wallet.FileHelpers; import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; -import com.concordium.sdk.serializing.JsonMapper; public class RequestStatementTest { - - static String readFile(String path, Charset encoding) - throws IOException { - byte[] encoded = Files.readAllBytes(Paths.get(path)); - return new String(encoded, encoding); - } - - private IdentityObject getIdentityObject() throws Exception { - return JsonMapper.INSTANCE.readValue( - readFile("./src/test/testresources/wallet/id_object.json", Charset.forName("UTF-8")), - IdentityObject.class); - } - + @Test public void testCanIdentitySatisfyStatement() throws Exception { - IdentityObject identityObject = getIdentityObject(); + IdentityObject identityObject = FileHelpers.getIdentityObject(); List statements = ProofTest.loadRequest("accountRequest.json").getCredentialStatements(); RequestStatement requestStatement1 = statements.get(0); @@ -42,7 +26,7 @@ public void testCanIdentitySatisfyStatement() throws Exception { @Test public void testGetsUnsatisfiedStatement() throws Exception { - IdentityObject identityObject = getIdentityObject(); + IdentityObject identityObject = FileHelpers.getIdentityObject(); List statements = ProofTest.loadRequest("accountRequestUnder18.json").getCredentialStatements(); RequestStatement requestStatement1 = statements.get(0); From 4976830a3d30830681e2f832112bb2043dbbafa9 Mon Sep 17 00:00:00 2001 From: orhoj Date: Mon, 19 Feb 2024 13:45:53 +0100 Subject: [PATCH 05/18] Use updated SDK with changed Nonce type --- .../concordium/example/wallet/activities/AccountActivity.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/activities/AccountActivity.kt b/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/activities/AccountActivity.kt index c1f2f4f85..230d8c7d6 100644 --- a/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/activities/AccountActivity.kt +++ b/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/activities/AccountActivity.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.unit.sp import com.concordium.sdk.crypto.ed25519.ED25519SecretKey import com.concordium.sdk.requests.AccountQuery import com.concordium.sdk.requests.BlockQuery -import com.concordium.sdk.transactions.AccountNonce import com.concordium.sdk.transactions.CCDAmount import com.concordium.sdk.transactions.Expiry import com.concordium.sdk.transactions.Index @@ -70,13 +69,13 @@ class AccountActivity : ComponentActivity() { val client = ConcordiumClientService.getClient() val senderInfo = client.getAccountInfo(BlockQuery.BEST, AccountQuery.from(sender)) - val nonce = senderInfo.accountNonce + val nonce = senderInfo.nonce val transactionHash = client.sendTransaction( TransactionFactory.newTransfer() .sender(sender) .receiver(receiver) .amount(amount) - .nonce(AccountNonce.from(nonce)) + .nonce(nonce) .expiry(expiry) .signer(signer) .build() From 9eac0f736e090617132c3b12086ef1a91d981411 Mon Sep 17 00:00:00 2001 From: orhoj Date: Mon, 19 Feb 2024 13:46:08 +0100 Subject: [PATCH 06/18] Fix space bug in fromSeedPhrase --- .../java/com/concordium/example/wallet/Storage.kt | 5 +---- .../sdk/crypto/wallet/ConcordiumHdWallet.java | 2 +- .../sdk/crypto/wallet/ConcordiumHdWalletTest.java | 11 +++++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt b/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt index 4a47dfd0b..de95f0b23 100644 --- a/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt +++ b/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt @@ -3,8 +3,6 @@ package com.concordium.example.wallet import android.content.Context import android.content.SharedPreferences import androidx.activity.ComponentActivity -import cash.z.ecc.android.bip39.Mnemonics -import cash.z.ecc.android.bip39.toSeed import com.concordium.sdk.crypto.wallet.ConcordiumHdWallet /*** @@ -26,8 +24,7 @@ class Storage(context: Context) { @OptIn(ExperimentalStdlibApi::class) fun getWallet(): ConcordiumHdWallet { val seedPhrase = this.seedPhrase.get() - val seedAsHex = Mnemonics.MnemonicCode(seedPhrase!!.toCharArray()).toSeed().toHexString() - return ConcordiumHdWallet.fromHex(seedAsHex, Constants.NETWORK) + return ConcordiumHdWallet.fromSeedPhrase(seedPhrase, Constants.NETWORK) } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWallet.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWallet.java index 08a89bf47..9047eb82c 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWallet.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWallet.java @@ -82,7 +82,7 @@ public static ConcordiumHdWallet fromSeedPhrase(List seedPhrase, Network byte[] seed = MnemonicCode.toSeed(seedPhrase, ""); StringBuilder sb = new StringBuilder(); for (byte b : seed) { - sb.append(String.format("%02X ", b)); + sb.append(String.format("%02X", b)); } return new ConcordiumHdWallet(sb.toString(), network); } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWalletTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWalletTest.java index 4417ec8ed..142e39bea 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWalletTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/ConcordiumHdWalletTest.java @@ -60,6 +60,17 @@ public void testWalletFromValidSeedPhrase() throws IOException, MnemonicExceptio ConcordiumHdWallet.fromSeedPhrase(validSeedPhrase, Network.MAINNET); } + @Test + public void testWalletFromSeedAndFromPhraseProduceEqualIdCredSec() throws IOException, MnemonicException { + Network network = Network.MAINNET; + String validSeedPhrase = "plug pipe now victory right then canvas monkey treat weasel bacon skull that shaft rookie sell adjust chase trumpet depth asthma traffic code castle"; + + ConcordiumHdWallet walletFromPhrase = ConcordiumHdWallet.fromSeedPhrase(validSeedPhrase, network); + ConcordiumHdWallet walletFromSeed = ConcordiumHdWallet.fromHex("5f9649eb1aeb049e095324f5a012188f3a0eebb56ed622b683686b0d603f114b421a86bbfa2d60ac64c3841d0a0b944abc510b50546645083a7ac9acbee27d25", network); + + assertEquals(walletFromPhrase.getIdCredSec(0, 0), walletFromSeed.getIdCredSec(0, 0)); + } + private static String TEST_SEED = "efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860"; @Test From 0aa61ca4ec34a80e565e05f882decc41db369187 Mon Sep 17 00:00:00 2001 From: orhoj Date: Mon, 19 Feb 2024 13:48:37 +0100 Subject: [PATCH 07/18] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c1c10cd..43e78b9f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased changes - Added `Web3IdProof` class with `getWeb3IdProof` method to create Presentations. (And supporting classes) +- Fixed an issue where `ConcordiumHdWallet.fromSeedPhrase` always produced an invalid seed as hex. ## 7.0.0 - Make the `energy` parameter for invoking an instance `Optional`. From ce63403cd8daf1deee087d6b77acddc238ea7086 Mon Sep 17 00:00:00 2001 From: Hjort Date: Mon, 19 Feb 2024 17:19:34 +0100 Subject: [PATCH 08/18] Add unqualified statements --- .../sdk/crypto/wallet/web3Id/Request.java | 4 ++-- .../web3Id/Statement/AtomicStatement.java | 5 ++--- .../wallet/web3Id/Statement/IdQualifier.java | 11 +++++++++++ .../web3Id/Statement/IdentityQualifier.java | 13 +++++++++++++ .../Statement/QualifiedRequestStatement.java | 12 ++++++++++++ .../web3Id/Statement/RequestStatement.java | 9 +++------ .../UnqualifiedRequestStatement.java | 19 +++++++++++++++++++ .../sdk/crypto/wallet/web3Id/Web3IdProof.java | 3 ++- .../wallet/web3Id/Web3IdProofInput.java | 5 +++-- 9 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdentityQualifier.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java index 9d59b5edd..8aecedfd3 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java @@ -11,7 +11,7 @@ @Getter @Builder @Jacksonized -public class Request { +public class Request { private final String challenge; - private final List credentialStatements; + private final List credentialStatements; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java index f0e545093..70aef40b8 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java @@ -4,15 +4,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; - import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.concordium.sdk.responses.accountinfo.credential.AttributeType; -@JsonTypeInfo(use = NAME, include = As.PROPERTY, property = "type", visible = true) +@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type", visible = true) @JsonSubTypes ({@Type (value = RevealStatement.class), @Type (value = RangeStatement.class), @Type (value = MembershipStatement.class), @Type (value = NonMembershipStatement.class)}) public abstract class AtomicStatement { @JsonProperty("attributeTag") diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java new file mode 100644 index 000000000..efa5d38a6 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java @@ -0,0 +1,11 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type", visible = true) +@JsonSubTypes ({@Type (value = IdentityQualifier.class)}) +public class IdQualifier {} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdentityQualifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdentityQualifier.java new file mode 100644 index 000000000..81cd08abc --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdentityQualifier.java @@ -0,0 +1,13 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonTypeName; + +import lombok.Getter; + +@Getter +@JsonTypeName("cred") +public class IdentityQualifier extends IdQualifier { + private List issuers; +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java new file mode 100644 index 000000000..ba116bbe0 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java @@ -0,0 +1,12 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement; + +import lombok.Getter; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Getter +@SuperBuilder +@Jacksonized +public class QualifiedRequestStatement extends RequestStatement { + private String id; +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java index 1503e48df..3ec25d967 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java @@ -5,15 +5,12 @@ import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; -import lombok.Builder; import lombok.Getter; -import lombok.extern.jackson.Jacksonized; +import lombok.experimental.SuperBuilder; +@SuperBuilder @Getter -@Builder -@Jacksonized -public class RequestStatement { - private String id; +public abstract class RequestStatement { private List type; private List statement; diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java new file mode 100644 index 000000000..aeeeb3150 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java @@ -0,0 +1,19 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement; + +import com.concordium.sdk.crypto.wallet.Network; + +import lombok.Getter; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@Getter +@SuperBuilder +@Jacksonized +public class UnqualifiedRequestStatement extends RequestStatement { + private IdQualifier idQualifier; + + public QualifiedRequestStatement qualify(String credId, Network network) { + String did = "did:ccd:" + network.getValue().toLowerCase() + ":cred:" + credId; + return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java index 4e0333361..88c20c6ff 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java @@ -3,6 +3,7 @@ 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.QualifiedRequestStatement; import com.concordium.sdk.exceptions.CryptoJniException; import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.core.JsonProcessingException; @@ -18,7 +19,7 @@ public class Web3IdProof { * @param input the input required to generate the Presentiation * @return a Presentation serialized as JSON */ - public static String getWeb3IdProof(Web3IdProofInput input) { + public static String getWeb3IdProof(Web3IdProofInput input) { StringResult result = null; try { String jsonStr = CryptoJniNative.createWeb3IdProof(JsonMapper.INSTANCE.writeValueAsString(input)); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java index f1d13284f..c6a23e42a 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java @@ -2,6 +2,7 @@ import java.util.List; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; import com.concordium.sdk.responses.cryptographicparameters.CryptographicParameters; import lombok.Builder; @@ -14,8 +15,8 @@ @Getter @Builder @Jacksonized -public class Web3IdProofInput { - private final Request request; +public class Web3IdProofInput { + private final Request request; private final List commitmentInputs; private final CryptographicParameters globalContext; } From 104a3151a6efb7c39a7c36a75990f5c14194587e Mon Sep 17 00:00:00 2001 From: Hjort Date: Tue, 20 Feb 2024 11:22:03 +0100 Subject: [PATCH 09/18] Add test + split Request into two classes --- .../{Request.java => QualifiedRequest.java} | 6 +-- .../wallet/web3Id/UnqualifiedRequest.java | 32 ++++++++++++++ .../sdk/crypto/wallet/web3Id/Web3IdProof.java | 3 +- .../wallet/web3Id/Web3IdProofInput.java | 5 +-- .../sdk/crypto/wallet/CredentialTest.java | 11 ----- .../sdk/crypto/wallet/web3Id/ProofTest.java | 14 +++---- .../wallet/web3Id/RequestStatementTest.java | 42 +++++++++++++++++-- .../wallet/web3Id/unqualifiedRequest.json | 19 +++++++++ 8 files changed, 102 insertions(+), 30 deletions(-) rename concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/{Request.java => QualifiedRequest.java} (54%) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java create mode 100644 concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/QualifiedRequest.java similarity index 54% rename from concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java rename to concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/QualifiedRequest.java index 8aecedfd3..432f160c5 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Request.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/QualifiedRequest.java @@ -2,7 +2,7 @@ import java.util.List; -import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; import lombok.Builder; import lombok.Getter; @@ -11,7 +11,7 @@ @Getter @Builder @Jacksonized -public class Request { +public class QualifiedRequest { private final String challenge; - private final List credentialStatements; + private final List credentialStatements; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java new file mode 100644 index 000000000..1804beb76 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java @@ -0,0 +1,32 @@ +package com.concordium.sdk.crypto.wallet.web3Id; + +import java.util.List; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.concordium.sdk.crypto.wallet.Network; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.UnqualifiedRequestStatement; + +import lombok.Builder; +import lombok.Getter; +import lombok.extern.jackson.Jacksonized; + +@Getter +@Builder +@Jacksonized +public class UnqualifiedRequest { + private final String challenge; + private final List credentialStatements; + + public QualifiedRequest qualify(List credIds, Network network) { + if (credIds.size() != credentialStatements.size()) { + throw new IllegalArgumentException("List of credIds must be same size as credentialStatements"); + } + List qualifiedStatements = IntStream.range(0, credentialStatements.size()) + .mapToObj(index -> credentialStatements.get(index).qualify(credIds.get(index), network)) + .collect(Collectors.toList()); + return QualifiedRequest.builder().challenge(challenge).credentialStatements(qualifiedStatements).build(); + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java index 88c20c6ff..4e0333361 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProof.java @@ -3,7 +3,6 @@ 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.QualifiedRequestStatement; import com.concordium.sdk.exceptions.CryptoJniException; import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.core.JsonProcessingException; @@ -19,7 +18,7 @@ public class Web3IdProof { * @param input the input required to generate the Presentiation * @return a Presentation serialized as JSON */ - public static String getWeb3IdProof(Web3IdProofInput input) { + public static String getWeb3IdProof(Web3IdProofInput input) { StringResult result = null; try { String jsonStr = CryptoJniNative.createWeb3IdProof(JsonMapper.INSTANCE.writeValueAsString(input)); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java index c6a23e42a..5384dcfe5 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Web3IdProofInput.java @@ -2,7 +2,6 @@ import java.util.List; -import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; import com.concordium.sdk.responses.cryptographicparameters.CryptographicParameters; import lombok.Builder; @@ -15,8 +14,8 @@ @Getter @Builder @Jacksonized -public class Web3IdProofInput { - private final Request request; +public class Web3IdProofInput { + private final QualifiedRequest request; private final List commitmentInputs; private final CryptographicParameters globalContext; } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java index 2c1ad91b3..e5dcba5e5 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/CredentialTest.java @@ -2,10 +2,6 @@ import static org.junit.Assert.assertTrue; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -17,17 +13,10 @@ import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentDetails; import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentSerializationContext; import com.concordium.sdk.crypto.wallet.credential.UnsignedCredentialDeploymentInfoWithRandomness; -import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; import com.concordium.sdk.responses.accountinfo.credential.AttributeType; -import com.concordium.sdk.responses.blocksummary.updates.queues.AnonymityRevokerInfo; -import com.concordium.sdk.responses.blocksummary.updates.queues.IdentityProviderInfo; -import com.concordium.sdk.responses.cryptographicparameters.CryptographicParameters; -import com.concordium.sdk.serializing.JsonMapper; import com.concordium.sdk.transactions.CredentialPublicKeys; import com.concordium.sdk.transactions.Expiry; import com.concordium.sdk.transactions.Index; -import com.fasterxml.jackson.databind.type.MapType; -import com.fasterxml.jackson.databind.type.TypeFactory; public class CredentialTest { diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java index caabf2159..50e44189c 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java @@ -15,8 +15,8 @@ import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.MembershipStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.NonMembershipStatement; +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.RevealStatement; import com.concordium.sdk.responses.accountinfo.credential.AttributeType; import com.concordium.sdk.serializing.JsonMapper; @@ -24,10 +24,10 @@ public class ProofTest { - static public Request loadRequest(String fileName) throws Exception { + static public QualifiedRequest loadRequest(String fileName) throws Exception { return JsonMapper.INSTANCE.readValue( FileHelpers.readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), - Request.class + QualifiedRequest.class ); } @@ -40,11 +40,11 @@ public List loadCommitmentInputs(String fileName) throws Except @Test public void testCanParseRequest() throws Exception { - Request request = loadRequest("accountRequest.json"); + QualifiedRequest request = loadRequest("accountRequest.json"); assertEquals("beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", request.getChallenge()); - RequestStatement credentialStatement = request.getCredentialStatements().get(0); + QualifiedRequestStatement credentialStatement = request.getCredentialStatements().get(0); assertEquals("did:ccd:testnet:cred:a88a8214fc7a7f11aeda54661b76a1fd7c67e15278b83a85ec92cb799ef0abaa3b7c61a7e90ea6bb108fa2ca1a3ba217", credentialStatement.getId()); Iterator iter = credentialStatement.getStatement().iterator(); @@ -113,11 +113,11 @@ public void testCanCreateProofForAccountCredential() throws Exception { @Test public void testCanParseWeb3IdRequest() throws Exception { - Request request = loadRequest("web3IdRequest.json"); + QualifiedRequest request = loadRequest("web3IdRequest.json"); assertEquals("5d50c6e18aca83e991af81e7d7e760f5dc753db7966dc19e9aa42f25aef1696b", request.getChallenge()); - RequestStatement credentialStatement = request.getCredentialStatements().get(0); + QualifiedRequestStatement credentialStatement = request.getCredentialStatements().get(0); assertEquals("did:ccd:testnet:sci:6105:0/credentialEntry/31163ba14e30b834f1e97b9544d86df94883fd4f2c77e1d1fac0b6189c9e7996", credentialStatement.getId()); List type = credentialStatement.getType(); 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 79767e452..e625b359a 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 @@ -3,23 +3,39 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.junit.Test; +import com.concordium.sdk.crypto.wallet.ConcordiumHdWallet; 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.Statement.AtomicStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; - +import com.concordium.sdk.crypto.wallet.web3Id.Statement.UnqualifiedRequestStatement; +import com.concordium.sdk.serializing.JsonMapper; public class RequestStatementTest { - + + static public UnqualifiedRequest loadUnqualifiedRequest(String fileName) throws Exception { + return JsonMapper.INSTANCE.readValue( + FileHelpers.readFile("./src/test/testresources/wallet/web3Id/" + fileName, Charset.forName("UTF-8")), + UnqualifiedRequest.class); + } + + private static String TEST_SEED = "efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860"; + @Test public void testCanIdentitySatisfyStatement() throws Exception { IdentityObject identityObject = FileHelpers.getIdentityObject(); - List statements = ProofTest.loadRequest("accountRequest.json").getCredentialStatements(); + List statements = ProofTest.loadRequest("accountRequest.json") + .getCredentialStatements(); RequestStatement requestStatement1 = statements.get(0); assertTrue(requestStatement1.canBeProvedBy(identityObject)); } @@ -28,11 +44,29 @@ public void testCanIdentitySatisfyStatement() throws Exception { public void testGetsUnsatisfiedStatement() throws Exception { IdentityObject identityObject = FileHelpers.getIdentityObject(); - List statements = ProofTest.loadRequest("accountRequestUnder18.json").getCredentialStatements(); + List statements = ProofTest.loadRequest("accountRequestUnder18.json") + .getCredentialStatements(); RequestStatement requestStatement1 = statements.get(0); List unsatisfied = requestStatement1.getUnsatisfiedStatements(identityObject); assertEquals(1, unsatisfied.size()); assertEquals("dob", unsatisfied.get(0).getAttributeTag()); assertEquals(requestStatement1.getStatement().get(0), unsatisfied.get(0)); } + + @Test + public void testCanQualifyStatement() throws Exception { + UnqualifiedRequest request = loadUnqualifiedRequest("unqualifiedRequest.json"); + + Network network = Network.MAINNET; + ConcordiumHdWallet wallet = ConcordiumHdWallet.fromHex(TEST_SEED, network); + // Parameters taken from ConcordiumHdWalletTest.testMainnetCredentialId + QualifiedRequest qualifiedRequest = request.qualify(Arrays.asList(wallet.getCredentialId(10, 50, 5, + "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a8d45e64b6f917c540eee16c970c3d4b7f3caf48a7746284878e2ace21c82ea44bf84609834625be1f309988ac523fac")), + network); + QualifiedRequestStatement qualifiedStatement = qualifiedRequest.getCredentialStatements().get(0); + assertEquals( + "did:ccd:mainnet:cred:8a3a87f3f38a7a507d1e85dc02a92b8bcaa859f5cf56accb3c1bc7c40e1789b4933875a38dd4c0646ca3e940a02c42d8", + qualifiedStatement.getId()); + assertEquals(request.getCredentialStatements().get(0).getStatement(), qualifiedStatement.getStatement()); + } } diff --git a/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json b/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json new file mode 100644 index 000000000..9b3548c5f --- /dev/null +++ b/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json @@ -0,0 +1,19 @@ +{ + "challenge": "beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", + "credentialStatements": [ + { + "statement": [ + { + "type": "RevealAttribute", + "attributeTag": "firstName" + } + ], + "idQualifier": { + "type": "cred", + "issuers": [ + 0 + ] + } + } + ] +} From 53032f74510e7ab338185ecdf5bbf1046cbe7c47 Mon Sep 17 00:00:00 2001 From: Hjort Date: Tue, 20 Feb 2024 15:20:09 +0100 Subject: [PATCH 10/18] Fixes/cleanup/add web3IdIssuer qualifier --- .../wallet/web3Id/Statement/IdQualifier.java | 21 +++++++-- .../UnqualifiedRequestStatement.java | 34 +++++++++++++-- .../Statement/Web3IdIssuerQualifier.java | 14 ++++++ .../wallet/web3Id/UnqualifiedRequest.java | 23 +++++----- .../wallet/web3Id/RequestStatementTest.java | 43 +++++++++++++------ .../wallet/web3Id/unqualifiedRequest.json | 12 ++++++ 6 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java index efa5d38a6..701cce168 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java @@ -1,11 +1,26 @@ package com.concordium.sdk.crypto.wallet.web3Id.Statement; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; -@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type", visible = true) -@JsonSubTypes ({@Type (value = IdentityQualifier.class)}) -public class IdQualifier {} +import lombok.Getter; + +@Getter +@JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type", visible = true) +@JsonSubTypes({ @Type(value = IdentityQualifier.class), @Type(value = Web3IdIssuerQualifier.class) }) +public class IdQualifier { + @JsonUnwrapped + QualifierType type; + + public enum QualifierType { + @JsonProperty("cred") + Credential, + @JsonProperty("sci") + SmartContract + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java index aeeeb3150..d85641493 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java @@ -1,6 +1,11 @@ package com.concordium.sdk.crypto.wallet.web3Id.Statement; +import org.apache.commons.codec.binary.Hex; + +import com.concordium.sdk.crypto.ed25519.ED25519PublicKey; import com.concordium.sdk.crypto.wallet.Network; +import com.concordium.sdk.transactions.CredentialRegistrationId; +import com.concordium.sdk.types.ContractAddress; import lombok.Getter; import lombok.experimental.SuperBuilder; @@ -12,8 +17,31 @@ public class UnqualifiedRequestStatement extends RequestStatement { private IdQualifier idQualifier; - public QualifiedRequestStatement qualify(String credId, Network network) { - String did = "did:ccd:" + network.getValue().toLowerCase() + ":cred:" + credId; - return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); + public QualifiedRequestStatement qualify(CredentialRegistrationId credId, Network network) { + if (!idQualifier.getType().equals(IdQualifier.QualifierType.Credential)) { + throw new IllegalArgumentException("Only an account statement may only be qualified using a credentialId"); + } + String did = "did:ccd:" + network.getValue().toLowerCase() + ":cred:" + credId.getEncoded(); + return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); + } + + public QualifiedRequestStatement qualify(ContractAddress contract, ED25519PublicKey publicKey, Network network) { + if (!idQualifier.getType().equals(IdQualifier.QualifierType.SmartContract)) { + throw new IllegalArgumentException( + "Only an web3Id statement may only be qualified using a Contract Address"); + } + if (!((Web3IdIssuerQualifier) idQualifier).getIssuers().contains(contract)) { + throw new IllegalArgumentException("The Contract Address must be one specified in the qualifier"); + } + + String did = "did:ccd:" + + network.getValue().toLowerCase() + + ":sci:" + + contract.getIndex() + + ":" + + contract.getSubIndex() + + "/credentialEntry/" + + Hex.encodeHexString(publicKey.getRawBytes()); + return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java new file mode 100644 index 000000000..c9eda9829 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java @@ -0,0 +1,14 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement; + +import java.util.List; + +import com.concordium.sdk.types.ContractAddress; +import com.fasterxml.jackson.annotation.JsonTypeName; + +import lombok.Getter; + +@Getter +@JsonTypeName("sci") +public class Web3IdIssuerQualifier extends IdQualifier { + private List issuers; +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java index 1804beb76..1f7a2188e 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java @@ -1,14 +1,9 @@ package com.concordium.sdk.crypto.wallet.web3Id; import java.util.List; -import java.util.stream.Collector; import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import com.concordium.sdk.crypto.wallet.Network; import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.UnqualifiedRequestStatement; - import lombok.Builder; import lombok.Getter; import lombok.extern.jackson.Jacksonized; @@ -20,13 +15,19 @@ public class UnqualifiedRequest { private final String challenge; private final List credentialStatements; - public QualifiedRequest qualify(List credIds, Network network) { - if (credIds.size() != credentialStatements.size()) { - throw new IllegalArgumentException("List of credIds must be same size as credentialStatements"); - } - List qualifiedStatements = IntStream.range(0, credentialStatements.size()) - .mapToObj(index -> credentialStatements.get(index).qualify(credIds.get(index), network)) + /** + * Creates a qualified request, that can be used to generate a presentation. + * @param qualifier function to qualify the individual request statements. + * @return a QualifiedRequest with the same challenge, and with the statements + * transformed using the qualifier. + */ + public QualifiedRequest qualify(Qualifier qualifier) { + List qualifiedStatements = credentialStatements.stream().map(qualifier::qualify) .collect(Collectors.toList()); return QualifiedRequest.builder().challenge(challenge).credentialStatements(qualifiedStatements).build(); } + + static interface Qualifier { + QualifiedRequestStatement qualify(UnqualifiedRequestStatement statement); + } } 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 e625b359a..a31d4da11 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,21 +4,21 @@ import static org.junit.Assert.assertTrue; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.junit.Test; -import com.concordium.sdk.crypto.wallet.ConcordiumHdWallet; +import com.concordium.sdk.crypto.ed25519.ED25519PublicKey; 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.Statement.AtomicStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.IdQualifier; import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; -import com.concordium.sdk.crypto.wallet.web3Id.Statement.UnqualifiedRequestStatement; import com.concordium.sdk.serializing.JsonMapper; +import com.concordium.sdk.transactions.CredentialRegistrationId; +import com.concordium.sdk.types.ContractAddress; public class RequestStatementTest { @@ -28,8 +28,6 @@ static public UnqualifiedRequest loadUnqualifiedRequest(String fileName) throws UnqualifiedRequest.class); } - private static String TEST_SEED = "efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860"; - @Test public void testCanIdentitySatisfyStatement() throws Exception { IdentityObject identityObject = FileHelpers.getIdentityObject(); @@ -55,18 +53,35 @@ public void testGetsUnsatisfiedStatement() throws Exception { @Test public void testCanQualifyStatement() throws Exception { + // Arrange UnqualifiedRequest request = loadUnqualifiedRequest("unqualifiedRequest.json"); Network network = Network.MAINNET; - ConcordiumHdWallet wallet = ConcordiumHdWallet.fromHex(TEST_SEED, network); - // Parameters taken from ConcordiumHdWalletTest.testMainnetCredentialId - QualifiedRequest qualifiedRequest = request.qualify(Arrays.asList(wallet.getCredentialId(10, 50, 5, - "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a8d45e64b6f917c540eee16c970c3d4b7f3caf48a7746284878e2ace21c82ea44bf84609834625be1f309988ac523fac")), - network); - QualifiedRequestStatement qualifiedStatement = qualifiedRequest.getCredentialStatements().get(0); + CredentialRegistrationId credId = CredentialRegistrationId.from( + "8a3a87f3f38a7a507d1e85dc02a92b8bcaa859f5cf56accb3c1bc7c40e1789b4933875a38dd4c0646ca3e940a02c42d8"); + ContractAddress contractAddress = ContractAddress.from(1232, 3); + ED25519PublicKey publicKey = ED25519PublicKey + .from("16afdb3cb3568b5ad8f9a0fa3c741b065642de8c53e58f7920bf449e63ff2bf9"); + + // Act + QualifiedRequest qualifiedRequest = request.qualify(statement -> { + if (statement.getIdQualifier().getType().equals(IdQualifier.QualifierType.Credential)) { + return statement.qualify(credId, network); + } else { + return statement.qualify(contractAddress, publicKey, network); + } + }); + + // Assert + QualifiedRequestStatement qualifiedAccountStatement = qualifiedRequest.getCredentialStatements().get(0); assertEquals( "did:ccd:mainnet:cred:8a3a87f3f38a7a507d1e85dc02a92b8bcaa859f5cf56accb3c1bc7c40e1789b4933875a38dd4c0646ca3e940a02c42d8", - qualifiedStatement.getId()); - assertEquals(request.getCredentialStatements().get(0).getStatement(), qualifiedStatement.getStatement()); + qualifiedAccountStatement.getId()); + assertEquals(request.getCredentialStatements().get(0).getStatement(), qualifiedAccountStatement.getStatement()); + QualifiedRequestStatement qualifiedWeb3IdStatement = qualifiedRequest.getCredentialStatements().get(1); + assertEquals( + "did:ccd:mainnet:sci:1232:3/credentialEntry/16afdb3cb3568b5ad8f9a0fa3c741b065642de8c53e58f7920bf449e63ff2bf9", + qualifiedWeb3IdStatement.getId()); + assertEquals(request.getCredentialStatements().get(1).getStatement(), qualifiedWeb3IdStatement.getStatement()); } } diff --git a/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json b/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json index 9b3548c5f..15d1f7c9a 100644 --- a/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json +++ b/concordium-sdk/src/test/testresources/wallet/web3Id/unqualifiedRequest.json @@ -14,6 +14,18 @@ 0 ] } + }, + { + "statement": [ + { + "type": "RevealAttribute", + "attributeTag": "degreeName" + } + ], + "idQualifier": { + "type": "sci", + "issuers": [{"index": 100, "subindex": 0}, {"index": 1232, "subindex": 3}] + } } ] } From 36ed3ae48635b27389efb1719bf5c99cd248a0dd Mon Sep 17 00:00:00 2001 From: orhoj Date: Wed, 21 Feb 2024 10:16:37 +0100 Subject: [PATCH 11/18] Remove use of OptIn of experimental API --- .../app/src/main/java/com/concordium/example/wallet/Storage.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt b/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt index de95f0b23..9cbff96eb 100644 --- a/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt +++ b/concordium-android-wallet-example/app/src/main/java/com/concordium/example/wallet/Storage.kt @@ -21,7 +21,6 @@ class Storage(context: Context) { val accountAddress = StorageAccessor(sharedPreferences, "account_address") val identity = StorageAccessor(sharedPreferences, "identity") - @OptIn(ExperimentalStdlibApi::class) fun getWallet(): ConcordiumHdWallet { val seedPhrase = this.seedPhrase.get() return ConcordiumHdWallet.fromSeedPhrase(seedPhrase, Constants.NETWORK) From 095a0432c8df2ae15c394625764bdf4e7f6bf90b Mon Sep 17 00:00:00 2001 From: Hjort Date: Wed, 21 Feb 2024 16:07:08 +0100 Subject: [PATCH 12/18] Address Feedback --- .../concordium/sdk/crypto/wallet/Network.java | 11 +++ .../wallet/identityobject/IdentityObject.java | 5 +- .../wallet/web3Id/CredentialAttribute.java | 8 +-- .../web3Id/Statement/AtomicStatement.java | 8 ++- .../web3Id/Statement/MembershipStatement.java | 4 +- .../Statement/NonMembershipStatement.java | 8 ++- .../web3Id/Statement/RangeStatement.java | 4 +- .../web3Id/Statement/RequestStatement.java | 26 +++++-- .../web3Id/Statement/StatementType.java | 10 +++ .../did/AccountRequestIdentifier.java | 52 ++++++++++++++ .../Statement/did/RequestIdentifier.java | 45 +++++++++++++ .../did/Web3IssuerRequestIdentifier.java | 67 +++++++++++++++++++ .../accountinfo/credential/AttributeType.java | 4 +- .../web3Id/CredentialAttributeTest.java | 29 ++++++++ .../sdk/crypto/wallet/web3Id/ProofTest.java | 6 +- .../wallet/web3Id/RequestStatementTest.java | 1 - 16 files changed, 265 insertions(+), 23 deletions(-) create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/StatementType.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/AccountRequestIdentifier.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java create mode 100644 concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java create mode 100644 concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/Network.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/Network.java index 55df303a1..87c0d7e40 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/Network.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/Network.java @@ -11,4 +11,15 @@ public String getValue() { default: throw new IllegalArgumentException("An unsupported network was provided"); } } + + static public Network fromLowerCase(String value) { + switch (value) { + case "mainnet": + return MAINNET; + case "testnet": + return TESTNET; + default: + throw new IllegalArgumentException("An unsupported network was provided"); + } + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java index 104f5ba7a..88998845b 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/identityobject/IdentityObject.java @@ -1,9 +1,10 @@ package com.concordium.sdk.crypto.wallet.identityobject; +import java.util.Map; + import com.concordium.sdk.responses.accountinfo.credential.AttributeType; import lombok.Getter; -import lombok.val; @Getter public class IdentityObject { @@ -13,7 +14,7 @@ public class IdentityObject { private String signature; public String getChosenAttribute(AttributeType attribute) throws MissingAttributeException { - val chosenAttributes = this.getAttributeList().getChosenAttributes(); + Map chosenAttributes = this.getAttributeList().getChosenAttributes(); if (!chosenAttributes.containsKey(attribute)) { throw new MissingAttributeException(attribute); } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java index 997242c88..b897e4866 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttribute.java @@ -54,10 +54,10 @@ public boolean isBetween(CredentialAttribute lower, CredentialAttribute upper) t } switch (this.type) { case INT: { - long lowerVal = Long.parseLong(lower.getValue()); - long upperVal = Long.parseLong(upper.getValue()); - long val = Long.parseLong(this.getValue()); - return lowerVal <= val && val < upperVal; + long lowerVal = Long.parseUnsignedLong(lower.getValue()); + long upperVal = Long.parseUnsignedLong(upper.getValue()); + long val = Long.parseUnsignedLong(this.getValue()); + return Long.compareUnsigned(lowerVal, val) <= 0 && Long.compareUnsigned(upperVal, val) > 0; } case TIMESTAMP: { LocalDateTime lowerVal = LocalDateTime.parse(lower.getValue()); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java index f0e545093..eff93b8f1 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/AtomicStatement.java @@ -4,11 +4,14 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.annotation.JsonTypeInfo; import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.concordium.sdk.responses.accountinfo.credential.AttributeType; @@ -19,11 +22,10 @@ public abstract class AtomicStatement { public abstract String getAttributeTag(); // TODO: add overload for web3Id credential - protected CredentialAttribute getAttributeValue(IdentityObject identityObject) throws Exception { + protected CredentialAttribute getAttributeValue(IdentityObject identityObject) throws JsonProcessingException, JsonParseException, MissingAttributeException { AttributeType type = AttributeType.fromJSON(this.getAttributeTag()); - String raw = identityObject.getAttributeList().getChosenAttributes().get(type); + String raw = identityObject.getChosenAttribute(type); return CredentialAttribute.builder().value(raw).type(CredentialAttribute.CredentialAttributeType.STRING).build(); - } // TODO: add overload for web3Id credential diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java index bd08ae62b..e1e52316d 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/MembershipStatement.java @@ -6,6 +6,8 @@ import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Getter; @@ -16,7 +18,7 @@ public class MembershipStatement extends AtomicStatement { private List set; @Override - public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { + public boolean canBeProvedBy(IdentityObject identityObject) throws JsonParseException, JsonProcessingException { try { CredentialAttribute value = this.getAttributeValue(identityObject); return set.contains(value); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java index c86ea4019..2c6cadd12 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/NonMembershipStatement.java @@ -3,8 +3,11 @@ import java.util.List; import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Getter; @@ -15,11 +18,12 @@ public class NonMembershipStatement extends AtomicStatement { private List set; @Override - public boolean canBeProvedBy(IdentityObject identityObject) { + public boolean canBeProvedBy(IdentityObject identityObject) throws JsonParseException, JsonProcessingException { try { CredentialAttribute value = this.getAttributeValue(identityObject); return !set.contains(value); - } catch (Exception e) { + } catch (MissingAttributeException e) { + // If the identityObject does not have the relevant attribute, it does not satisfy the statement return false; } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java index 4747ddd78..84e248511 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RangeStatement.java @@ -4,6 +4,8 @@ import com.concordium.sdk.crypto.wallet.identityobject.MissingAttributeException; import com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute; import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Builder; import lombok.Getter; @@ -18,7 +20,7 @@ public class RangeStatement extends AtomicStatement { private CredentialAttribute upper; private String attributeTag; @Override - public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { + public boolean canBeProvedBy(IdentityObject identityObject) throws JsonParseException, JsonProcessingException { try { CredentialAttribute value = getAttributeValue(identityObject); return value.isBetween(lower, upper); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java index 1503e48df..0a8e2bfa3 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java @@ -4,6 +4,8 @@ import java.util.stream.Collectors; import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.did.RequestIdentifier; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Builder; import lombok.Getter; @@ -13,11 +15,20 @@ @Builder @Jacksonized public class RequestStatement { - private String id; - private List type; + private RequestIdentifier id; + @JsonProperty("type") + private List verifiableCredentialTypes; private List statement; + public StatementType getStatementType() { + return this.id.getType(); + } + public List getUnsatisfiedStatements(IdentityObject identityObject) { + if (!this.getStatementType().equals(StatementType.Credential)) { + throw new IllegalArgumentException("Only an account statement can be satisfied by an identity"); + } + return statement.stream().filter(s -> { try { return !s.canBeProvedBy(identityObject); @@ -28,10 +39,15 @@ public List getUnsatisfiedStatements(IdentityObject identityObj } public boolean canBeProvedBy(IdentityObject identityObject) throws Exception { - for (AtomicStatement s: this.statement) { - if (s.canBeProvedBy(identityObject)) continue; + if (!this.getStatementType().equals(StatementType.Credential)) { + throw new IllegalArgumentException("Only an account statement can by proven using a identity"); + } + + for (AtomicStatement s : this.statement) { + if (s.canBeProvedBy(identityObject)) + continue; return false; } - return true; + return true; } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/StatementType.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/StatementType.java new file mode 100644 index 000000000..4ac11d94e --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/StatementType.java @@ -0,0 +1,10 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public enum StatementType { + @JsonProperty("cred") + Credential, + @JsonProperty("sci") + SmartContract +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/AccountRequestIdentifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/AccountRequestIdentifier.java new file mode 100644 index 000000000..f766b1471 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/AccountRequestIdentifier.java @@ -0,0 +1,52 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement.did; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import com.concordium.sdk.crypto.wallet.Network; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.StatementType; +import com.concordium.sdk.transactions.CredentialRegistrationId; +import com.fasterxml.jackson.annotation.JsonValue; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class AccountRequestIdentifier extends RequestIdentifier { + private CredentialRegistrationId credId; + private static Pattern pattern = Pattern.compile( "^" + getDID("(mainnet|testnet)", "([a-zA-Z0-9]*)") + "$"); + + @Builder + public AccountRequestIdentifier(Network network, CredentialRegistrationId credId) { + super(network); + this.credId = credId; + } + + private static String getDID(String network, String credId) { + return "did:ccd:" + network + ":cred:" + credId; + } + + @JsonValue + @Override + public String toString() { + return getDID(network.getValue().toLowerCase(), credId.getEncoded()); + } + + @Nullable + public static AccountRequestIdentifier tryFromString(String did) { + Matcher matcher = pattern.matcher(did); + if (matcher.matches()) { + Network network = Network.fromLowerCase(matcher.group(1)); + CredentialRegistrationId credId = CredentialRegistrationId.from(matcher.group(2)); + return new AccountRequestIdentifier(network, credId); + } + return null; + } + + @Override + public StatementType getType() { + return StatementType.Credential; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java new file mode 100644 index 000000000..1780170d9 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java @@ -0,0 +1,45 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement.did; + +import java.io.IOException; +import com.concordium.sdk.crypto.wallet.Network; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.StatementType; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +@JsonDeserialize(using = RequestIdentifierDeserializer.class) +public abstract class RequestIdentifier { + Network network; + + public abstract StatementType getType(); +} + +class RequestIdentifierDeserializer extends JsonDeserializer { + @Override + public RequestIdentifier deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + System.out.println("Test"); + JsonNode reference = p.getCodec().readTree(p); + if (!reference.isTextual()) { + throw new JsonParseException(p, "RequestIdentifier must be deserialize from a String"); + } + String did = reference.asText(); + + AccountRequestIdentifier accountDID = AccountRequestIdentifier.tryFromString(did); + if (accountDID != null) { + return accountDID; + } + Web3IssuerRequestIdentifier web3DID = Web3IssuerRequestIdentifier.tryFromString(did); + if (web3DID != null) { + return web3DID; + } + throw new JsonParseException(p, "Given string is not a valid request statement did"); + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java new file mode 100644 index 000000000..2481e7248 --- /dev/null +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java @@ -0,0 +1,67 @@ +package com.concordium.sdk.crypto.wallet.web3Id.Statement.did; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import org.apache.commons.codec.binary.Hex; + +import com.concordium.sdk.crypto.ed25519.ED25519PublicKey; +import com.concordium.sdk.crypto.wallet.Network; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.StatementType; +import com.concordium.sdk.types.ContractAddress; +import com.fasterxml.jackson.annotation.JsonValue; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class Web3IssuerRequestIdentifier extends RequestIdentifier { + private ContractAddress address; + private ED25519PublicKey publicKey; + private static Pattern pattern = Pattern.compile("^" + getDID("(mainnet|testnet)", "([0-9]*)", "([0-9]*)", "([a-zA-Z0-9]*)") + "$", Pattern.CASE_INSENSITIVE); + + @Builder + public Web3IssuerRequestIdentifier(Network network, ContractAddress address, ED25519PublicKey publicKey) { + super(network); + this.address = address; + this.publicKey = publicKey; + } + + private static String getDID(String network, String index, String subindex, String publicKey) { + return "did:ccd:" + + network + + ":sci:" + + index + + ":" + + subindex + + "/credentialEntry/" + + publicKey; + } + + @JsonValue + @Override + public String toString() { + return getDID(network.getValue().toLowerCase(), Long.toUnsignedString(address.getIndex()), Long.toUnsignedString(address.getSubIndex()), + Hex.encodeHexString(publicKey.getRawBytes())); + } + + @Nullable + public static Web3IssuerRequestIdentifier tryFromString(String did) { + Matcher matcher = pattern.matcher(did); + if (matcher.matches()) { + Network network = Network.fromLowerCase(matcher.group(1)); + long index = Long.parseUnsignedLong(matcher.group(2)); + long subindex = Long.parseUnsignedLong(matcher.group(3)); + ED25519PublicKey publicKey = ED25519PublicKey.from(matcher.group(4)); + return new Web3IssuerRequestIdentifier(network, ContractAddress.from(index, subindex), publicKey); + } + return null; + } + + @Override + public StatementType getType() { + return StatementType.SmartContract; + } +} diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java index 038ce4b2f..4ec565ba9 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/responses/accountinfo/credential/AttributeType.java @@ -2,6 +2,8 @@ import com.concordium.sdk.serializing.JsonMapper; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; /** * Identity attributes of which a {@link Commitment} is generated for by the identity provider. @@ -39,7 +41,7 @@ public enum AttributeType { @JsonProperty("lei") LEI; - public static AttributeType fromJSON(String jsonStr) throws Exception { + public static AttributeType fromJSON(String jsonStr) throws JsonMappingException, JsonProcessingException { return JsonMapper.INSTANCE.readValue(String.format("\"%s\"", jsonStr), AttributeType.class); } } diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java new file mode 100644 index 000000000..950f7a3ca --- /dev/null +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java @@ -0,0 +1,29 @@ +package com.concordium.sdk.crypto.wallet.web3Id; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import static com.concordium.sdk.crypto.wallet.web3Id.CredentialAttribute.CredentialAttributeType.INT; + +public class CredentialAttributeTest { + @Test + public void testIsBetweenForNumberAttributes() { + CredentialAttribute low = new CredentialAttribute("9", INT); + CredentialAttribute middle = new CredentialAttribute("10", INT); + CredentialAttribute high = new CredentialAttribute("11", INT); + assertTrue(!low.isBetween(middle, high)); + assertTrue(middle.isBetween(low, high)); + assertTrue(!high.isBetween(low, middle)); + } + + @Test + public void testIsBetweenHandles64BitNumbers() { + CredentialAttribute low = new CredentialAttribute("1", INT); + CredentialAttribute middle = new CredentialAttribute("18446744073709551600", INT); + CredentialAttribute high = new CredentialAttribute("18446744073709551615", INT); + assertTrue(!low.isBetween(middle, high)); + assertTrue(middle.isBetween(low, high)); + assertTrue(!high.isBetween(low, middle)); + } +} diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java index caabf2159..d86e7bcd3 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/ProofTest.java @@ -45,7 +45,7 @@ public void testCanParseRequest() throws Exception { assertEquals("beefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef", request.getChallenge()); RequestStatement credentialStatement = request.getCredentialStatements().get(0); - assertEquals("did:ccd:testnet:cred:a88a8214fc7a7f11aeda54661b76a1fd7c67e15278b83a85ec92cb799ef0abaa3b7c61a7e90ea6bb108fa2ca1a3ba217", credentialStatement.getId()); + assertEquals("did:ccd:testnet:cred:a88a8214fc7a7f11aeda54661b76a1fd7c67e15278b83a85ec92cb799ef0abaa3b7c61a7e90ea6bb108fa2ca1a3ba217", credentialStatement.getId().toString()); Iterator iter = credentialStatement.getStatement().iterator(); @@ -118,9 +118,9 @@ public void testCanParseWeb3IdRequest() throws Exception { assertEquals("5d50c6e18aca83e991af81e7d7e760f5dc753db7966dc19e9aa42f25aef1696b", request.getChallenge()); RequestStatement credentialStatement = request.getCredentialStatements().get(0); - assertEquals("did:ccd:testnet:sci:6105:0/credentialEntry/31163ba14e30b834f1e97b9544d86df94883fd4f2c77e1d1fac0b6189c9e7996", credentialStatement.getId()); + assertEquals("did:ccd:testnet:sci:6105:0/credentialEntry/31163ba14e30b834f1e97b9544d86df94883fd4f2c77e1d1fac0b6189c9e7996", credentialStatement.getId().toString()); - List type = credentialStatement.getType(); + List type = credentialStatement.getVerifiableCredentialTypes(); assert(type.stream().anyMatch(item -> item.equals("VerifiableCredential"))); assert(type.stream().anyMatch(item -> item.equals("ConcordiumVerifiableCredential"))); assert(type.stream().anyMatch(item -> item.equals("UniversityDegreeCredential"))); 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 79767e452..babc0a781 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 @@ -12,7 +12,6 @@ import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; - public class RequestStatementTest { @Test From 9e417adf4a5be2dab3c31c608e2a1c0da443e2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Hjort?= <87635671+shjortConcordium@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:02:51 +0100 Subject: [PATCH 13/18] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakob Ørhøj <57264157+orhoj@users.noreply.github.com> --- .../crypto/wallet/web3Id/Statement/did/RequestIdentifier.java | 3 +-- .../web3Id/Statement/did/Web3IssuerRequestIdentifier.java | 2 +- .../sdk/crypto/wallet/web3Id/CredentialAttributeTest.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java index 1780170d9..3264bea0e 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/RequestIdentifier.java @@ -25,10 +25,9 @@ public abstract class RequestIdentifier { class RequestIdentifierDeserializer extends JsonDeserializer { @Override public RequestIdentifier deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - System.out.println("Test"); JsonNode reference = p.getCodec().readTree(p); if (!reference.isTextual()) { - throw new JsonParseException(p, "RequestIdentifier must be deserialize from a String"); + throw new JsonParseException(p, "RequestIdentifier must be deserialized from a String"); } String did = reference.asText(); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java index 2481e7248..1609dadba 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java @@ -44,7 +44,7 @@ private static String getDID(String network, String index, String subindex, Stri @Override public String toString() { return getDID(network.getValue().toLowerCase(), Long.toUnsignedString(address.getIndex()), Long.toUnsignedString(address.getSubIndex()), - Hex.encodeHexString(publicKey.getRawBytes())); + publicKey.toString()); } @Nullable diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java index 950f7a3ca..1c5d49c80 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java @@ -22,7 +22,7 @@ public void testIsBetweenHandles64BitNumbers() { CredentialAttribute low = new CredentialAttribute("1", INT); CredentialAttribute middle = new CredentialAttribute("18446744073709551600", INT); CredentialAttribute high = new CredentialAttribute("18446744073709551615", INT); - assertTrue(!low.isBetween(middle, high)); + assertFalse(low.isBetween(middle, high)); assertTrue(middle.isBetween(low, high)); assertTrue(!high.isBetween(low, middle)); } From 4f5e42d388e5d0fca9c9ca0461a69f721aa49647 Mon Sep 17 00:00:00 2001 From: Hjort Date: Wed, 28 Feb 2024 16:29:41 +0100 Subject: [PATCH 14/18] Use assertFalse --- .../sdk/crypto/wallet/web3Id/CredentialAttributeTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java index 1c5d49c80..753b3ac1f 100644 --- a/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java +++ b/concordium-sdk/src/test/java/com/concordium/sdk/crypto/wallet/web3Id/CredentialAttributeTest.java @@ -1,5 +1,6 @@ package com.concordium.sdk.crypto.wallet.web3Id; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; @@ -12,9 +13,9 @@ public void testIsBetweenForNumberAttributes() { CredentialAttribute low = new CredentialAttribute("9", INT); CredentialAttribute middle = new CredentialAttribute("10", INT); CredentialAttribute high = new CredentialAttribute("11", INT); - assertTrue(!low.isBetween(middle, high)); + assertFalse(low.isBetween(middle, high)); assertTrue(middle.isBetween(low, high)); - assertTrue(!high.isBetween(low, middle)); + assertFalse(high.isBetween(low, middle)); } @Test @@ -24,6 +25,6 @@ public void testIsBetweenHandles64BitNumbers() { CredentialAttribute high = new CredentialAttribute("18446744073709551615", INT); assertFalse(low.isBetween(middle, high)); assertTrue(middle.isBetween(low, high)); - assertTrue(!high.isBetween(low, middle)); + assertFalse(high.isBetween(low, middle)); } } From e169199ccfbf6ac4e042c4846408bd6ccfb21373 Mon Sep 17 00:00:00 2001 From: Hjort Date: Mon, 4 Mar 2024 13:17:42 +0100 Subject: [PATCH 15/18] Finish merge with main --- .../wallet/web3Id/Statement/IdQualifier.java | 11 ++------ .../Statement/QualifiedRequestStatement.java | 8 +++++- .../web3Id/Statement/RequestStatement.java | 5 +--- .../UnqualifiedRequestStatement.java | 25 ++++++++++--------- ...ava => VerifiableCredentialQualifier.java} | 2 +- .../did/Web3IssuerRequestIdentifier.java | 2 -- .../wallet/web3Id/RequestStatementTest.java | 8 +++--- 7 files changed, 28 insertions(+), 33 deletions(-) rename concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/{Web3IdIssuerQualifier.java => VerifiableCredentialQualifier.java} (81%) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java index 701cce168..de6c89fb2 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/IdQualifier.java @@ -12,15 +12,8 @@ @Getter @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type", visible = true) -@JsonSubTypes({ @Type(value = IdentityQualifier.class), @Type(value = Web3IdIssuerQualifier.class) }) +@JsonSubTypes({ @Type(value = IdentityQualifier.class), @Type(value = VerifiableCredentialQualifier.class) }) public class IdQualifier { @JsonUnwrapped - QualifierType type; - - public enum QualifierType { - @JsonProperty("cred") - Credential, - @JsonProperty("sci") - SmartContract - } + StatementType type; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java index ba116bbe0..adaef019e 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/QualifiedRequestStatement.java @@ -1,5 +1,7 @@ package com.concordium.sdk.crypto.wallet.web3Id.Statement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.did.RequestIdentifier; + import lombok.Getter; import lombok.experimental.SuperBuilder; import lombok.extern.jackson.Jacksonized; @@ -8,5 +10,9 @@ @SuperBuilder @Jacksonized public class QualifiedRequestStatement extends RequestStatement { - private String id; + private RequestIdentifier id; + + public StatementType getStatementType() { + return this.id.getType(); + } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java index 51d3e05c2..6f6ee22d4 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/RequestStatement.java @@ -13,14 +13,11 @@ @SuperBuilder @Getter public abstract class RequestStatement { - private RequestIdentifier id; @JsonProperty("type") private List verifiableCredentialTypes; private List statement; - public StatementType getStatementType() { - return this.id.getType(); - } + public abstract StatementType getStatementType(); public List getUnsatisfiedStatements(IdentityObject identityObject) { if (!this.getStatementType().equals(StatementType.Credential)) { diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java index d85641493..3b76d2a71 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java @@ -4,6 +4,9 @@ import com.concordium.sdk.crypto.ed25519.ED25519PublicKey; import com.concordium.sdk.crypto.wallet.Network; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.did.AccountRequestIdentifier; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.did.RequestIdentifier; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.did.Web3IssuerRequestIdentifier; import com.concordium.sdk.transactions.CredentialRegistrationId; import com.concordium.sdk.types.ContractAddress; @@ -17,31 +20,29 @@ public class UnqualifiedRequestStatement extends RequestStatement { private IdQualifier idQualifier; + @Override + public StatementType getStatementType() { + return this.idQualifier.type; + } + public QualifiedRequestStatement qualify(CredentialRegistrationId credId, Network network) { - if (!idQualifier.getType().equals(IdQualifier.QualifierType.Credential)) { + if (!idQualifier.getType().equals(StatementType.Credential)) { throw new IllegalArgumentException("Only an account statement may only be qualified using a credentialId"); } - String did = "did:ccd:" + network.getValue().toLowerCase() + ":cred:" + credId.getEncoded(); + RequestIdentifier did = new AccountRequestIdentifier(network, credId); return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); } public QualifiedRequestStatement qualify(ContractAddress contract, ED25519PublicKey publicKey, Network network) { - if (!idQualifier.getType().equals(IdQualifier.QualifierType.SmartContract)) { + if (!idQualifier.getType().equals(StatementType.SmartContract)) { throw new IllegalArgumentException( "Only an web3Id statement may only be qualified using a Contract Address"); } - if (!((Web3IdIssuerQualifier) idQualifier).getIssuers().contains(contract)) { + if (!((VerifiableCredentialQualifier) idQualifier).getIssuers().contains(contract)) { throw new IllegalArgumentException("The Contract Address must be one specified in the qualifier"); } - String did = "did:ccd:" + - network.getValue().toLowerCase() + - ":sci:" + - contract.getIndex() + - ":" + - contract.getSubIndex() + - "/credentialEntry/" + - Hex.encodeHexString(publicKey.getRawBytes()); + RequestIdentifier did = new Web3IssuerRequestIdentifier(network, contract, publicKey); return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); } } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/VerifiableCredentialQualifier.java similarity index 81% rename from concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java rename to concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/VerifiableCredentialQualifier.java index c9eda9829..b1d2ef4ba 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/Web3IdIssuerQualifier.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/VerifiableCredentialQualifier.java @@ -9,6 +9,6 @@ @Getter @JsonTypeName("sci") -public class Web3IdIssuerQualifier extends IdQualifier { +public class VerifiableCredentialQualifier extends IdQualifier { private List issuers; } diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java index 1609dadba..4daa5ea5c 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/did/Web3IssuerRequestIdentifier.java @@ -5,8 +5,6 @@ import javax.annotation.Nullable; -import org.apache.commons.codec.binary.Hex; - import com.concordium.sdk.crypto.ed25519.ED25519PublicKey; import com.concordium.sdk.crypto.wallet.Network; import com.concordium.sdk.crypto.wallet.web3Id.Statement.StatementType; 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 a31d4da11..9826fb03f 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 @@ -13,9 +13,9 @@ import com.concordium.sdk.crypto.wallet.Network; import com.concordium.sdk.crypto.wallet.identityobject.IdentityObject; import com.concordium.sdk.crypto.wallet.web3Id.Statement.AtomicStatement; -import com.concordium.sdk.crypto.wallet.web3Id.Statement.IdQualifier; import com.concordium.sdk.crypto.wallet.web3Id.Statement.QualifiedRequestStatement; import com.concordium.sdk.crypto.wallet.web3Id.Statement.RequestStatement; +import com.concordium.sdk.crypto.wallet.web3Id.Statement.StatementType; import com.concordium.sdk.serializing.JsonMapper; import com.concordium.sdk.transactions.CredentialRegistrationId; import com.concordium.sdk.types.ContractAddress; @@ -65,7 +65,7 @@ public void testCanQualifyStatement() throws Exception { // Act QualifiedRequest qualifiedRequest = request.qualify(statement -> { - if (statement.getIdQualifier().getType().equals(IdQualifier.QualifierType.Credential)) { + if (statement.getIdQualifier().getType().equals(StatementType.Credential)) { return statement.qualify(credId, network); } else { return statement.qualify(contractAddress, publicKey, network); @@ -76,12 +76,12 @@ public void testCanQualifyStatement() throws Exception { QualifiedRequestStatement qualifiedAccountStatement = qualifiedRequest.getCredentialStatements().get(0); assertEquals( "did:ccd:mainnet:cred:8a3a87f3f38a7a507d1e85dc02a92b8bcaa859f5cf56accb3c1bc7c40e1789b4933875a38dd4c0646ca3e940a02c42d8", - qualifiedAccountStatement.getId()); + qualifiedAccountStatement.getId().toString()); assertEquals(request.getCredentialStatements().get(0).getStatement(), qualifiedAccountStatement.getStatement()); QualifiedRequestStatement qualifiedWeb3IdStatement = qualifiedRequest.getCredentialStatements().get(1); assertEquals( "did:ccd:mainnet:sci:1232:3/credentialEntry/16afdb3cb3568b5ad8f9a0fa3c741b065642de8c53e58f7920bf449e63ff2bf9", - qualifiedWeb3IdStatement.getId()); + qualifiedWeb3IdStatement.getId().toString()); assertEquals(request.getCredentialStatements().get(1).getStatement(), qualifiedWeb3IdStatement.getStatement()); } } From 36dfa0adb753b2f74c375828621b77fe4ada1aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Hjort?= <87635671+shjortConcordium@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:23:05 +0100 Subject: [PATCH 16/18] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakob Ørhøj <57264157+orhoj@users.noreply.github.com> --- .../wallet/web3Id/Statement/UnqualifiedRequestStatement.java | 4 ++-- .../sdk/crypto/wallet/web3Id/UnqualifiedRequest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java index 3b76d2a71..846950b85 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/Statement/UnqualifiedRequestStatement.java @@ -27,7 +27,7 @@ public StatementType getStatementType() { public QualifiedRequestStatement qualify(CredentialRegistrationId credId, Network network) { if (!idQualifier.getType().equals(StatementType.Credential)) { - throw new IllegalArgumentException("Only an account statement may only be qualified using a credentialId"); + throw new IllegalArgumentException("Only an account statement may be qualified using a credentialId"); } RequestIdentifier did = new AccountRequestIdentifier(network, credId); return QualifiedRequestStatement.builder().id(did).statement(getStatement()).build(); @@ -36,7 +36,7 @@ public QualifiedRequestStatement qualify(CredentialRegistrationId credId, Networ public QualifiedRequestStatement qualify(ContractAddress contract, ED25519PublicKey publicKey, Network network) { if (!idQualifier.getType().equals(StatementType.SmartContract)) { throw new IllegalArgumentException( - "Only an web3Id statement may only be qualified using a Contract Address"); + "Only a verifiable credential statement may be qualified using a Contract Address"); } if (!((VerifiableCredentialQualifier) idQualifier).getIssuers().contains(contract)) { throw new IllegalArgumentException("The Contract Address must be one specified in the qualifier"); diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java index 1f7a2188e..b3aba7a2c 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/crypto/wallet/web3Id/UnqualifiedRequest.java @@ -27,7 +27,7 @@ public QualifiedRequest qualify(Qualifier qualifier) { return QualifiedRequest.builder().challenge(challenge).credentialStatements(qualifiedStatements).build(); } - static interface Qualifier { + public static interface Qualifier { QualifiedRequestStatement qualify(UnqualifiedRequestStatement statement); } } From 0f30b0d5d166885e7e7c8b0c887d1077efecff90 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 4 Mar 2024 13:28:11 +0100 Subject: [PATCH 17/18] Removed `tag` field from CustomEvent --- .../java/com/concordium/sdk/cis2/SerializationUtils.java | 6 +++--- .../java/com/concordium/sdk/cis2/events/CustomEvent.java | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/cis2/SerializationUtils.java b/concordium-sdk/src/main/java/com/concordium/sdk/cis2/SerializationUtils.java index c9f90838b..3b3e439c7 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/cis2/SerializationUtils.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/cis2/SerializationUtils.java @@ -202,13 +202,13 @@ public static Cis2Event deserializeCis2Event(byte[] eventBytes) { case TOKEN_METADATA: return SerializationUtils.deserializeTokenMetadataEvent(buffer); case CUSTOM: - return SerializationUtils.deserializeCustomEvent(tag, buffer); + return SerializationUtils.deserializeCustomEvent(buffer); } throw new IllegalArgumentException("Malformed CIS2 event"); } - private static Cis2Event deserializeCustomEvent(byte tag, ByteBuffer buffer) { - return new CustomEvent(tag, buffer.array()); + private static Cis2Event deserializeCustomEvent(ByteBuffer buffer) { + return new CustomEvent(buffer.array()); } @SneakyThrows diff --git a/concordium-sdk/src/main/java/com/concordium/sdk/cis2/events/CustomEvent.java b/concordium-sdk/src/main/java/com/concordium/sdk/cis2/events/CustomEvent.java index 4428da218..f139f523f 100644 --- a/concordium-sdk/src/main/java/com/concordium/sdk/cis2/events/CustomEvent.java +++ b/concordium-sdk/src/main/java/com/concordium/sdk/cis2/events/CustomEvent.java @@ -12,11 +12,9 @@ @Getter public class CustomEvent implements Cis2Event { - private final byte tag; private final byte[] data; - public CustomEvent(byte tag, byte[] data) { - this.tag = tag; + public CustomEvent(byte[] data) { this.data = data; } From de875a65054858368933f4b59f130e0c2ef32e89 Mon Sep 17 00:00:00 2001 From: magnusbechwind Date: Mon, 4 Mar 2024 13:30:10 +0100 Subject: [PATCH 18/18] updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e78b9f5..c9ffb0506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unreleased changes +- Fixed a bug in `CustomEvent`. Removed unnecessary `tag` field. - Added `Web3IdProof` class with `getWeb3IdProof` method to create Presentations. (And supporting classes) - Fixed an issue where `ConcordiumHdWallet.fromSeedPhrase` always produced an invalid seed as hex.