/**
* Copyright 2013 multibit.org
*
* Licensed under the MIT license (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.multibit.viewsystem.swing.action;
import com.google.bitcoin.core.*;
import com.google.bitcoin.crypto.KeyCrypterException;
import org.multibit.controller.bitcoin.BitcoinController;
import org.multibit.model.bitcoin.WalletBusyListener;
import org.multibit.utils.WhitespaceTrimmer;
import org.multibit.viewsystem.swing.MultiBitFrame;
import org.multibit.viewsystem.swing.view.panels.SignMessagePanel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.nio.CharBuffer;
import java.util.Iterator;
/**
* This {@link Action} signs a message
*/
public class SignMessageSubmitAction extends MultiBitSubmitAction implements WalletBusyListener {
private static final Logger log = LoggerFactory.getLogger(ImportPrivateKeysSubmitAction.class);
private static final long serialVersionUID = 1923333087598757765L;
private MultiBitFrame mainFrame;
private SignMessagePanel signMessagePanel;
/**
* Creates a new {@link SignMessageSubmitAction}.
*/
public SignMessageSubmitAction(BitcoinController bitcoinController, MultiBitFrame mainFrame,
SignMessagePanel signMessagePanel, ImageIcon icon) {
super(bitcoinController, "signMessageAction.text", "signMessageAction.tooltip", "signMessageAction.mnemonicKey", icon);
this.mainFrame = mainFrame;
this.signMessagePanel = signMessagePanel;
// This action is a WalletBusyListener.
super.bitcoinController.registerWalletBusyListener(this);
walletBusyChange(super.bitcoinController.getModel().getActivePerWalletModelData().isBusy());
}
/**
* Verify the message.
*/
@Override
public void actionPerformed(ActionEvent event) {
if (abort()) {
return;
}
if (signMessagePanel == null) {
return;
}
String addressText = null;
if (signMessagePanel.getAddressTextArea() != null) {
addressText = signMessagePanel.getAddressTextArea().getText();
if (addressText != null) {
addressText = WhitespaceTrimmer.trim(addressText);
}
}
String messageText = null;
if (signMessagePanel.getMessageTextArea() != null) {
messageText = signMessagePanel.getMessageTextArea().getText();
}
CharSequence walletPassword = null;
if (signMessagePanel.getWalletPasswordField() != null) {
walletPassword = CharBuffer.wrap(signMessagePanel.getWalletPasswordField().getPassword());
if (bitcoinController.getModel().getActiveWallet().isEncrypted()) {
if (walletPassword.length() == 0) {
signMessagePanel.setMessageText1(controller.getLocaliser().getString(
"showExportPrivateKeysAction.youMustEnterTheWalletPassword"));
signMessagePanel.setMessageText2(" ");
return;
}
if (!bitcoinController.getModel().getActiveWallet().checkPassword(walletPassword)) {
// The password supplied is incorrect.
signMessagePanel.setMessageText1(controller.getLocaliser().getString(
"createNewReceivingAddressSubmitAction.passwordIsIncorrect"));
signMessagePanel.setMessageText2(" ");
return;
}
}
}
log.debug("addressText = '" + addressText + "'");
log.debug("messageText = '" + messageText + "'");
if (addressText == null || "".equals(addressText)) {
signMessagePanel.setMessageText1(controller.getLocaliser().getString("signMessageAction.noAddress"));
signMessagePanel.setMessageText2(" ");
return;
}
if (messageText == null || "".equals(messageText.trim())) {
signMessagePanel.setMessageText1(controller.getLocaliser().getString("signMessageAction.noMessage"));
signMessagePanel.setMessageText2(" ");
return;
}
try {
Address signingAddress = new Address(bitcoinController.getModel().getNetworkParameters(), addressText);
// Find the ECKey corresponding to the signing address.
Wallet activeWallet = bitcoinController.getModel().getActiveWallet();
Iterable<ECKey> keychain = activeWallet.getKeys();
Iterator<ECKey> iterator = keychain.iterator();
ECKey signingKey = null;
while (iterator.hasNext()) {
ECKey ecKey = iterator.next();
if (ecKey.toAddress(bitcoinController.getModel().getNetworkParameters()).equals(signingAddress)) {
signingKey = ecKey;
break;
}
}
if (signingKey == null) {
// No signing key found.
signMessagePanel.setMessageText1(controller.getLocaliser().getString("signMessageAction.noSigningKey", new String[]{addressText}));
signMessagePanel.setMessageText2(" ");
} else {
KeyParameter aesKey = null;
if (signingKey.isEncrypted()) {
aesKey = signingKey.getKeyCrypter().deriveKey(walletPassword);
signingKey = signingKey.decrypt(signingKey.getKeyCrypter(), aesKey);
}
String signatureBase64 = signingKey.signMessage(messageText, aesKey);
if (signMessagePanel.getSignatureTextArea() != null) {
signMessagePanel.getSignatureTextArea().setText(signatureBase64);
signMessagePanel.setMessageText1(controller.getLocaliser().getString("signMessageAction.success"));
signMessagePanel.setMessageText2(" ");
}
}
} catch (KeyCrypterException | AddressFormatException e) {
logError(e);
}
}
private void logError(Exception e) {
e.printStackTrace();
signMessagePanel.setMessageText1(controller.getLocaliser().getString("signMessageAction.error"));
signMessagePanel.setMessageText2(controller.getLocaliser().getString("deleteWalletConfirmDialog.walletDeleteError2",
new String[]{e.getClass().getCanonicalName() + " " + e.getMessage()}));
}
@Override
public void walletBusyChange(boolean newWalletIsBusy) {
// Update the enable status of the action to match the wallet busy
// status.
if (super.bitcoinController.getModel().getActivePerWalletModelData().isBusy()) {
// Wallet is busy with another operation that may change the private
// keys - Action is disabled.
putValue(
SHORT_DESCRIPTION,
controller.getLocaliser().getString(
"multiBitSubmitAction.walletIsBusy",
new Object[]{controller.getLocaliser().getString(
this.bitcoinController.getModel().getActivePerWalletModelData().getBusyTaskKey())}));
setEnabled(false);
} else {
// Enable
putValue(SHORT_DESCRIPTION, controller.getLocaliser().getString("signMessageAction.tooltip"));
setEnabled(true);
}
}
}