From 6435671044d1e20a72ba51a535607c17bcfb4716 Mon Sep 17 00:00:00 2001 From: Mateusz Czeladka Date: Mon, 9 Sep 2024 10:37:23 +0200 Subject: [PATCH] feat: vote verification adjustments for KERI. --- .../voting/domain/VoteSerialisations.java | 9 +- .../voting/VotingVerificationApp.java | 3 +- .../voting/client/ChainFollowerClient.java | 6 +- .../voting/client/KeriVerificationClient.java | 69 +++++++ ...{CardanoConfig.java => NetworkConfig.java} | 6 +- .../voting/domain/CoseWrappedVote.java | 19 -- .../{CardanoNetwork.java => Network.java} | 2 +- .../domain/VoteVerificationRequest.java | 17 +- .../voting/domain/VoteVerificationResult.java | 7 +- .../foundation/voting/domain/WalletType.java | 7 + .../foundation/voting/domain/WrappedVote.java | 50 +++++ .../verify/VoteVerificationService.java | 171 +++++++++++++++--- .../voting/utils/VoteSerialisations.java | 29 ++- .../src/main/resources/application.properties | 1 + 14 files changed, 328 insertions(+), 68 deletions(-) create mode 100644 backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/KeriVerificationClient.java rename backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/{CardanoConfig.java => NetworkConfig.java} (67%) delete mode 100644 backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java rename backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/{CardanoNetwork.java => Network.java} (93%) create mode 100644 backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WalletType.java create mode 100644 backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WrappedVote.java diff --git a/backend-services/vote-commitment-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java b/backend-services/vote-commitment-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java index 5fcbd5f7e..6566671ae 100644 --- a/backend-services/vote-commitment-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java +++ b/backend-services/vote-commitment-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java @@ -24,9 +24,14 @@ public static Function createSerialiserFunct yield blake2bHash256(bytes); } case KERI -> { - val bytes = vote.getPayload().map(String::getBytes).orElse(new byte[0]); + val message = vote.getSignature().getBytes(); + val payload = vote.getPayload().map(String::getBytes).orElse(new byte[0]); - yield blake2bHash256(bytes); + val result = new byte[message.length + payload.length]; + + System.arraycopy(message, 0, result, 0, payload.length); + + yield blake2bHash256(result); } }; } diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/VotingVerificationApp.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/VotingVerificationApp.java index fab616c7a..a19ce9cdf 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/VotingVerificationApp.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/VotingVerificationApp.java @@ -4,7 +4,6 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; -import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.boot.CommandLineRunner; @@ -18,7 +17,7 @@ import static org.springframework.aot.hint.ExecutableMode.INVOKE; -@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, ErrorMvcAutoConfiguration.class}) +@SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ErrorMvcAutoConfiguration.class }) @ComponentScan(basePackages = { "org.cardano.foundation.voting.client", "org.cardano.foundation.voting.service", diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/ChainFollowerClient.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/ChainFollowerClient.java index 699396ffa..0d14f38b8 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/ChainFollowerClient.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/ChainFollowerClient.java @@ -4,7 +4,7 @@ import io.vavr.control.Either; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.cardano.foundation.voting.domain.CardanoNetwork; +import org.cardano.foundation.voting.domain.Network; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; @@ -25,7 +25,7 @@ public class ChainFollowerClient { private final RestTemplate restTemplate; - private final CardanoNetwork network; + private final Network network; @Value("${ledger.follower.app.base.url}") private String ledgerFollowerBaseUrl; @@ -88,6 +88,6 @@ public boolean isEventInactive() { } - record MerkleRootHashResponse(boolean isPresent, CardanoNetwork network) { } + record MerkleRootHashResponse(boolean isPresent, Network network) { } } diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/KeriVerificationClient.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/KeriVerificationClient.java new file mode 100644 index 000000000..7b52e6a2d --- /dev/null +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/client/KeriVerificationClient.java @@ -0,0 +1,69 @@ +package org.cardano.foundation.voting.client; + +import io.vavr.control.Either; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.zalando.problem.Problem; +import org.zalando.problem.spring.common.HttpStatusAdapter; + +import java.util.HashMap; +import java.util.Map; + +import static org.springframework.http.HttpMethod.POST; + +@RequiredArgsConstructor +@Component +@Slf4j +public class KeriVerificationClient { + + private final RestTemplate restTemplate; + + @Value("${keri.ballot.verifier.base.url}") + private String keriVerifierBaseUrl; + + public Either verifySignature(String aid, + String signature, + String payload) { + val url = String.format("%s/verify", keriVerifierBaseUrl); + + val headers = new HttpHeaders(); + headers.add("Content-Type", "application/json"); + + val requestBody = new HashMap(); + requestBody.put("pre", aid); + requestBody.put("signature", signature); + requestBody.put("payload", payload); + + val entity = new HttpEntity>(requestBody, headers); + + try { + val response = restTemplate.exchange(url, POST, entity, String.class); + + if (response.getStatusCode().is2xxSuccessful()) { + return Either.right(true); + } + + return Either.left(Problem.builder() + .withTitle("KERI_VERIFICATION_FAILED") + .withDetail("The Keri-specific condition was not met.") + .withStatus(new HttpStatusAdapter(response.getStatusCode())) + .build()); + } catch (HttpClientErrorException e) { + log.error("Unable to verify signature, reason: {}", e.getMessage()); + + return Either.left(Problem.builder() + .withTitle("KERI_VERIFICATION_ERROR") + .withDetail("Unable to verify signature, reason: " + e.getMessage()) + .withStatus(new HttpStatusAdapter(e.getStatusCode())) + .build()); + } + } + +} diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/CardanoConfig.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/NetworkConfig.java similarity index 67% rename from backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/CardanoConfig.java rename to backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/NetworkConfig.java index 69b809a54..951e09d5b 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/CardanoConfig.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/config/NetworkConfig.java @@ -1,17 +1,17 @@ package org.cardano.foundation.voting.config; import lombok.extern.slf4j.Slf4j; -import org.cardano.foundation.voting.domain.CardanoNetwork; +import org.cardano.foundation.voting.domain.Network; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @Slf4j -public class CardanoConfig { +public class NetworkConfig { @Bean - public CardanoNetwork network(@Value("${cardano.network:main}") CardanoNetwork network) { + public Network network(@Value("${cardano.network:main}") Network network) { log.info("Configured backend network:{}", network); return network; diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java deleted file mode 100644 index 1394b38ba..000000000 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.cardano.foundation.voting.domain; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -import java.util.Optional; - -@Getter -@Builder -@AllArgsConstructor -public class CoseWrappedVote { - - private String coseSignature; - - @Builder.Default - private Optional cosePublicKey = Optional.empty(); - -} diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CardanoNetwork.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/Network.java similarity index 93% rename from backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CardanoNetwork.java rename to backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/Network.java index 9882d7917..03a812c5c 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CardanoNetwork.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/Network.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(description = "Cardano networks") -public enum CardanoNetwork { +public enum Network { MAIN, // main-net PREPROD, // preprod-net diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationRequest.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationRequest.java index eb00c67e0..aba3c92b2 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationRequest.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationRequest.java @@ -19,11 +19,22 @@ public class VoteVerificationRequest { private String rootHash; @NotBlank - @Schema(description = "COSE signature of the vote", required = true) - protected String voteCoseSignature; + @Schema(description = "Cardano or KERI", required = true) + private WalletType walletType; + + @NotBlank + @Schema(description = "aid or stake address", required = true) + private String walletId; + + @Schema(description = "payload") + protected Optional payload; + + @NotBlank + @Schema(description = "Signature of the vote", required = true) + protected String signature; @Schema(description = "Public key for the vote") - protected Optional<@NotBlank String> voteCosePublicKey; + protected Optional<@NotBlank String> publicKey; @Builder.Default @Schema(description = "Merkle proof") diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationResult.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationResult.java index e1a6dd26c..425882184 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationResult.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/VoteVerificationResult.java @@ -12,7 +12,10 @@ public class VoteVerificationResult { @Schema(description = "Indicates if the vote is verified or not", example = "true") private boolean isVerified; - @Schema(description = "Cardano network ", required = true) - private CardanoNetwork network; + @Schema(description = "cardano or keri", required = true, example = "CARDANO") + private WalletType walletType; + + @Schema(description = "Network ", required = true, example = "MAINNET") + private Network network; } diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WalletType.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WalletType.java new file mode 100644 index 000000000..7d2d344ce --- /dev/null +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WalletType.java @@ -0,0 +1,7 @@ +package org.cardano.foundation.voting.domain; + +public enum WalletType { + + KERI, CARDANO + +} diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WrappedVote.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WrappedVote.java new file mode 100644 index 000000000..a0dbba7ed --- /dev/null +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/WrappedVote.java @@ -0,0 +1,50 @@ +package org.cardano.foundation.voting.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.Optional; + +@Getter +@Builder +@AllArgsConstructor +public class WrappedVote { + + private String walletId; + + private WalletType walletType; + + @Builder.Default + private Optional payload = Optional.empty(); + + private String signature; + + @Builder.Default + private Optional publicKey = Optional.empty(); + + public static WrappedVote createCardanoVote(String walletId, + String signature, + Optional publicKey) { + return WrappedVote.builder() + .walletId(walletId) + .walletType(WalletType.CARDANO) + .signature(signature) + .payload(Optional.empty()) + .publicKey(publicKey) + .build(); + } + + public static WrappedVote createKERIVote(String walletId, + String signature, + String payload) { + return WrappedVote.builder() + .walletId(walletId) + .walletType(WalletType.KERI) + .signature(signature) + .payload(Optional.of(payload)) + .publicKey(Optional.empty()) + .build(); + } + +} diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java index f8f042d7e..8f08c9282 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java @@ -1,15 +1,15 @@ package org.cardano.foundation.voting.service.verify; +import com.bloxbean.cardano.client.util.HexUtil; import com.fasterxml.jackson.core.JsonProcessingException; import io.micrometer.core.annotation.Timed; import io.vavr.control.Either; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import lombok.val; import org.cardano.foundation.voting.client.ChainFollowerClient; -import org.cardano.foundation.voting.domain.CardanoNetwork; -import org.cardano.foundation.voting.domain.CoseWrappedVote; -import org.cardano.foundation.voting.domain.VoteVerificationRequest; -import org.cardano.foundation.voting.domain.VoteVerificationResult; +import org.cardano.foundation.voting.client.KeriVerificationClient; +import org.cardano.foundation.voting.domain.*; import org.cardano.foundation.voting.utils.Enums; import org.cardanofoundation.cip30.CIP30Verifier; import org.cardanofoundation.merkle.MerkleTree; @@ -18,6 +18,7 @@ import org.zalando.problem.Problem; import java.util.List; +import java.util.Optional; import static com.bloxbean.cardano.client.util.HexUtil.decodeHexString; import static com.bloxbean.cardano.client.util.JsonUtil.parseJson; @@ -31,12 +32,13 @@ public class VoteVerificationService { private final ChainFollowerClient chainFollowerClient; + private final KeriVerificationClient keriVerificationClient; - private final CardanoNetwork cardanoNetwork; + private final Network network; @Timed(value = "service.verifyVote", histogram = true) public Either verifyVoteProof(VoteVerificationRequest voteVerificationRequest) { - var maybeSteps = voteVerificationRequest.getSteps(); + val maybeSteps = voteVerificationRequest.getSteps(); if (maybeSteps.isEmpty()) { log.warn("Merkle proof steps not found for:{}", voteVerificationRequest); @@ -47,9 +49,17 @@ public Either verifyVoteProof(VoteVerificationR .build()); } - var cip30Parser = new CIP30Verifier(voteVerificationRequest.getVoteCoseSignature(), voteVerificationRequest.getVoteCosePublicKey()); + return switch (voteVerificationRequest.getWalletType()) { + case WalletType.CARDANO -> verifyForCardano(voteVerificationRequest, maybeSteps); + case WalletType.KERI -> verifyForKeri(voteVerificationRequest, maybeSteps); + }; + } + + private Either verifyForCardano(VoteVerificationRequest voteVerificationRequest, + Optional> stepsM) { + val cip30Parser = new CIP30Verifier(voteVerificationRequest.getSignature(), voteVerificationRequest.getPublicKey()); - var cip30VerificationResult = cip30Parser.verify(); + val cip30VerificationResult = cip30Parser.verify(); if (!cip30VerificationResult.isValid()) { return Either.left(Problem.builder() @@ -60,17 +70,122 @@ public Either verifyVoteProof(VoteVerificationR } try { - var jsonNode = parseJson(cip30VerificationResult.getMessage(TEXT)); - var dataNode = jsonNode.get("data"); - var event = dataNode.get("event").asText(); + val jsonNode = parseJson(cip30VerificationResult.getMessage(TEXT)); + val dataNode = jsonNode.get("data"); + val event = dataNode.get("event").asText(); + + val maybeEventE = chainFollowerClient.findEventById(event); + + if (maybeEventE.isEmpty()) { + return Either.left(maybeEventE.getLeft()); + } + + val maybeEvent = maybeEventE.get(); + if (maybeEvent.isEmpty()) { + return Either.left(Problem.builder() + .withTitle("UNSUPPORTED_EVENT") + .withDetail("Unsupported event: " + event) + .withStatus(BAD_REQUEST) + .build()); + } + + val e = maybeEvent.orElseThrow(); + if (e.notStarted()) { + return Either.left(Problem.builder() + .withTitle("EVENT_NOT_STARTED") + .withDetail("Event not started yet: " + event) + .withStatus(BAD_REQUEST) + .build()); + } + + val voteNetwork = dataNode.get("network").asText(); + val maybeNetwork = Enums.getIfPresent(Network.class, voteNetwork); + if (maybeNetwork.isEmpty()) { + return Either.left(Problem.builder() + .withTitle("INVALID_NETWORK") + .withDetail("Invalid network.") + .withStatus(BAD_REQUEST) + .build()); + } + val network = maybeNetwork.orElseThrow(); + + if (network != network) { + log.warn("Invalid network, network:{}", voteNetwork); + + return Either.left(Problem.builder() + .withTitle("NETWORK_MISMATCH") + .withDetail("Invalid network, backend configured with network:" + network + ", however request is with network:" + network) + .withStatus(BAD_REQUEST) + .build()); + } + + val isPresent = chainFollowerClient.isMerkleProofPresent(event, voteVerificationRequest.getRootHash()); + + if (!isPresent) { + return Either.right(new VoteVerificationResult(false, WalletType.CARDANO, network)); + } + + val steps = io.vavr.collection.List.ofAll(deserialiseProofItems(stepsM.orElseThrow())); + + val vote = WrappedVote.createCardanoVote( + voteVerificationRequest.getWalletId(), + voteVerificationRequest.getSignature(), + voteVerificationRequest.getPublicKey() + ); + + val rootHash = decodeHexString(voteVerificationRequest.getRootHash()); + val isVerified = MerkleTree.verifyProof(rootHash, vote, steps, VOTE_SERIALISER); + + return Either.right(new VoteVerificationResult(isVerified, WalletType.CARDANO, network)); + } catch (JsonProcessingException e) { + return Either.left(Problem.builder() + .withTitle("INVALID_JSON") + .withStatus(BAD_REQUEST) + .withDetail("Invalid json in the vote!") + .build()); + } + } + + private Either verifyForKeri(VoteVerificationRequest voteVerificationRequest, + Optional> stepsM) { + val walletId = voteVerificationRequest.getWalletId(); + val walletType = voteVerificationRequest.getWalletType(); + val signature = voteVerificationRequest.getSignature(); + val payloadM = voteVerificationRequest.getPayload(); + + if (payloadM.isEmpty()) { + log.warn("Payload not found for:{}", voteVerificationRequest); + + return Either.left(Problem.builder() + .withTitle("INVALID_PAYLOAD") + .withStatus(BAD_REQUEST) + .withDetail("Payload not found!") + .build()); + } + + val payload = voteVerificationRequest.getPayload().orElseThrow(); + + val keriVerificationResultE = keriVerificationClient.verifySignature(walletId, signature, payload); + if (keriVerificationResultE.isLeft()) { + return Either.left(Problem.builder() + .withTitle("INVALID_VOTE") + .withStatus(BAD_REQUEST) + .withDetail("Invalid KERI signature") + .build()); + } + + try { + val jsonNode = parseJson(new String(HexUtil.decodeHexString(payload))); + val dataNode = jsonNode.get("data"); + val event = dataNode.get("event").asText(); - var maybeEventE = chainFollowerClient.findEventById(event); + val maybeEventE = chainFollowerClient.findEventById(event); if (maybeEventE.isEmpty()) { return Either.left(maybeEventE.getLeft()); } - var maybeEvent = maybeEventE.get(); + val maybeEvent = maybeEventE.get(); if (maybeEvent.isEmpty()) { return Either.left(Problem.builder() .withTitle("UNSUPPORTED_EVENT") @@ -79,7 +194,7 @@ public Either verifyVoteProof(VoteVerificationR .build()); } - var e = maybeEvent.orElseThrow(); + val e = maybeEvent.orElseThrow(); if (e.notStarted()) { return Either.left(Problem.builder() .withTitle("EVENT_NOT_STARTED") @@ -88,8 +203,8 @@ public Either verifyVoteProof(VoteVerificationR .build()); } - var voteNetwork = dataNode.get("network").asText(); - var maybeNetwork = Enums.getIfPresent(CardanoNetwork.class, voteNetwork); + val voteNetwork = dataNode.get("network").asText(); + val maybeNetwork = Enums.getIfPresent(Network.class, voteNetwork); if (maybeNetwork.isEmpty()) { return Either.left(Problem.builder() .withTitle("INVALID_NETWORK") @@ -97,32 +212,36 @@ public Either verifyVoteProof(VoteVerificationR .withStatus(BAD_REQUEST) .build()); } - var network = maybeNetwork.orElseThrow(); + val network = maybeNetwork.orElseThrow(); - if (network != cardanoNetwork) { + if (network != network) { log.warn("Invalid network, network:{}", voteNetwork); return Either.left(Problem.builder() .withTitle("NETWORK_MISMATCH") - .withDetail("Invalid network, backend configured with network:" + cardanoNetwork + ", however request is with network:" + network) + .withDetail("Invalid network, backend configured with network:" + network + ", however request is with network:" + network) .withStatus(BAD_REQUEST) .build()); } - var isPresent = chainFollowerClient.isMerkleProofPresent(event, voteVerificationRequest.getRootHash()); + val isPresent = chainFollowerClient.isMerkleProofPresent(event, voteVerificationRequest.getRootHash()); if (!isPresent) { - return Either.right(new VoteVerificationResult(false, network)); + return Either.right(new VoteVerificationResult(false, WalletType.KERI, network)); } - var steps = io.vavr.collection.List.ofAll(deserialiseProofItems(maybeSteps.orElseThrow())); + val steps = io.vavr.collection.List.ofAll(deserialiseProofItems(stepsM.orElseThrow())); - var vote = new CoseWrappedVote(voteVerificationRequest.getVoteCoseSignature(), voteVerificationRequest.getVoteCosePublicKey()); + val vote = WrappedVote.createKERIVote( + voteVerificationRequest.getWalletId(), + voteVerificationRequest.getSignature(), + payload + ); - var rootHash = decodeHexString(voteVerificationRequest.getRootHash()); - var isVerified = MerkleTree.verifyProof(rootHash, vote, steps, VOTE_SERIALISER); + val rootHash = decodeHexString(voteVerificationRequest.getRootHash()); + val isVerified = MerkleTree.verifyProof(rootHash, vote, steps, VOTE_SERIALISER); - return Either.right(new VoteVerificationResult(isVerified, network)); + return Either.right(new VoteVerificationResult(isVerified, WalletType.KERI, network)); } catch (JsonProcessingException e) { return Either.left(Problem.builder() .withTitle("INVALID_JSON") diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java index a841188f1..924fb1dd7 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java @@ -1,6 +1,7 @@ package org.cardano.foundation.voting.utils; -import org.cardano.foundation.voting.domain.CoseWrappedVote; +import lombok.val; +import org.cardano.foundation.voting.domain.WrappedVote; import org.cardanofoundation.cip30.CIP30Verifier; import java.util.Optional; @@ -10,16 +11,30 @@ public final class VoteSerialisations { - public static final Function VOTE_SERIALISER = createSerialiserFunction(); + public static final Function VOTE_SERIALISER = createSerialiserFunction(); - private static Function createSerialiserFunction() { + private static Function createSerialiserFunction() { return vote -> { - var cip30Verifier = new CIP30Verifier(vote.getCoseSignature(), vote.getCosePublicKey()); - var verificationResult = cip30Verifier.verify(); + return switch (vote.getWalletType()) { + case KERI -> { + val message = vote.getSignature().getBytes(); + val payload = vote.getPayload().map(String::getBytes).orElse(new byte[0]); - var bytes = Optional.ofNullable(verificationResult.getMessage()).orElse(new byte[0]); + val result = new byte[message.length + payload.length]; - return blake2bHash256(bytes); + System.arraycopy(message, 0, result, 0, payload.length); + + yield blake2bHash256(result); + } + case CARDANO -> { + val cip30Verifier = new CIP30Verifier(vote.getSignature(), vote.getPublicKey()); + val verificationResult = cip30Verifier.verify(); + + val bytes = Optional.ofNullable(verificationResult.getMessage()).orElse(new byte[0]); + + yield blake2bHash256(bytes); + } + }; }; } diff --git a/backend-services/voting-verification-app/src/main/resources/application.properties b/backend-services/voting-verification-app/src/main/resources/application.properties index 4be24baff..8f5417b14 100644 --- a/backend-services/voting-verification-app/src/main/resources/application.properties +++ b/backend-services/voting-verification-app/src/main/resources/application.properties @@ -22,5 +22,6 @@ spring.profiles.active=${SPRING_PROFILES_ACTIVE:dev--preprod} cors.allowed.origins=${CORS_ALLOWED_ORIGINS:http://localhost:3000} ledger.follower.app.base.url=${LEDGER_FOLLOWER_APP_URL:http://localhost:9090} +keri.ballot.verifier.base.url=${KERI_BALLOT_VERIFIER_APP_URL:http://localhost:5667} spring.jackson.default-property-inclusion=non_null \ No newline at end of file