Skip to content

Commit

Permalink
Merge pull request #1325 from ConsenSys/support-mandatory-recipients
Browse files Browse the repository at this point in the history
Support for mandatory recipients
  • Loading branch information
Krish1979 authored Aug 18, 2021
2 parents 25fef0c + f7fe1de commit 9a7c938
Show file tree
Hide file tree
Showing 52 changed files with 2,865 additions and 154 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public EncodedPayload encryptPayload(
.withPrivacyMode(privacyMetadata.getPrivacyMode())
.withAffectedContractTransactions(affectedContractTransactionHashes)
.withExecHash(privacyMetadata.getExecHash())
.withMandatoryRecipients(privacyMetadata.getMandatoryRecipients())
.build();
}

Expand Down Expand Up @@ -149,6 +150,7 @@ public EncodedPayload encryptPayload(
.withPrivacyMode(privacyMetadata.getPrivacyMode())
.withAffectedContractTransactions(affectedContractTransactionHashes)
.withExecHash(privacyMetadata.getExecHash())
.withMandatoryRecipients(privacyMetadata.getMandatoryRecipients())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class EncodedPayload {

private final PrivacyGroup.Id privacyGroupId;

private final Set<PublicKey> mandatoryRecipients;

private EncodedPayload(
final PublicKey senderKey,
final byte[] cipherText,
Expand All @@ -38,7 +40,8 @@ private EncodedPayload(
final PrivacyMode privacyMode,
final Map<TxHash, SecurityHash> affectedContractTransactions,
final byte[] execHash,
final PrivacyGroup.Id privacyGroupId) {
final PrivacyGroup.Id privacyGroupId,
final Set<PublicKey> mandatoryRecipients) {
this.senderKey = senderKey;
this.cipherText = cipherText;
this.cipherTextNonce = cipherTextNonce;
Expand All @@ -49,6 +52,7 @@ private EncodedPayload(
this.affectedContractTransactions = affectedContractTransactions;
this.execHash = execHash;
this.privacyGroupId = privacyGroupId;
this.mandatoryRecipients = mandatoryRecipients;
}

public PublicKey getSenderKey() {
Expand Down Expand Up @@ -91,6 +95,10 @@ public Optional<PrivacyGroup.Id> getPrivacyGroupId() {
return Optional.ofNullable(privacyGroupId);
}

public Set<PublicKey> getMandatoryRecipients() {
return mandatoryRecipients;
}

public static class Builder {

private Builder() {}
Expand Down Expand Up @@ -145,6 +153,8 @@ public static Builder from(EncodedPayload encodedPayload) {

private PrivacyGroup.Id privacyGroupId;

private Set<PublicKey> mandatoryRecipients = Collections.emptySet();

public Builder withSenderKey(final PublicKey senderKey) {
this.senderKey = senderKey;
return this;
Expand Down Expand Up @@ -227,6 +237,11 @@ public Builder withPrivacyGroupId(final PrivacyGroup.Id privacyGroupId) {
return this;
}

public Builder withMandatoryRecipients(final Set<PublicKey> mandatoryRecipients) {
this.mandatoryRecipients = mandatoryRecipients;
return this;
}

public EncodedPayload build() {

Map<TxHash, SecurityHash> affectedTransactions =
Expand All @@ -242,6 +257,12 @@ public EncodedPayload build() {
throw new RuntimeException("ExecutionHash data is invalid");
}

if ((privacyMode == PrivacyMode.MANDATORY_RECIPIENTS) == mandatoryRecipients.isEmpty()) {
throw new RuntimeException(
"Mandatory recipients data only applicable for Mandatory Recipients privacy mode. "
+ "In case no mandatory recipient is required, consider using Party Protection privacy mode");
}

return new EncodedPayload(
senderKey,
cipherText,
Expand All @@ -252,7 +273,8 @@ public EncodedPayload build() {
privacyMode,
affectedTransactions,
execHash,
privacyGroupId);
privacyGroupId,
mandatoryRecipients);
}
}

Expand All @@ -269,7 +291,8 @@ public boolean equals(Object o) {
&& Objects.equals(recipientKeys, that.recipientKeys)
&& privacyMode == that.privacyMode
&& Arrays.equals(execHash, that.execHash)
&& Objects.equals(privacyGroupId, that.privacyGroupId);
&& Objects.equals(privacyGroupId, that.privacyGroupId)
&& Objects.equals(mandatoryRecipients, that.mandatoryRecipients);
}

@Override
Expand All @@ -282,7 +305,8 @@ public int hashCode() {
recipientNonce,
recipientKeys,
privacyMode,
privacyGroupId);
privacyGroupId,
mandatoryRecipients);
result = 31 * result + Arrays.hashCode(cipherText);
result = 31 * result + Arrays.hashCode(execHash);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ public byte[] encode(final EncodedPayload payload) {
executionHash = encodeField(payload.getExecHash());
}

byte[] mandatoryRecipients = new byte[0];
if (payload.getPrivacyMode() == PrivacyMode.MANDATORY_RECIPIENTS) {
mandatoryRecipients =
encodeArray(
payload.getMandatoryRecipients().stream()
.map(PublicKey::getKeyBytes)
.collect(Collectors.toUnmodifiableList()));
}

byte[] privacyGroupId =
payload
.getPrivacyGroupId()
Expand All @@ -70,6 +79,7 @@ public byte[] encode(final EncodedPayload payload) {
+ privacyModeByte.length
+ affectedContractsPayloadLength
+ executionHash.length
+ mandatoryRecipients.length
+ privacyGroupId.length)
.put(senderKey)
.put(cipherText)
Expand All @@ -80,6 +90,7 @@ public byte[] encode(final EncodedPayload payload) {
.put(privacyModeByte)
.put(affectedContractTxs.array())
.put(executionHash)
.put(mandatoryRecipients)
.put(privacyGroupId)
.array();
}
Expand Down Expand Up @@ -113,17 +124,20 @@ public EncodedPayload decode(final byte[] input) {
final byte[] recipientNonce = new byte[Math.toIntExact(recipientNonceSize)];
buffer.get(recipientNonce);

EncodedPayload.Builder payloadBuilder = EncodedPayload.Builder.create();

payloadBuilder
.withSenderKey(PublicKey.from(senderKey))
.withCipherText(cipherText)
.withCipherTextNonce(nonce)
.withRecipientBoxes(recipientBoxes)
.withRecipientNonce(recipientNonce);

// this means there are no recipients in the payload (which we receive when we are a
// participant)
// TODO - not sure this is right
if (!buffer.hasRemaining()) {

return EncodedPayload.Builder.create()
.withSenderKey(PublicKey.from(senderKey))
.withCipherText(cipherText)
.withCipherTextNonce(nonce)
.withRecipientBoxes(recipientBoxes)
.withRecipientNonce(recipientNonce)
return payloadBuilder
.withRecipientKeys(emptyList())
.withPrivacyMode(PrivacyMode.STANDARD_PRIVATE)
.withAffectedContractTransactions(emptyMap())
Expand All @@ -141,15 +155,10 @@ public EncodedPayload decode(final byte[] input) {
recipientKeys.add(box);
}

if (!buffer.hasRemaining()) {
payloadBuilder.withRecipientKeys(recipientKeys.stream().map(PublicKey::from).collect(toList()));

return EncodedPayload.Builder.create()
.withSenderKey(PublicKey.from(senderKey))
.withCipherText(cipherText)
.withCipherTextNonce(nonce)
.withRecipientBoxes(recipientBoxes)
.withRecipientNonce(recipientNonce)
.withRecipientKeys(recipientKeys.stream().map(PublicKey::from).collect(toList()))
if (!buffer.hasRemaining()) {
return payloadBuilder
.withPrivacyMode(PrivacyMode.STANDARD_PRIVATE)
.withAffectedContractTransactions(emptyMap())
.withExecHash(new byte[0])
Expand Down Expand Up @@ -185,36 +194,40 @@ public EncodedPayload decode(final byte[] input) {
}
}

payloadBuilder
.withPrivacyMode(privacyMode)
.withAffectedContractTransactions(affectedContractTransactions)
.withExecHash(executionHash);

if (buffer.hasRemaining()) {
if (privacyMode == PrivacyMode.MANDATORY_RECIPIENTS) {
final long mandatoryRecipientLength = buffer.getLong();

final List<byte[]> mandatoryRecipients = new ArrayList<>();
for (long i = 0; i < mandatoryRecipientLength; i++) {
final long boxSize = buffer.getLong();
final byte[] box = new byte[Math.toIntExact(boxSize)];
buffer.get(box);
mandatoryRecipients.add(box);
}
payloadBuilder.withMandatoryRecipients(
mandatoryRecipients.stream().map(PublicKey::from).collect(Collectors.toSet()));
}
}

if (!buffer.hasRemaining()) {
return EncodedPayload.Builder.create()
.withSenderKey(PublicKey.from(senderKey))
.withCipherText(cipherText)
.withCipherTextNonce(nonce)
.withRecipientBoxes(recipientBoxes)
.withRecipientNonce(recipientNonce)
.withRecipientKeys(recipientKeys.stream().map(PublicKey::from).collect(toList()))
.withPrivacyMode(privacyMode)
.withAffectedContractTransactions(affectedContractTransactions)
.withExecHash(executionHash)
.build();
return payloadBuilder.build();
}

final long privacyGroupIdSize = buffer.getLong();
final byte[] privacyGroupId = new byte[Math.toIntExact(privacyGroupIdSize)];
buffer.get(privacyGroupId);

return EncodedPayload.Builder.create()
.withSenderKey(PublicKey.from(senderKey))
.withCipherText(cipherText)
.withCipherTextNonce(nonce)
.withRecipientBoxes(recipientBoxes)
.withRecipientNonce(recipientNonce)
.withRecipientKeys(recipientKeys.stream().map(PublicKey::from).collect(toList()))
.withPrivacyMode(privacyMode)
.withAffectedContractTransactions(affectedContractTransactions)
.withExecHash(executionHash)
.withPrivacyGroupId(PrivacyGroup.Id.fromBytes(privacyGroupId))
.build();
if (privacyGroupId.length > 0) {
payloadBuilder.withPrivacyGroupId(PrivacyGroup.Id.fromBytes(privacyGroupId));
}

return payloadBuilder.build();
}

@Override
Expand Down Expand Up @@ -252,7 +265,8 @@ public EncodedPayload forRecipient(final EncodedPayload payload, final PublicKey
.withRecipientKeys(recipientList)
.withPrivacyMode(payload.getPrivacyMode())
.withAffectedContractTransactions(affectedTxnMap)
.withExecHash(payload.getExecHash());
.withExecHash(payload.getExecHash())
.withMandatoryRecipients(payload.getMandatoryRecipients());
payload.getPrivacyGroupId().ifPresent(builder::withPrivacyGroupId);

return builder.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.quorum.tessera.enclave;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import com.quorum.tessera.encryption.PublicKey;
import java.util.*;

public interface PrivacyMetadata {

Expand All @@ -15,6 +13,8 @@ public interface PrivacyMetadata {

Optional<PrivacyGroup.Id> getPrivacyGroupId();

Set<PublicKey> getMandatoryRecipients();

class Builder {

private PrivacyMode privacyMode;
Expand All @@ -25,6 +25,8 @@ class Builder {

private PrivacyGroup.Id privacyGroupId;

private Set<PublicKey> mandatoryRecipients = Collections.emptySet();

public static Builder create() {
return new Builder();
}
Expand All @@ -49,6 +51,11 @@ public Builder withPrivacyGroupId(PrivacyGroup.Id privacyGroupId) {
return this;
}

public Builder withMandatoryRecipients(Set<PublicKey> mandatoryRecipients) {
this.mandatoryRecipients = mandatoryRecipients;
return this;
}

public PrivacyMetadata build() {

Objects.requireNonNull(privacyMode, "privacyMode is required");
Expand All @@ -58,6 +65,12 @@ public PrivacyMetadata build() {
throw new RuntimeException("ExecutionHash data is invalid");
}

if ((privacyMode == PrivacyMode.MANDATORY_RECIPIENTS) == mandatoryRecipients.isEmpty()) {
throw new RuntimeException(
"Mandatory recipients data only applicable for Mandatory Recipients privacy mode. "
+ "In case no mandatory recipient is required, consider using Party Protection privacy mode");
}

return new PrivacyMetadata() {
@Override
public PrivacyMode getPrivacyMode() {
Expand All @@ -78,6 +91,11 @@ public byte[] getExecHash() {
public Optional<PrivacyGroup.Id> getPrivacyGroupId() {
return Optional.ofNullable(privacyGroupId);
}

@Override
public Set<PublicKey> getMandatoryRecipients() {
return Set.copyOf(mandatoryRecipients);
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public enum PrivacyMode {
STANDARD_PRIVATE(0),
PARTY_PROTECTION(1),
MANDATORY_RECIPIENTS(2),
PRIVATE_STATE_VALIDATION(3);

private final int privacyFlag;
Expand Down
Loading

0 comments on commit 9a7c938

Please sign in to comment.