use of com.quorum.tessera.encryption.PublicKey in project tessera by ConsenSys.
the class TransactionManagerImpl method storePayload.
@Override
public synchronized MessageHash storePayload(final EncodedPayload payload) {
final byte[] digest = payloadDigest.digest(payload.getCipherText());
final MessageHash transactionHash = new MessageHash(digest);
final List<AffectedTransaction> affectedContractTransactions = privacyHelper.findAffectedContractTransactionsFromPayload(payload);
final TxHash txHash = TxHash.from(transactionHash.getHashBytes());
if (!privacyHelper.validatePayload(txHash, payload, affectedContractTransactions)) {
return transactionHash;
}
final Set<TxHash> invalidSecurityHashes = enclave.findInvalidSecurityHashes(payload, affectedContractTransactions);
final EncodedPayload encodedPayload;
if (!invalidSecurityHashes.isEmpty()) {
encodedPayload = privacyHelper.sanitisePrivacyPayload(txHash, payload, invalidSecurityHashes);
} else {
encodedPayload = payload;
}
LOGGER.debug("AffectedContractTransaction.size={} InvalidSecurityHashes.size={}", affectedContractTransactions.size(), invalidSecurityHashes.size());
if (enclave.getPublicKeys().contains(encodedPayload.getSenderKey())) {
// This is our own message that we are rebuilding, handle separately
this.resendManager.acceptOwnMessage(encodedPayload);
LOGGER.debug("Stored payload for which we were the sender. Hash = {}", transactionHash);
return transactionHash;
}
// This is a transaction with a different node as the sender
final Optional<EncryptedTransaction> tx = this.encryptedTransactionDAO.retrieveByHash(transactionHash);
if (tx.isEmpty()) {
// This is the first time we have seen the payload, so just save it to the database as is
this.encryptedTransactionDAO.save(new EncryptedTransaction(transactionHash, encodedPayload));
LOGGER.debug("Stored new payload with hash {}", transactionHash);
return transactionHash;
}
final EncryptedTransaction encryptedTransaction = tx.get();
final EncodedPayload existing = encryptedTransaction.getPayload();
// check all the other bits of the payload match
final boolean txMatches = Stream.of(Arrays.equals(existing.getCipherText(), encodedPayload.getCipherText()), Objects.equals(existing.getCipherTextNonce(), encodedPayload.getCipherTextNonce()), Objects.equals(existing.getSenderKey(), encodedPayload.getSenderKey()), Objects.equals(existing.getRecipientNonce(), encodedPayload.getRecipientNonce()), Objects.equals(existing.getPrivacyMode(), encodedPayload.getPrivacyMode()), Arrays.equals(existing.getExecHash(), encodedPayload.getExecHash()), // checks the affected contracts contents match
Objects.equals(existing.getAffectedContractTransactions().size(), payload.getAffectedContractTransactions().size()), existing.getAffectedContractTransactions().entrySet().stream().allMatch(e -> e.getValue().equals(payload.getAffectedContractTransactions().get(e.getKey())))).allMatch(p -> p);
if (!txMatches) {
throw new RuntimeException("Invalid existing transaction");
}
// this is the easiest way to tell if a recipient has already been included
for (RecipientBox existingBox : existing.getRecipientBoxes()) {
if (Objects.equals(existingBox, encodedPayload.getRecipientBoxes().get(0))) {
// recipient must already exist, so just act as though things went normally
LOGGER.info("Recipient already existed in payload with hash {}", transactionHash);
return transactionHash;
}
}
final EncodedPayload.Builder existingPayloadBuilder = EncodedPayload.Builder.from(existing);
// Boxes are all handled the same way. The new payload will only contain one box, and this will
// be prepended
// to the the list of existing boxes.
final List<RecipientBox> existingBoxes = new ArrayList<>(existing.getRecipientBoxes());
existingBoxes.add(0, encodedPayload.getRecipientBoxes().get(0));
existingPayloadBuilder.withRecipientBoxes(existingBoxes.stream().map(RecipientBox::getData).collect(Collectors.toList()));
// is handled implicitly, as we don't need to add anything to the recipients list
if (PrivacyMode.PRIVATE_STATE_VALIDATION == encodedPayload.getPrivacyMode()) {
// PSV transaction, we have one box, and the relevant key is the first value
// the existing payload will contain the key, but we can remove it and prepend the key again,
// along with the
// box
final List<PublicKey> existingKeys = new ArrayList<>(existing.getRecipientKeys());
final PublicKey newRecipient = encodedPayload.getRecipientKeys().get(0);
if (!existingKeys.contains(newRecipient)) {
throw new RuntimeException("expected recipient not found");
}
existingKeys.remove(newRecipient);
existingKeys.add(0, newRecipient);
existingPayloadBuilder.withNewRecipientKeys(existingKeys);
} else if (!encodedPayload.getRecipientKeys().isEmpty()) {
// Regular tx, add the recipient and the box
final List<PublicKey> existingKeys = new ArrayList<>(existing.getRecipientKeys());
final PublicKey newRecipient = encodedPayload.getRecipientKeys().get(0);
existingKeys.add(0, newRecipient);
existingPayloadBuilder.withNewRecipientKeys(existingKeys);
}
encryptedTransaction.setPayload(existingPayloadBuilder.build());
this.encryptedTransactionDAO.update(encryptedTransaction);
LOGGER.info("Updated existing payload with hash {}", transactionHash);
return transactionHash;
}
use of com.quorum.tessera.encryption.PublicKey in project tessera by ConsenSys.
the class EncodedPayloadManagerImplTest method createCallsEnclaveCorrectly.
@Test
public void createCallsEnclaveCorrectly() {
final byte[] payload = "test payload".getBytes();
final String senderKeyBase64 = "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=";
final PublicKey sender = PublicKey.from(Base64.getDecoder().decode(senderKeyBase64));
final String singleRecipientBase64 = "QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc=";
final PublicKey singleRecipient = PublicKey.from(Base64.getDecoder().decode(singleRecipientBase64));
final List<PublicKey> recipients = List.of(singleRecipient);
final SendRequest request = mock(SendRequest.class);
when(request.getSender()).thenReturn(sender);
when(request.getRecipients()).thenReturn(recipients);
when(request.getPayload()).thenReturn(payload);
when(request.getPrivacyMode()).thenReturn(PrivacyMode.STANDARD_PRIVATE);
when(request.getAffectedContractTransactions()).thenReturn(emptySet());
when(request.getExecHash()).thenReturn(new byte[0]);
final EncodedPayload sampleReturnPayload = mock(EncodedPayload.class);
when(enclave.encryptPayload(any(), eq(sender), eq(List.of(singleRecipient, sender)), any())).thenReturn(sampleReturnPayload);
final EncodedPayload encodedPayload = encodedPayloadManager.create(request);
assertThat(encodedPayload).isEqualTo(sampleReturnPayload);
verify(privacyHelper).findAffectedContractTransactionsFromSendRequest(emptySet());
verify(privacyHelper).validateSendRequest(PrivacyMode.STANDARD_PRIVATE, List.of(singleRecipient, sender), emptyList(), emptySet());
verify(enclave).encryptPayload(eq(request.getPayload()), eq(sender), eq(List.of(singleRecipient, sender)), any());
verify(enclave).getForwardingKeys();
}
use of com.quorum.tessera.encryption.PublicKey in project tessera by ConsenSys.
the class PrivacyHelperTest method validatePsvPayloadRecipientsMismatched.
@Test
public void validatePsvPayloadRecipientsMismatched() {
PublicKey recipient1 = PublicKey.from("Recipient1".getBytes());
PublicKey recipient2 = PublicKey.from("Recipient2".getBytes());
final TxHash txHash = TxHash.from("someHash".getBytes());
EncodedPayload payload = mock(EncodedPayload.class);
when(payload.getPrivacyMode()).thenReturn(PrivacyMode.PRIVATE_STATE_VALIDATION);
when(payload.getSenderKey()).thenReturn(recipient1);
when(payload.getRecipientKeys()).thenReturn(List.of(recipient1, recipient2));
Map<TxHash, SecurityHash> affected = new HashMap<>();
affected.put(TxHash.from("Hash1".getBytes()), SecurityHash.from("secHash1".getBytes()));
affected.put(TxHash.from("Hash2".getBytes()), SecurityHash.from("secHash2".getBytes()));
when(payload.getAffectedContractTransactions()).thenReturn(affected);
EncodedPayload affectedPayload1 = mock(EncodedPayload.class);
when(affectedPayload1.getPrivacyMode()).thenReturn(PrivacyMode.PRIVATE_STATE_VALIDATION);
when(affectedPayload1.getRecipientKeys()).thenReturn(singletonList(recipient1));
AffectedTransaction affectedTransaction1 = mock(AffectedTransaction.class);
when(affectedTransaction1.getPayload()).thenReturn(affectedPayload1);
when(affectedTransaction1.getHash()).thenReturn(TxHash.from("hash1".getBytes()));
EncodedPayload affectedPayload2 = mock(EncodedPayload.class);
when(affectedPayload2.getPrivacyMode()).thenReturn(PrivacyMode.PRIVATE_STATE_VALIDATION);
when(affectedPayload2.getRecipientKeys()).thenReturn(List.of(recipient1, recipient2));
AffectedTransaction affectedTransaction2 = mock(AffectedTransaction.class);
when(affectedTransaction2.getPayload()).thenReturn(affectedPayload2);
when(affectedTransaction2.getHash()).thenReturn(TxHash.from("hash2".getBytes()));
assertThatExceptionOfType(PrivacyViolationException.class).isThrownBy(() -> privacyHelper.validatePayload(txHash, payload, List.of(affectedTransaction1, affectedTransaction2))).withMessage("Recipients mismatched for Affected Txn " + TxHash.from("hash1".getBytes()).encodeToBase64());
}
use of com.quorum.tessera.encryption.PublicKey in project tessera by ConsenSys.
the class PrivacyHelperTest method testValidateSendPsv.
@Test
public void testValidateSendPsv() {
PublicKey recipient1 = mock(PublicKey.class);
PublicKey recipient2 = mock(PublicKey.class);
final EncodedPayload encodedPayload = mock(EncodedPayload.class);
when(encodedPayload.getPrivacyMode()).thenReturn(PrivacyMode.PRIVATE_STATE_VALIDATION);
when(encodedPayload.getRecipientKeys()).thenReturn(List.of(recipient1, recipient2));
final AffectedTransaction affectedTransaction = mock(AffectedTransaction.class);
when(affectedTransaction.getPayload()).thenReturn(encodedPayload);
final TxHash hash = TxHash.from("someHash".getBytes());
when(affectedTransaction.getHash()).thenReturn(hash);
boolean isValid = privacyHelper.validateSendRequest(PrivacyMode.PRIVATE_STATE_VALIDATION, List.of(recipient1, recipient2), singletonList(affectedTransaction), emptySet());
assertThat(isValid).isTrue();
}
use of com.quorum.tessera.encryption.PublicKey in project tessera by ConsenSys.
the class PrivacyHelperTest method testValidateSendPsvMoreRecipientsAffected.
@Test
public void testValidateSendPsvMoreRecipientsAffected() {
PublicKey recipient1 = mock(PublicKey.class);
PublicKey recipient2 = mock(PublicKey.class);
final EncodedPayload encodedPayload = mock(EncodedPayload.class);
when(encodedPayload.getPrivacyMode()).thenReturn(PrivacyMode.PRIVATE_STATE_VALIDATION);
when(encodedPayload.getRecipientKeys()).thenReturn(List.of(recipient1, recipient2));
final AffectedTransaction affectedTransaction = mock(AffectedTransaction.class);
when(affectedTransaction.getPayload()).thenReturn(encodedPayload);
final TxHash hash = TxHash.from("someHash".getBytes());
when(affectedTransaction.getHash()).thenReturn(hash);
assertThatExceptionOfType(PrivacyViolationException.class).isThrownBy(() -> privacyHelper.validateSendRequest(PrivacyMode.PRIVATE_STATE_VALIDATION, List.of(recipient1), singletonList(affectedTransaction), emptySet())).withMessage("Recipients mismatched for Affected Txn " + hash.encodeToBase64());
}
Aggregations