add bip322 message signing for singlesig addresses including p2tr

This commit is contained in:
Craig Raw 2023-07-04 08:51:38 +02:00
parent 296223130e
commit 9576581d89
3 changed files with 68 additions and 34 deletions

2
drongo

@ -1 +1 @@
Subproject commit 4341973acd9577def1d8fee486718bf5eca9b771 Subproject commit f47d5de3922a88ae385e586dcc3947f05c2ae803

View file

@ -376,7 +376,7 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
private static boolean canSignMessage(WalletNode walletNode) { private static boolean canSignMessage(WalletNode walletNode) {
Wallet wallet = walletNode.getWallet(); Wallet wallet = walletNode.getWallet();
return wallet.getKeystores().size() == 1 && wallet.getScriptType() != ScriptType.P2TR && return wallet.getKeystores().size() == 1 &&
(wallet.getKeystores().get(0).hasPrivateKey() || wallet.getKeystores().get(0).getSource() == KeystoreSource.HW_USB || wallet.getKeystores().get(0).getWalletModel().isCard()) && (wallet.getKeystores().get(0).hasPrivateKey() || wallet.getKeystores().get(0).getSource() == KeystoreSource.HW_USB || wallet.getKeystores().get(0).getWalletModel().isCard()) &&
(!wallet.isBip47() || walletNode.getKeyPurpose() == KeyPurpose.RECEIVE); (!wallet.isBip47() || walletNode.getKeyPurpose() == KeyPurpose.RECEIVE);
} }

View file

