All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quorum.tessera.enclave.EnclaveImpl Maven / Gradle / Ivy

There is a newer version: 0.11.0.30092019161842
Show newest version
package com.quorum.tessera.enclave;

import com.quorum.tessera.encryption.Encryptor;
import com.quorum.tessera.encryption.KeyManager;
import com.quorum.tessera.encryption.MasterKey;
import com.quorum.tessera.encryption.Nonce;
import com.quorum.tessera.encryption.PrivateKey;
import com.quorum.tessera.encryption.PublicKey;
import com.quorum.tessera.encryption.SharedKey;
import static java.util.Collections.singletonList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

public class EnclaveImpl implements Enclave {

    private final Encryptor encryptor;

    private final KeyManager keyManager;

    public EnclaveImpl(Encryptor encryptor, KeyManager keyManager) {
        this.encryptor = Objects.requireNonNull(encryptor);
        this.keyManager = Objects.requireNonNull(keyManager);
    }

    @Override
    public EncodedPayload encryptPayload(
            final byte[] message, final PublicKey senderPublicKey, final List recipientPublicKeys) {
        final MasterKey masterKey = encryptor.createMasterKey();
        final Nonce nonce = encryptor.randomNonce();
        final Nonce recipientNonce = encryptor.randomNonce();

        final byte[] cipherText = encryptor.sealAfterPrecomputation(message, nonce, masterKey);

        final List encryptedMasterKeys =
                buildRecipientMasterKeys(senderPublicKey, recipientPublicKeys, recipientNonce, masterKey);

        return EncodedPayload.Builder.create()
                .withSenderKey(senderPublicKey)
                .withCipherText(cipherText)
                .withCipherTextNonce(nonce)
                .withRecipientBoxes(encryptedMasterKeys)
                .withRecipientNonce(recipientNonce)
                .withRecipientKeys(recipientPublicKeys)
                .build();
    }

    @Override
    public byte[] createNewRecipientBox(final EncodedPayload payload, final PublicKey publicKey) {

        if (payload.getRecipientKeys().isEmpty() || payload.getRecipientBoxes().isEmpty()) {
            throw new RuntimeException("No key or recipient-box to use");
        }

        final MasterKey master =
                this.getMasterKey(
                        payload.getRecipientKeys().get(0), payload.getSenderKey(),
                        payload.getRecipientNonce(), payload.getRecipientBoxes().get(0));

        final List sealedMasterKeyList =
                this.buildRecipientMasterKeys(
                        payload.getSenderKey(), singletonList(publicKey), payload.getRecipientNonce(), master);

        return sealedMasterKeyList.get(0);
    }

    @Override
    public EncodedPayload encryptPayload(
            final RawTransaction rawTransaction, final List recipientPublicKeys) {
        final MasterKey masterKey =
                this.getMasterKey(
                        rawTransaction.getFrom(), rawTransaction.getFrom(),
                        rawTransaction.getNonce(), rawTransaction.getEncryptedKey());

        final Nonce recipientNonce = encryptor.randomNonce();
        final List encryptedMasterKeys =
                buildRecipientMasterKeys(rawTransaction.getFrom(), recipientPublicKeys, recipientNonce, masterKey);

        return EncodedPayload.Builder.create()
                .withSenderKey(rawTransaction.getFrom())
                .withCipherText(rawTransaction.getEncryptedPayload())
                .withCipherTextNonce(rawTransaction.getNonce())
                .withRecipientBoxes(encryptedMasterKeys)
                .withRecipientNonce(recipientNonce)
                .withRecipientKeys(recipientPublicKeys)
                .build();
    }

    private List buildRecipientMasterKeys(
            final PublicKey senderPublicKey,
            final List recipientPublicKeys,
            final Nonce recipientNonce,
            final MasterKey masterKey) {
        final PrivateKey privateKey = keyManager.getPrivateKeyForPublicKey(senderPublicKey);

        return recipientPublicKeys.stream()
                .map(publicKey -> encryptor.computeSharedKey(publicKey, privateKey))
                .map(sharedKey -> encryptor.sealAfterPrecomputation(masterKey.getKeyBytes(), recipientNonce, sharedKey))
                .collect(Collectors.toList());
    }

    @Override
    public RawTransaction encryptRawPayload(byte[] message, PublicKey sender) {
        final MasterKey masterKey = encryptor.createMasterKey();
        final Nonce nonce = encryptor.randomNonce();

        final byte[] cipherText = encryptor.sealAfterPrecomputation(message, nonce, masterKey);

        final PrivateKey privateKey = keyManager.getPrivateKeyForPublicKey(sender);

        // TODO NL - check if it makes sense to compute a shared key from the public and private parts of the same key
        SharedKey sharedKey = encryptor.computeSharedKey(sender, privateKey);
        final byte[] encryptedMasterKey = encryptor.sealAfterPrecomputation(masterKey.getKeyBytes(), nonce, sharedKey);

        return new RawTransaction(cipherText, encryptedMasterKey, nonce, sender);
    }

    @Override
    public byte[] unencryptTransaction(EncodedPayload payload, final PublicKey providedSenderKey) {

        final PublicKey senderPubKey;

        final PublicKey recipientPubKey;

        if (!this.getPublicKeys().contains(payload.getSenderKey())) {
            // This is a payload originally sent to us by another node
            senderPubKey = providedSenderKey;
            recipientPubKey = payload.getSenderKey();
        } else {
            // This is a payload that originated from us
            senderPubKey = payload.getSenderKey();
            recipientPubKey = payload.getRecipientKeys().get(0);
        }

        final PrivateKey senderPrivKey = keyManager.getPrivateKeyForPublicKey(senderPubKey);

        final SharedKey sharedKey = encryptor.computeSharedKey(recipientPubKey, senderPrivKey);

        final byte[] recipientBox = payload.getRecipientBoxes().iterator().next();

        final Nonce recipientNonce = payload.getRecipientNonce();

        final byte[] masterKeyBytes = encryptor.openAfterPrecomputation(recipientBox, recipientNonce, sharedKey);

        final MasterKey masterKey = MasterKey.from(masterKeyBytes);

        final byte[] cipherText = payload.getCipherText();
        final Nonce cipherTextNonce = payload.getCipherTextNonce();

        return encryptor.openAfterPrecomputation(cipherText, cipherTextNonce, masterKey);
    }

    private MasterKey getMasterKey(PublicKey recipient, PublicKey sender, Nonce nonce, byte[] encryptedKey) {

        final SharedKey sharedKey = encryptor.computeSharedKey(recipient, keyManager.getPrivateKeyForPublicKey(sender));

        final byte[] masterKeyBytes = encryptor.openAfterPrecomputation(encryptedKey, nonce, sharedKey);

        return MasterKey.from(masterKeyBytes);
    }

    @Override
    public PublicKey defaultPublicKey() {
        return keyManager.defaultPublicKey();
    }

    @Override
    public Set getForwardingKeys() {
        return keyManager.getForwardingKeys();
    }

    @Override
    public Set getPublicKeys() {
        return keyManager.getPublicKeys();
    }

    @Override
    public Status status() {
        return Status.STARTED;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy