diff --git a/core/src/main/java/bisq/core/xmr/knaccc/monero/address/WalletAddress.java b/core/src/main/java/bisq/core/xmr/knaccc/monero/address/WalletAddress.java index 647dd4355e6..c2665b17f6f 100755 --- a/core/src/main/java/bisq/core/xmr/knaccc/monero/address/WalletAddress.java +++ b/core/src/main/java/bisq/core/xmr/knaccc/monero/address/WalletAddress.java @@ -7,6 +7,10 @@ import bisq.core.xmr.org.nem.core.crypto.ed25519.arithmetic.Ed25519Group; import bisq.core.xmr.org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement; +import com.google.common.annotations.VisibleForTesting; + +import java.util.Arrays; + import static bisq.core.xmr.knaccc.monero.address.ByteUtil.concat; import static bisq.core.xmr.knaccc.monero.address.ByteUtil.hexToBytes; import static bisq.core.xmr.knaccc.monero.address.ByteUtil.longToLittleEndianUint32ByteArray; @@ -121,7 +125,30 @@ public static String getSubaddressBase58(Scalar privateViewKey, } - public String getSubaddressBase58(String privateViewKeyHex, long accountId, long subaddressId) { + public String getSubaddressBase58(String privateViewKeyHex, long accountId, long subaddressId) throws InvalidWalletAddressException { + if (!checkPrivateViewKey(privateViewKeyHex)) { + throw new InvalidWalletAddressException("Wrong private view key for main address"); + } return getSubaddressBase58(new Scalar(privateViewKeyHex), hexToBytes(getPublicSpendKeyHex()), accountId, subaddressId); } + + @VisibleForTesting + boolean checkPrivateViewKey(String privateViewKey) { + return isPrivateKeyReduced(privateViewKey) && doesPrivateKeyResolveToPublicKey(privateViewKey, this.publicViewKeyHex); + } + + @VisibleForTesting + static boolean isPrivateKeyReduced(String privateKey) { + byte[] input = hexToBytes(privateKey); + byte[] reduced = CryptoUtil.scReduce32(input); + return Arrays.equals(input, reduced); + } + + @VisibleForTesting + static boolean doesPrivateKeyResolveToPublicKey(String privateKey, String publicKey) { + Scalar m = new Scalar(privateKey); + Ed25519GroupElement M = G.scalarMultiply(new Ed25519EncodedFieldElement(m.bytes)); + byte[] generatedPubKey = M.encode().getRaw(); + return Arrays.equals(generatedPubKey, hexToBytes(publicKey)); + } } diff --git a/core/src/test/java/knaccc/monero/address/CryptoUtilTest.java b/core/src/test/java/bisq/core/xmr/knaccc/monero/address/CryptoUtilTest.java similarity index 97% rename from core/src/test/java/knaccc/monero/address/CryptoUtilTest.java rename to core/src/test/java/bisq/core/xmr/knaccc/monero/address/CryptoUtilTest.java index f1d34cd6d06..07e82d3413e 100644 --- a/core/src/test/java/knaccc/monero/address/CryptoUtilTest.java +++ b/core/src/test/java/bisq/core/xmr/knaccc/monero/address/CryptoUtilTest.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package knaccc.monero.address; +package bisq.core.xmr.knaccc.monero.address; import bisq.core.xmr.knaccc.monero.crypto.CryptoUtil; diff --git a/core/src/test/java/knaccc/monero/address/WalletAddressTest.java b/core/src/test/java/bisq/core/xmr/knaccc/monero/address/WalletAddressTest.java similarity index 60% rename from core/src/test/java/knaccc/monero/address/WalletAddressTest.java rename to core/src/test/java/bisq/core/xmr/knaccc/monero/address/WalletAddressTest.java index 146e8ebd369..dd447cdc0d6 100644 --- a/core/src/test/java/knaccc/monero/address/WalletAddressTest.java +++ b/core/src/test/java/bisq/core/xmr/knaccc/monero/address/WalletAddressTest.java @@ -15,13 +15,13 @@ * along with Bisq. If not, see . */ -package knaccc.monero.address; - -import bisq.core.xmr.knaccc.monero.address.WalletAddress; +package bisq.core.xmr.knaccc.monero.address; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; public class WalletAddressTest { @Test @@ -30,6 +30,7 @@ public void testWalletAddress() throws WalletAddress.InvalidWalletAddressExcepti WalletAddress walletAddress = new WalletAddress(mainAddress); String privateViewKeyHex = "7b37d8922245a07244fd31855d1e705a590a9bd2881825f0542ad99cdaba090a"; + String publicViewKeyHex = "3cd5a3079e8b4cff3630ce16bfda6eebb2da86169accdb93206a92a58d586faa"; System.out.println("subaddress for account index 0, subaddress index 1: " + walletAddress.getSubaddressBase58(privateViewKeyHex, 0, 1)); @@ -46,5 +47,25 @@ public void testWalletAddress() throws WalletAddress.InvalidWalletAddressExcepti assertEquals(walletAddress.getSubaddressBase58(privateViewKeyHex, 0, 1), addr01); assertEquals(walletAddress.getSubaddressBase58(privateViewKeyHex, 1, 0), addr10); assertEquals(walletAddress.getSubaddressBase58(privateViewKeyHex, 1, 1), addr11); + + assertTrue(walletAddress.checkPrivateViewKey(privateViewKeyHex)); + assertTrue(WalletAddress.doesPrivateKeyResolveToPublicKey(privateViewKeyHex, publicViewKeyHex)); + assertFalse(WalletAddress.doesPrivateKeyResolveToPublicKey(privateViewKeyHex, privateViewKeyHex)); + + assertTrue(WalletAddress.doesPrivateKeyResolveToPublicKey( + "a82a9017a1d259c71f5392ad9091b743b86dac7a21f5e402ea0a55e5c8a6750f", + "bdc158199c8933353627d54edb4bbae547dbbde3130860d7940313210edca0a6")); + + assertTrue(WalletAddress.doesPrivateKeyResolveToPublicKey( + "dae1bceeb2563b8c376f8e0456e5fe7aa3d6291b38ace18c6ad5647424a3b104", + "d17698d07fe9edbc41552299b90a93de73bb1bd4b94b8083af0bbe3a1931e2ec")); + + assertFalse(WalletAddress.doesPrivateKeyResolveToPublicKey( + "0000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF", + "0000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF")); + + String nonReducedPrivateKey = "680bceef3ca8b2ca1a9a29283c184f6f590a9bd2881825f0542ad99cdaba091a"; + assertFalse(WalletAddress.isPrivateKeyReduced(nonReducedPrivateKey)); + assertTrue(WalletAddress.isPrivateKeyReduced(privateViewKeyHex)); } } diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/XmrForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/XmrForm.java index 7e3517d6463..b76f372ae1d 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/XmrForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/XmrForm.java @@ -251,6 +251,7 @@ public void updateFromInputs() { xmrAccountDelegate.setPrivateViewKey(privateViewKeyInputTextField.getText()); xmrAccountDelegate.setAccountIndex(accountIndex.getText()); xmrAccountDelegate.setSubAddressIndex(subAddressIndex.getText()); + subAddressTextField.getStyleClass().remove("error-text"); if (accountIndex.validate() && subAddressIndex.validate() && mainAddressTextField.validate() && privateViewKeyInputTextField.validate() @@ -258,10 +259,13 @@ public void updateFromInputs() { && privateViewKeyInputTextField.getText().length() > 0) { try { xmrAccountDelegate.createAndSetNewSubAddress(); + subAddressTextField.setText(xmrAccountDelegate.getSubAddress()); } catch (Exception ex) { - log.warn(ex.toString()); + log.warn(ex.getMessage()); + String[] parts = ex.getMessage().split(":"); + subAddressTextField.setText(parts.length > 0 ? parts[parts.length-1] : ex.getMessage()); + subAddressTextField.getStyleClass().add("error-text"); } - subAddressTextField.setText(xmrAccountDelegate.getSubAddress()); } else { xmrAccountDelegate.setSubAddress(""); subAddressTextField.setText("");