@ -5,6 +5,7 @@ import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.SecureString; import com.sparrowwallet.drongo.SecureString;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.address.InvalidAddressException; import com.sparrowwallet.drongo.address.InvalidAddressException;
import com.sparrowwallet.drongo.crypto.Bip322;
import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.protocol.ScriptType;
@ -46,9 +47,9 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
private final ToggleGroup formatGroup; private final ToggleGroup formatGroup;
private final ToggleButton formatTrezor; private final ToggleButton formatTrezor;
private final ToggleButton formatElectrum; private final ToggleButton formatElectrum;
private final ToggleButton formatBip322;
private final Wallet wallet; private final Wallet wallet;
private WalletNode walletNode; private WalletNode walletNode;
private boolean electrumSignatureFormat;
private boolean closed; private boolean closed;
/** /**
@ -125,7 +126,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
address = new TextField(); address = new TextField();
address.getStyleClass().add("id"); address.getStyleClass().add("id");
address.setEditable(walletNode == null); address.setEditable(walletNode == null);
address.setTooltip(new Tooltip("Only Legacy (P2PKH), Nested Segwit (P2SH-P2WPKH) and Native Segwit (P2WPKH) singlesig addresses can sign")); address.setTooltip(new Tooltip("Only singlesig addresses can sign"));
addressField.getInputs().add(address); addressField.getInputs().add(address);
if(walletNode != null) { if(walletNode != null) {
@ -136,16 +137,16 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
messageField.setText("Message:"); messageField.setText("Message:");
message = new TextArea(); message = new TextArea();
message.setWrapText(true); message.setWrapText(true);
message.setPrefRowCount(10); message.setPrefRowCount(8);
message.setStyle("-fx-pref-height: 180px"); message.setStyle("-fx-pref-height: 160px");
messageField.getInputs().add(message); messageField.getInputs().add(message);
Field signatureField = new Field(); Field signatureField = new Field();
signatureField.setText("Signature:"); signatureField.setText("Signature:");
signature = new TextArea(); signature = new TextArea();
signature.getStyleClass().add("id"); signature.getStyleClass().add("id");
signature.setPrefRowCount(2); signature.setPrefRowCount(4);
signature.setStyle("-fx-pref-height: 60px"); signature.setStyle("-fx-pref-height: 80px");
signature.setWrapText(true); signature.setWrapText(true);
signatureField.getInputs().add(signature); signatureField.getInputs().add(signature);
@ -154,16 +155,11 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
formatGroup = new ToggleGroup(); formatGroup = new ToggleGroup();
formatElectrum = new ToggleButton("Standard (Electrum)"); formatElectrum = new ToggleButton("Standard (Electrum)");
formatTrezor = new ToggleButton("BIP137 (Trezor)"); formatTrezor = new ToggleButton("BIP137 (Trezor)");
SegmentedButton formatButtons = new SegmentedButton(formatElectrum, formatTrezor); formatBip322 = new ToggleButton("BIP322 (Simple)");
SegmentedButton formatButtons = new SegmentedButton(formatElectrum, formatTrezor, formatBip322);
formatButtons.setToggleGroup(formatGroup); formatButtons.setToggleGroup(formatGroup);
formatField.getInputs().add(formatButtons); formatField.getInputs().add(formatButtons);
formatGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
electrumSignatureFormat = (newValue == formatElectrum);
});
formatButtons.setDisable(wallet != null && walletNode != null && wallet.getScriptType() == ScriptType.P2PKH);
fieldset.getChildren().addAll(addressField, messageField, signatureField, formatField); fieldset.getChildren().addAll(addressField, messageField, signatureField, formatField);
form.getChildren().add(fieldset); form.getChildren().add(fieldset);
dialogPane.setContent(form); dialogPane.setContent(form);
@ -223,10 +219,13 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
signButton.setDisable(!valid || (wallet == null)); signButton.setDisable(!valid || (wallet == null));
verifyButton.setDisable(!valid); verifyButton.setDisable(!valid);
if(valid && wallet != null) { if(valid) {
try { try {
Address address = getAddress(); Address address = getAddress();
setWalletNodeFromAddress(wallet, address); setFormatFromScriptType(address.getScriptType());
if(wallet != null) {
setWalletNodeFromAddress(wallet, address);
}
} catch(InvalidAddressException e) { } catch(InvalidAddressException e) {
//can't happen //can't happen
} }
@ -258,7 +257,11 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
message.requestFocus(); message.requestFocus();
} }
formatGroup.selectToggle(formatElectrum); if(wallet != null && walletNode != null) {
setFormatFromScriptType(wallet.getScriptType());
} else {
formatGroup.selectToggle(formatElectrum);
}
}); });
} }
@ -279,20 +282,10 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
return signature.getText(); return signature.getText();
} }
/**
* Use the Electrum signing format, which uses the non-segwit compressed signing parameters for both segwit types (p2sh-p2wpkh and p2wpkh)
*
* @param electrumSignatureFormat
*/
public void setElectrumSignatureFormat(boolean electrumSignatureFormat) {
formatGroup.selectToggle(electrumSignatureFormat ? formatElectrum : formatTrezor);
this.electrumSignatureFormat = electrumSignatureFormat;
}
private boolean isValidAddress() { private boolean isValidAddress() {
try { try {
Address address = getAddress(); Address address = getAddress();
return address.getScriptType() != ScriptType.P2TR && (address.getScriptType().isAllowed(PolicyType.SINGLE) || address.getScriptType() == ScriptType.P2SH); return address.getScriptType().isAllowed(PolicyType.SINGLE) || address.getScriptType() == ScriptType.P2SH;
} catch (InvalidAddressException e) { } catch (InvalidAddressException e) {
return false; return false;
} }
@ -302,6 +295,25 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
walletNode = wallet.getWalletAddresses().get(address); walletNode = wallet.getWalletAddresses().get(address);
} }
private void setFormatFromScriptType(ScriptType scriptType) {
formatElectrum.setDisable(scriptType == ScriptType.P2TR);
formatTrezor.setDisable(scriptType == ScriptType.P2TR || scriptType == ScriptType.P2PKH);
formatBip322.setDisable(scriptType != ScriptType.P2WPKH && scriptType != ScriptType.P2TR);
if(scriptType == ScriptType.P2TR) {
formatGroup.selectToggle(formatBip322);
} else if(formatGroup.getSelectedToggle() == null || scriptType == ScriptType.P2PKH || (scriptType != ScriptType.P2WPKH && formatBip322.isSelected())) {
formatGroup.selectToggle(formatElectrum);
}
}
private boolean isBip322() {
return formatBip322.isSelected();
}
private boolean isElectrumSignatureFormat() {
return formatElectrum.isSelected();
}
private void signMessage() { private void signMessage() {
if(walletNode == null) { if(walletNode == null) {
AppServices.showErrorDialog("Address not in wallet", "The provided address is not present in the currently selected wallet."); AppServices.showErrorDialog("Address not in wallet", "The provided address is not present in the currently selected wallet.");
@ -325,8 +337,14 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
try { try {
Keystore keystore = decryptedWallet.getKeystores().get(0); Keystore keystore = decryptedWallet.getKeystores().get(0);
ECKey privKey = keystore.getKey(walletNode); ECKey privKey = keystore.getKey(walletNode);
ScriptType scriptType = electrumSignatureFormat ? ScriptType.P2PKH : decryptedWallet.getScriptType(); String signatureText;
String signatureText = privKey.signMessage(message.getText().trim(), scriptType); if(isBip322()) {
ScriptType scriptType = decryptedWallet.getScriptType();
signatureText = Bip322.signMessageBip322(scriptType, message.getText().trim(), privKey);
} else {
ScriptType scriptType = isElectrumSignatureFormat() ? ScriptType.P2PKH : decryptedWallet.getScriptType();
signatureText = privKey.signMessage(message.getText().trim(), scriptType);
}
signature.clear(); signature.clear();
signature.appendText(signatureText); signature.appendText(signatureText);
privKey.clear(); privKey.clear();
@ -353,21 +371,37 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
//http://www.secg.org/download/aid-780/sec1-v2.pdf section 4.1.6 //http://www.secg.org/download/aid-780/sec1-v2.pdf section 4.1.6
boolean verified = false; boolean verified = false;
try { try {
ECKey signedMessageKey = ECKey.signedMessageToKey(message.getText().trim(), signature.getText().trim(), false); ECKey signedMessageKey = ECKey.signedMessageToKey(message.getText().trim(), signature.getText().trim(), true);
verified = verifyMessage(signedMessageKey); verified = verifyMessage(signedMessageKey);
if(verified) {
formatGroup.selectToggle(formatElectrum);
}
} catch(SignatureException e) { } catch(SignatureException e) {
//ignore //ignore
} }
if(!verified) { if(!verified) {
try { try {
ECKey electrumSignedMessageKey = ECKey.signedMessageToKey(message.getText(), signature.getText(), true); ECKey electrumSignedMessageKey = ECKey.signedMessageToKey(message.getText(), signature.getText(), false);
verified = verifyMessage(electrumSignedMessageKey); verified = verifyMessage(electrumSignedMessageKey);
if(verified) {
formatGroup.selectToggle(formatTrezor);
}
} catch(SignatureException e) { } catch(SignatureException e) {
//ignore //ignore
} }
} }
if(!verified) {
try {
Bip322.verifyMessageBip322(getAddress().getScriptType(), getAddress(), message.getText().trim(), signature.getText().trim());
verified = true;
formatGroup.selectToggle(formatBip322);
} catch(Exception e) {
//ignore
}
}
if(verified) { if(verified) {
AppServices.showSuccessDialog("Verification Succeeded", "The signature verified against the message."); AppServices.showSuccessDialog("Verification Succeeded", "The signature verified against the message.");
} else { } else {
@ -415,7 +449,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
}); });
decryptWalletService.setOnFailed(workerStateEvent -> { decryptWalletService.setOnFailed(workerStateEvent -> {
EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.END, "Failed")); EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.END, "Failed"));
AppServices.showErrorDialog("Incorrect Password", decryptWalletService.getException().getMessage()); AppServices.showErrorDialog("Incorrect Password", "The password was incorrect.");
}); });
EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.START, "Decrypting wallet...")); EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.START, "Decrypting wallet..."));
decryptWalletService.start(); decryptWalletService.start();