Skip to content

Commit

Permalink
fix: support hashed content CIP-30 data content which is typically g… (
Browse files Browse the repository at this point in the history
…#593)

* feat: support hashed content CIP-30 data content which is typically generated by hardware wallets.

* chore(gradle): bump cip30-data-signature-parser to 0.0.12

* chore(gradle): remove usage of local maven repo

---------

Co-authored-by: Roberto C. Morano <[email protected]>
  • Loading branch information
matiwinnetou and rcmorano authored Nov 6, 2024
1 parent 57356cc commit 58d25c2
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 20 deletions.
2 changes: 1 addition & 1 deletion backend-services/voting-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ dependencies {
runtimeOnly("org.postgresql:postgresql")

implementation("org.cardanofoundation:merkle-tree-java:0.0.7")
implementation("org.cardanofoundation:cip30-data-signature-parser:0.0.11")
implementation("org.cardanofoundation:cip30-data-signature-parser:0.0.12")

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public class Vote extends AbstractTimestampEntity {
private String signature;

@Column(name = "payload", nullable = false, columnDefinition = "text", length = 2048)
@Nullable
@Nullable // TODO remove nullable since payload is now always required
private String payload;

@Column(name = "public_key")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public Either<Problem, LoginResult> login(Web3AuthenticationToken web3Authentica
}

private Either<Problem, LoginEnvelope> unwrapLoginVoteEnvelope(Web3ConcreteDetails concreteDetails) {
val jsonBody = concreteDetails.getSignedJson();
val jsonBody = concreteDetails.getPayload();

val jsonPayloadE = jsonService.decodeCIP93LoginEnvelope(jsonBody);
if (jsonPayloadE.isLeft()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class CardanoWeb3Details implements Web3ConcreteDetails {
private Cip30VerificationResult cip30VerificationResult;
private CIP93Envelope<Map<String, Object>> envelope;
private SignedCIP30 signedCIP30;
private String payload;

public String getUri() {
return envelope.getUri();
Expand All @@ -40,8 +41,12 @@ public String getSignature() {
return signedCIP30.getSignature();
}

public Optional<String> getPayload() {
return Optional.empty();
public String getPayload() {
if (cip30VerificationResult.isHashed()) {
return payload;
}

return cip30VerificationResult.getMessage(MessageFormat.TEXT);
}

public Optional<String> getPublicKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
import java.util.List;
import java.util.Optional;

import static com.bloxbean.cardano.client.crypto.Blake2bUtil.blake2bHash224;
import static com.bloxbean.cardano.client.util.HexUtil.decodeHexString;
import static com.bloxbean.cardano.client.util.HexUtil.encodeHexString;
import static org.cardano.foundation.voting.domain.Role.VOTER;
import static org.cardano.foundation.voting.domain.web3.WalletType.CARDANO;
import static org.cardano.foundation.voting.resource.Headers.X_Ballot_PublicKey;
import static org.cardano.foundation.voting.resource.Headers.X_Ballot_Signature;
import static org.cardano.foundation.voting.resource.Headers.*;
import static org.cardano.foundation.voting.service.auth.LoginSystem.CARDANO_CIP93;
import static org.cardano.foundation.voting.service.auth.web3.MoreFilters.sendBackProblem;
import static org.cardano.foundation.voting.utils.MoreNumber.isNumeric;
Expand Down Expand Up @@ -77,6 +79,7 @@ protected void doFilterInternal(HttpServletRequest req,

val signatureM = Optional.ofNullable(req.getHeader(X_Ballot_Signature));
val publicKey = req.getHeader(X_Ballot_PublicKey);
val payloadM = Optional.ofNullable(req.getHeader(X_Ballot_Payload));

if (signatureM.isEmpty()) {
val problem = Problem.builder()
Expand Down Expand Up @@ -122,7 +125,37 @@ protected void doFilterInternal(HttpServletRequest req,

val walletId = maybeAddress.orElseThrow();

val cipBody = cipVerificationResult.getMessage(MessageFormat.TEXT);
var cipBody = cipVerificationResult.getMessage(MessageFormat.TEXT);
if (cipVerificationResult.isHashed() && payloadM.isEmpty()) {
val problem = Problem.builder()
.withTitle("HASHED_CONTENT_NO_PAYLOAD")
.withDetail("Payload was not sent along with the request and CIP-30 signature contains is hashed!")
.withStatus(BAD_REQUEST)
.build();

sendBackProblem(objectMapper, res, problem);
return;
}

if (cipVerificationResult.isHashed()) {
val cipBodyHash = cipVerificationResult.getMessage(MessageFormat.HEX);
val payload = payloadM.orElseThrow();

val payloadHash = encodeHexString(blake2bHash224(decodeHexString(payload)));

if (!cipBodyHash.equals(payloadHash)) {
val problem = Problem.builder()
.withTitle("CIP_30_HASH_MISMATCH")
.withDetail("Signed hash does not match our precalculated hash!")
.withStatus(BAD_REQUEST)
.build();

sendBackProblem(objectMapper, res, problem);
return;
}

cipBody = new String(decodeHexString(payload)); // flip cipBody to be payload for further processing
}

val cip93EnvelopeE = jsonService.decodeGenericCIP93(cipBody);
if (cip93EnvelopeE.isEmpty()) {
Expand Down Expand Up @@ -318,6 +351,7 @@ protected void doFilterInternal(HttpServletRequest req,
.web3CommonDetails(commonWeb3Details)
.envelope(genericEnvelope)
.signedCIP30(signedWeb3Request)
.payload(cipBody)
.cip30VerificationResult(cipVerificationResult)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public String getSignature() {
}

@Override
public Optional<String> getPayload() {
return Optional.of(signedKERI.getPayload());
public String getPayload() {
return signedKERI.getPayload();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface Web3ConcreteDetails {

String getSignature();

Optional<String> getPayload();
String getPayload();

Optional<String> getPublicKey();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
import org.zalando.problem.Problem;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.Objects;

import static com.bloxbean.cardano.client.util.HexUtil.encodeHexString;
import static org.cardano.foundation.voting.domain.VoteReceipt.Status.*;
Expand Down Expand Up @@ -306,7 +306,7 @@ public Either<Problem, Vote> castVote(Web3AuthenticationToken web3Authentication
existingVote.setVotedAtSlot(castVote.getVotedAtSlot());
existingVote.setWalletType(walletType);
existingVote.setSignature(concreteDetails.getSignature());
existingVote.setPayload(concreteDetails.getPayload());
existingVote.setPayload(Optional.of(concreteDetails.getPayload()));
existingVote.setPublicKey(concreteDetails.getPublicKey());

return Either.right(voteRepository.saveAndFlush(existingVote));
Expand All @@ -321,7 +321,7 @@ public Either<Problem, Vote> castVote(Web3AuthenticationToken web3Authentication
vote.setWalletType(walletType);
vote.setVotedAtSlot(castVote.getVotedAtSlot());
vote.setSignature(concreteDetails.getSignature());
vote.setPayload(concreteDetails.getPayload());
vote.setPayload(Optional.of(concreteDetails.getPayload()));
vote.setPublicKey(concreteDetails.getPublicKey());
vote.setIdNumericHash(UUID.fromString(voteId).hashCode() & 0xFFFFFFF);

Expand Down Expand Up @@ -412,7 +412,7 @@ public Either<Problem, Vote> castVote(Web3AuthenticationToken web3Authentication
}

private Either<Problem, ViewVoteReceiptEnvelope> unwrapViewVoteReceiptEnvelope(Web3ConcreteDetails concreteDetails) {
val signedJson = concreteDetails.getSignedJson();
val signedJson = concreteDetails.getPayload();

switch (concreteDetails) {
case CardanoWeb3Details cardanoWeb3Details -> {
Expand Down Expand Up @@ -455,7 +455,7 @@ private Either<Problem, ViewVoteReceiptEnvelope> unwrapViewVoteReceiptEnvelope(W
}

private Either<Problem, VoteEnvelope> unwrapCastCoteEnvelope(Web3ConcreteDetails concreteDetails) {
val signedJson = concreteDetails.getSignedJson();
val signedJson = concreteDetails.getPayload();

switch (concreteDetails) {
case CardanoWeb3Details cardanoWeb3Details -> {
Expand Down
Loading

0 comments on commit 58d25c2

Please sign in to comment.