/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.alert;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.bitsquare.app.AppOptionKeys;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.messaging.SendMailboxMessageListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.SignatureException;
import static org.bitcoinj.core.Utils.HEX;
public class PrivateNotificationManager {
private static final Logger log = LoggerFactory.getLogger(PrivateNotificationManager.class);
private final P2PService p2PService;
private final KeyRing keyRing;
private final ObjectProperty<PrivateNotification> privateNotificationMessageProperty = new SimpleObjectProperty<>();
// Pub key for developer global privateNotification message
private static final String pubKeyAsHex = "02ba7c5de295adfe57b60029f3637a2c6b1d0e969a8aaefb9e0ddc3a7963f26925";
private ECKey privateNotificationSigningKey;
private DecryptedMsgWithPubKey decryptedMsgWithPubKey;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, Initialization
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public PrivateNotificationManager(P2PService p2PService, KeyRing keyRing, @Named(AppOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
this.p2PService = p2PService;
this.keyRing = keyRing;
if (!ignoreDevMsg) {
this.p2PService.addDecryptedDirectMessageListener(this::handleMessage);
this.p2PService.addDecryptedMailboxListener(this::handleMessage);
}
}
private void handleMessage(DecryptedMsgWithPubKey decryptedMsgWithPubKey, NodeAddress senderNodeAddress) {
this.decryptedMsgWithPubKey = decryptedMsgWithPubKey;
Message message = decryptedMsgWithPubKey.message;
if (message instanceof PrivateNotificationMessage) {
PrivateNotificationMessage privateNotificationMessage = (PrivateNotificationMessage) message;
log.trace("Received privateNotificationMessage: " + privateNotificationMessage);
if (privateNotificationMessage.getSenderNodeAddress().equals(senderNodeAddress)) {
final PrivateNotification privateNotification = privateNotificationMessage.privateNotification;
if (verifySignature(privateNotification))
privateNotificationMessageProperty.set(privateNotification);
} else {
log.warn("Peer address not matching for privateNotificationMessage");
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public ReadOnlyObjectProperty<PrivateNotification> privateNotificationProperty() {
return privateNotificationMessageProperty;
}
public boolean sendPrivateNotificationMessageIfKeyIsValid(PrivateNotification privateNotification, PubKeyRing pubKeyRing, NodeAddress nodeAddress,
String privKeyString, SendMailboxMessageListener sendMailboxMessageListener) {
boolean isKeyValid = isKeyValid(privKeyString);
if (isKeyValid) {
signAndAddSignatureToPrivateNotificationMessage(privateNotification);
p2PService.sendEncryptedMailboxMessage(nodeAddress,
pubKeyRing,
new PrivateNotificationMessage(privateNotification, p2PService.getNetworkNode().getNodeAddress()),
sendMailboxMessageListener);
}
return isKeyValid;
}
public void removePrivateNotification() {
p2PService.removeEntryFromMailbox(decryptedMsgWithPubKey);
}
private boolean isKeyValid(String privKeyString) {
try {
privateNotificationSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString)));
return pubKeyAsHex.equals(Utils.HEX.encode(privateNotificationSigningKey.getPubKey()));
} catch (Throwable t) {
return false;
}
}
private void signAndAddSignatureToPrivateNotificationMessage(PrivateNotification privateNotification) {
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
String signatureAsBase64 = privateNotificationSigningKey.signMessage(privateNotificationMessageAsHex);
privateNotification.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
}
private boolean verifySignature(PrivateNotification privateNotification) {
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
try {
ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(privateNotificationMessageAsHex, privateNotification.getSignatureAsBase64());
return true;
} catch (SignatureException e) {
log.warn("verifySignature failed");
return false;
}
}
}