package io.bitsquare.p2p.storage.payload; import io.bitsquare.app.Version; import io.bitsquare.common.crypto.Sig; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.messaging.PrefixedSealedAndSignedMessage; import io.bitsquare.p2p.peers.BroadcastHandler; import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.concurrent.TimeUnit; /** * Envelope message which support a time to live and sender and receiver's pub keys for storage operations. * It differs from the ProtectedExpirableMessage in the way that the sender is permitted to do an add operation * but only the receiver is permitted to remove the data. * That is the typical requirement for a mailbox like system. * <p> * Typical payloads are trade or dispute messages to be stored when the peer is offline. */ public final class MailboxStoragePayload implements StoragePayload { // That object is sent over the wire, so we need to take care of version compatibility. private static final long serialVersionUID = Version.P2P_NETWORK_VERSION; private static final Logger log = LoggerFactory.getLogger(MailboxStoragePayload.class); private static final long TTL = TimeUnit.DAYS.toMillis(10); /** * The encrypted and signed payload message */ public final PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage; /** * Used for check if the add operation is permitted. * senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData * * @see ProtectedStorageEntry#ownerPubKey * @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedStorageEntry, NodeAddress, BroadcastHandler.Listener, boolean) */ public transient PublicKey senderPubKeyForAddOperation; private final byte[] senderPubKeyForAddOperationBytes; /** * Used for check if the remove operation is permitted. * senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData * * @see ProtectedStorageEntry#ownerPubKey * @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedStorageEntry, NodeAddress, boolean) */ public transient PublicKey receiverPubKeyForRemoveOperation; private final byte[] receiverPubKeyForRemoveOperationBytes; public MailboxStoragePayload(PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage, PublicKey senderPubKeyForAddOperation, PublicKey receiverPubKeyForRemoveOperation) { this.prefixedSealedAndSignedMessage = prefixedSealedAndSignedMessage; this.senderPubKeyForAddOperation = senderPubKeyForAddOperation; this.senderPubKeyForAddOperationBytes = new X509EncodedKeySpec(this.senderPubKeyForAddOperation.getEncoded()).getEncoded(); this.receiverPubKeyForRemoveOperation = receiverPubKeyForRemoveOperation; this.receiverPubKeyForRemoveOperationBytes = new X509EncodedKeySpec(this.receiverPubKeyForRemoveOperation.getEncoded()).getEncoded(); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { try { in.defaultReadObject(); senderPubKeyForAddOperation = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(senderPubKeyForAddOperationBytes)); receiverPubKeyForRemoveOperation = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(receiverPubKeyForRemoveOperationBytes)); } catch (Throwable t) { log.warn("Exception at readObject: " + t.getMessage() + "\nThis= " + this.toString()); } } @Override public long getTTL() { return TTL; } @Override public PublicKey getOwnerPubKey() { return receiverPubKeyForRemoveOperation; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof MailboxStoragePayload)) return false; MailboxStoragePayload that = (MailboxStoragePayload) o; return !(prefixedSealedAndSignedMessage != null ? !prefixedSealedAndSignedMessage.equals(that.prefixedSealedAndSignedMessage) : that.prefixedSealedAndSignedMessage != null); } @Override public int hashCode() { return prefixedSealedAndSignedMessage != null ? prefixedSealedAndSignedMessage.hashCode() : 0; } @Override public String toString() { return "MailboxStoragePayload{" + "prefixedSealedAndSignedMessage=" + prefixedSealedAndSignedMessage + ", senderPubKeyForAddOperation.hashCode()=" + (senderPubKeyForAddOperation != null ? senderPubKeyForAddOperation.hashCode() : "null") + ", receiverPubKeyForRemoveOperation.hashCode()=" + (receiverPubKeyForRemoveOperation != null ? receiverPubKeyForRemoveOperation.hashCode() : "null") + '}'; } }