diff --git a/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java b/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java index 00fbaf6b61..fcb263838e 100644 --- a/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java +++ b/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java @@ -22,6 +22,7 @@ import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.RegTestHost; import io.bitsquare.btc.UserAgent; import io.bitsquare.btc.WalletService; +import io.bitsquare.crypto.KeyStorage; import io.bitsquare.p2p.BootstrapNodes; import io.bitsquare.p2p.tomp2p.TomP2PModule; import io.bitsquare.storage.Storage; @@ -166,6 +167,7 @@ public class BitsquareEnvironment extends StandardEnvironment { setProperty(WalletService.PREFIX_KEY, appName); setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString()); + setProperty(KeyStorage.DIR_KEY, Paths.get(appDataDir, "keys").toString()); setProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY, bootstrapNodePort); } diff --git a/core/src/main/java/io/bitsquare/arbitration/tomp2p/TomP2PArbitratorService.java b/core/src/main/java/io/bitsquare/arbitration/tomp2p/TomP2PArbitratorService.java index 280a69fcd7..6096ccfbbc 100644 --- a/core/src/main/java/io/bitsquare/arbitration/tomp2p/TomP2PArbitratorService.java +++ b/core/src/main/java/io/bitsquare/arbitration/tomp2p/TomP2PArbitratorService.java @@ -21,9 +21,9 @@ import io.bitsquare.arbitration.Arbitrator; import io.bitsquare.arbitration.ArbitratorService; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; +import io.bitsquare.crypto.KeyRing; import io.bitsquare.p2p.tomp2p.TomP2PDHTService; import io.bitsquare.p2p.tomp2p.TomP2PNode; -import io.bitsquare.user.User; import java.io.IOException; @@ -53,8 +53,8 @@ public class TomP2PArbitratorService extends TomP2PDHTService implements Arbitra private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); @Inject - public TomP2PArbitratorService(TomP2PNode tomP2PNode, User user) { - super(tomP2PNode, user); + public TomP2PArbitratorService(TomP2PNode tomP2PNode, KeyRing keyRing) { + super(tomP2PNode, keyRing); } public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java index 0ae63bfe4d..df3eab5086 100644 --- a/core/src/main/java/io/bitsquare/btc/WalletService.java +++ b/core/src/main/java/io/bitsquare/btc/WalletService.java @@ -20,7 +20,7 @@ package io.bitsquare.btc; import io.bitsquare.btc.listeners.AddressConfidenceListener; import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.TxConfidenceListener; -import io.bitsquare.crypto.SignatureService; +import io.bitsquare.crypto.CryptoService; import org.bitcoinj.core.AbstractWalletEventListener; import org.bitcoinj.core.Address; @@ -95,7 +95,7 @@ public class WalletService { private final TradeWalletService tradeWalletService; private final AddressEntryList addressEntryList; private final NetworkParameters params; - private final SignatureService signatureService; + private final CryptoService cryptoService; private final File walletDir; private final String walletPrefix; private final UserAgent userAgent; @@ -111,14 +111,14 @@ public class WalletService { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, SignatureService signatureService, + public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, CryptoService cryptoService, TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent, @Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) { this.regTestHost = regTestHost; this.tradeWalletService = tradeWalletService; this.addressEntryList = addressEntryList; this.params = bitcoinNetwork.getParameters(); - this.signatureService = signatureService; + this.cryptoService = cryptoService; this.walletDir = walletDir; this.walletPrefix = walletPrefix; this.userAgent = userAgent; @@ -208,7 +208,8 @@ public class WalletService { }, Threading.USER_THREAD); walletAppKit.startAsync(); - return status.mergeWith(downloadProgress).timeout(30, TimeUnit.SECONDS); + return status.timeout(30, TimeUnit.SECONDS); + //return status.mergeWith(downloadProgress).timeout(30, TimeUnit.SECONDS); } private void initWallet() { @@ -457,7 +458,7 @@ public class WalletService { Transaction tx = new Transaction(params); - byte[] data = signatureService.digestMessageWithSignature(getRegistrationAddressEntry().getKeyPair(), stringifiedFiatAccounts); + byte[] data = cryptoService.digestMessageWithSignature(getRegistrationAddressEntry().getKeyPair(), stringifiedFiatAccounts); tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build()); // We don't take a fee at the moment diff --git a/core/src/main/java/io/bitsquare/crypto/CryptoModule.java b/core/src/main/java/io/bitsquare/crypto/CryptoModule.java index a0d68dc24b..24d8a3f53c 100644 --- a/core/src/main/java/io/bitsquare/crypto/CryptoModule.java +++ b/core/src/main/java/io/bitsquare/crypto/CryptoModule.java @@ -31,8 +31,6 @@ public class CryptoModule extends BitsquareModule { @Override protected void configure() { - bind(SignatureService.class).in(Singleton.class); - bind(HashService.class).in(Singleton.class); - bind(EncryptionService.class).in(Singleton.class); + bind(CryptoService.class).in(Singleton.class); } } diff --git a/core/src/main/java/io/bitsquare/crypto/CryptoService.java b/core/src/main/java/io/bitsquare/crypto/CryptoService.java new file mode 100644 index 0000000000..e6254dd36e --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/CryptoService.java @@ -0,0 +1,217 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import io.bitsquare.p2p.Message; + +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Utils; + +import com.google.common.base.Charsets; + +import java.io.IOException; + +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.SignedObject; + +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.spongycastle.util.encoders.Base64; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SealedObject; +import javax.crypto.SecretKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class CryptoService { + private static final Logger log = LoggerFactory.getLogger(CryptoService.class); + public static final String DHT_SIGN_KEY_ALGO = "DSA"; + public static final String MSG_SIGN_KEY_ALGO = "DSA"; + public static final String MSG_ENCR_KEY_ALGO = "RSA"; + + private static final String SYM_ENCR_KEY_ALGO = "AES"; + private static final String SYM_CIPHER = "AES"; + private static final String ASYM_CIPHER = "RSA"; //RSA/ECB/PKCS1Padding + private static final String MSG_SIGN_ALGO = "SHA1withDSA"; + + public static KeyPair generateDhtSignatureKeyPair() throws NoSuchAlgorithmException { + long ts = System.currentTimeMillis(); + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(DHT_SIGN_KEY_ALGO); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + log.debug("Generate dhtSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts); + return keyPair; + } + + public static KeyPair generateMsgSignatureKeyPair() throws NoSuchAlgorithmException { + long ts = System.currentTimeMillis(); + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_SIGN_KEY_ALGO); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + log.debug("Generate dhtSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts); + return keyPair; + } + + public static KeyPair generateMsgEncryptionKeyPair() throws NoSuchAlgorithmException { + long ts = System.currentTimeMillis(); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_ENCR_KEY_ALGO); + keyPairGenerator.initialize(2048); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + log.debug("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts); + return keyPair; + } + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + private KeyRing keyRing; + + @Inject + public CryptoService(KeyRing keyRing) { + this.keyRing = keyRing; + } + + public SealedAndSignedMessage encryptAndSignMessage(PubKeyRing pubKeyRing, Message message) throws CryptoException { + long ts = System.currentTimeMillis(); + + try { + // Create symmetric key + KeyGenerator keyGenerator = KeyGenerator.getInstance(SYM_ENCR_KEY_ALGO); + keyGenerator.init(128); + SecretKey secretKey = keyGenerator.generateKey(); + + // Encrypt secretKey with peers pubKey using SealedObject + Cipher cipherAsym = Cipher.getInstance(ASYM_CIPHER); + cipherAsym.init(Cipher.ENCRYPT_MODE, pubKeyRing.getMsgEncryptionPubKey()); + SealedObject sealedSecretKey = new SealedObject(secretKey, cipherAsym); + + // Sign (hash of) message and pack it into SignedObject + SignedObject signedMessage = new SignedObject(message, keyRing.getMsgSignatureKeyPair().getPrivate(), Signature.getInstance(MSG_SIGN_ALGO)); + + // // Encrypt signedMessage with secretKey using SealedObject + Cipher cipherSym = Cipher.getInstance(SYM_CIPHER); + cipherSym.init(Cipher.ENCRYPT_MODE, secretKey); + SealedObject sealedMessage = new SealedObject(signedMessage, cipherSym); + + SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(sealedSecretKey, + sealedMessage, + keyRing.getMsgSignatureKeyPair().getPublic() + ); + log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts); + return sealedAndSignedMessage; + } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException + | IllegalBlockSizeException | IOException | SignatureException e) { + throw new CryptoException(e); + + } + } + + public MessageWithPubKey decryptAndVerifyMessage(SealedAndSignedMessage sealedAndSignedMessage) throws CryptoException { + long ts = System.currentTimeMillis(); + try { + SealedObject sealedSecretKey = sealedAndSignedMessage.getSealedSecretKey(); + SealedObject sealedMessage = sealedAndSignedMessage.getSealedMessage(); + PublicKey signaturePubKey = sealedAndSignedMessage.getSignaturePubKey(); + + // Decrypt secretKey with my privKey + Cipher cipherAsym = Cipher.getInstance(ASYM_CIPHER); + cipherAsym.init(Cipher.DECRYPT_MODE, keyRing.getMsgEncryptionKeyPair().getPrivate()); + Object secretKeyObject = sealedSecretKey.getObject(cipherAsym); + assert secretKeyObject instanceof SecretKey; + SecretKey secretKey = (SecretKey) secretKeyObject; + + // Decrypt signedMessage with secretKey + Cipher cipherSym = Cipher.getInstance(SYM_CIPHER); + cipherSym.init(Cipher.DECRYPT_MODE, secretKey); + Object signedMessageObject = sealedMessage.getObject(cipherSym); + assert signedMessageObject instanceof SignedObject; + SignedObject signedMessage = (SignedObject) signedMessageObject; + + // Verify message with peers pubKey + if (signedMessage.verify(signaturePubKey, Signature.getInstance(MSG_SIGN_ALGO))) { + // Get message + Object messageObject = signedMessage.getObject(); + assert messageObject instanceof Message; + log.debug("Decryption needed {} ms", System.currentTimeMillis() - ts); + return new MessageWithPubKey((Message) messageObject, signaturePubKey); + } + else { + throw new CryptoException("Signature is not valid"); + } + } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | + ClassNotFoundException | IllegalBlockSizeException | IOException | SignatureException e) { + throw new CryptoException(e); + } + } + + + public String signMessage(ECKey key, Sha256Hash hash) { + ECKey.ECDSASignature sig = key.sign(hash, null); + // Now we have to work backwards to figure out the recId needed to recover the signature. + int recId = -1; + for (int i = 0; i < 4; i++) { + ECKey k = ECKey.recoverFromSignature(i, sig, hash, key.isCompressed()); + if (k != null && k.getPubKeyPoint().equals(key.getPubKeyPoint())) { + recId = i; + break; + } + } + if (recId == -1) + throw new RuntimeException("Could not construct a recoverable key. This should never happen."); + int headerByte = recId + 27 + (key.isCompressed() ? 4 : 0); + byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S + sigData[0] = (byte) headerByte; + System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32); + System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32); + return new String(Base64.encode(sigData), Charsets.UTF_8); + } + + + public byte[] digestMessageWithSignature(ECKey key, String message) { + String signedMessage = signMessage(key, message); + return Utils.sha256hash160(message.concat(signedMessage).getBytes(Charsets.UTF_8)); + } + + public String signMessage(ECKey key, String message) { + byte[] data = Utils.formatMessageForSigning(message); + Sha256Hash hash = Sha256Hash.hashTwice(data); + return signMessage(key, hash); + } + + public Sha256Hash hash(String message) { + byte[] data = Utils.formatMessageForSigning(message); + return Sha256Hash.hashTwice(data); + } +} + diff --git a/core/src/main/java/io/bitsquare/crypto/EncryptionService.java b/core/src/main/java/io/bitsquare/crypto/EncryptionService.java deleted file mode 100644 index e0dbaf5484..0000000000 --- a/core/src/main/java/io/bitsquare/crypto/EncryptionService.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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 . - */ - -package io.bitsquare.crypto; - -import io.bitsquare.app.Version; -import io.bitsquare.p2p.Message; -import io.bitsquare.util.Utilities; - -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Utils; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Security; - -import java.util.Arrays; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -public class EncryptionService { - private static final Logger log = LoggerFactory.getLogger(EncryptionService.class); - private static final String ALGO_SYM = "AES"; - private static final String CIPHER_SYM = "AES";// AES/CBC/PKCS5Padding - private static final String ALGO_ASYM = "RSA"; - private static final String CIPHER_ASYM = "RSA/ECB/PKCS1Padding"; - - private static final int MAX_SIZE = 10000; // in bytes - - static { - Security.addProvider(new BouncyCastleProvider()); - } - - private SignatureService signatureService; - - @Inject - public EncryptionService(SignatureService signatureService) { - this.signatureService = signatureService; - } - - public KeyPair getGeneratedDSAKeyPair() throws NoSuchAlgorithmException { - long ts = System.currentTimeMillis(); - final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); - keyPairGenerator.initialize(1024); - KeyPair keyPair = keyPairGenerator.genKeyPair(); - log.debug("getGeneratedDSAKeyPair needed {} ms", System.currentTimeMillis() - ts); - return keyPair; - } - - public KeyPair getGeneratedRSAKeyPair() throws NoSuchAlgorithmException { - long ts = System.currentTimeMillis(); - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGO_ASYM); - keyPairGenerator.initialize(1024); - KeyPair keyPair = keyPairGenerator.genKeyPair(); - log.debug("getGeneratedRSAKeyPair needed {} ms", System.currentTimeMillis() - ts); - return keyPair; - } - - public byte[] encryptObject(PublicKey publicKey, ECKey signatureKeyPair, Object object) throws CryptoException { - return encryptBytes(publicKey, signatureKeyPair, Utilities.objectToBytArray(object)); - } - - public byte[] encryptMessage(PublicKey publicKey, ECKey signatureKeyPair, Message object) throws CryptoException { - return encryptBytes(publicKey, signatureKeyPair, Utilities.objectToBytArray(object)); - } - - public byte[] encryptBytes(PublicKey publicKey, ECKey signatureKeyPair, byte[] plainText) throws CryptoException { - long ts = System.currentTimeMillis(); - - if (plainText.length == 0) - throw new CryptoException("Input data is null."); - - try { - // Create symmetric key - KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGO_SYM); - keyGenerator.init(128); - SecretKey oneTimeKey = keyGenerator.generateKey(); - - // Encrypt secretKey with asymmetric key (16 bytes) - Cipher cipherAsym = Cipher.getInstance(CIPHER_ASYM); - cipherAsym.init(Cipher.ENCRYPT_MODE, publicKey); - - byte[] encryptedOneTimeKey = cipherAsym.doFinal(oneTimeKey.getEncoded()); - - // Create signature of plainText (65 bytes) - ECKey.ECDSASignature signature = signatureService.signBytes(signatureKeyPair, plainText).toCanonicalised(); - byte[] sig = signature.encodeToDER(); // has 70-72 bytes ;-( - byte[] sigLength = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(sig.length).array(); - - // Encrypt plainText with symmetric key - Cipher cipherSym = Cipher.getInstance(CIPHER_SYM); - cipherSym.init(Cipher.ENCRYPT_MODE, oneTimeKey); - byte[] cipherText = cipherSym.doFinal(plainText); - - // payload - byte[] payload = Utilities.concatByteArrays(sigLength, sig, signatureKeyPair.getPubKey(), encryptedOneTimeKey, cipherText); - byte[] payloadLength = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(payload.length).array(); - - // Checksum - byte[] checksum = Utils.sha256hash160(Utilities.concatByteArrays(payloadLength, payload)); - - // 1 byte version | 20 byte checksum | 4 byte payload length | n bytes payload - // payload consist of: 4 byte sigLength | sigLength(70-72) bytes for sig | 33 bytes for signaturePubKey | 128 bytes for encryptedOneTimeKey | - // remaining - // bytes for cipherText - byte[] result = Utilities.concatByteArrays(Version.NETWORK_PROTOCOL_VERSION, checksum, payloadLength, payload); - log.debug("result.length " + result.length); - log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts); - return result; - } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) { - throw new CryptoException(e); - } - } - - public T decryptToMessage(PrivateKey privateKey, byte[] data) throws IllegalBlockSizeException, InvalidKeyException, - BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, CryptoException { - return Utilities.byteArrayToObject(decryptBytes(privateKey, data)); - } - - public byte[] decryptBytes(PrivateKey privateKey, byte[] data) throws CryptoException { - long ts = System.currentTimeMillis(); - - if (data.length < 25) - throw new CryptoException("The data is shorter as the min. overhead length."); - else if (data.length > MAX_SIZE) - throw new CryptoException("The data exceeds the max. size."); - - // 1 byte version | 20 byte checksum | 4 byte payload length | n bytes payload consisting of sig, encryptedOneTimeKey, cipherText - byte[] version = new byte[1]; - int cursor = 0; - System.arraycopy(data, cursor, version, 0, version.length); - - if (!Arrays.equals(version, Version.NETWORK_PROTOCOL_VERSION)) - throw new CryptoException("Incorrect version."); - - byte[] checksum = new byte[20]; - cursor += version.length; - System.arraycopy(data, cursor, checksum, 0, checksum.length); - - byte[] payloadLength = new byte[4]; - cursor += checksum.length; - System.arraycopy(data, cursor, payloadLength, 0, payloadLength.length); - int payloadLengthInt = ByteBuffer.wrap(payloadLength).order(ByteOrder.LITTLE_ENDIAN).getInt(); - log.debug("encode payloadLengthInt " + payloadLengthInt); - if (payloadLengthInt < 0) - throw new CryptoException("Payload length cannot be negative."); - else if (payloadLengthInt > data.length - 25) - throw new CryptoException("Payload length cannot be larger then data excluding overhead."); - - byte[] payload = new byte[payloadLengthInt]; - cursor += payloadLength.length; - System.arraycopy(data, cursor, payload, 0, payload.length); - - - byte[] sigLength = new byte[4]; - // cursor stays the same - System.arraycopy(data, cursor, sigLength, 0, sigLength.length); - int sigLengthInt = ByteBuffer.wrap(sigLength).order(ByteOrder.LITTLE_ENDIAN).getInt(); - - byte[] sig = new byte[sigLengthInt]; - cursor += sigLength.length; - System.arraycopy(data, cursor, sig, 0, sig.length); - - byte[] signaturePubKey = new byte[33]; - cursor += sig.length; - System.arraycopy(data, cursor, signaturePubKey, 0, signaturePubKey.length); - - byte[] encryptedOneTimeKey = new byte[128]; - cursor += signaturePubKey.length; - System.arraycopy(data, cursor, encryptedOneTimeKey, 0, encryptedOneTimeKey.length); - - byte[] cipherText = new byte[payloadLengthInt - (sigLength.length + sig.length + signaturePubKey.length + encryptedOneTimeKey.length)]; - cursor += encryptedOneTimeKey.length; - System.arraycopy(data, cursor, cipherText, 0, cipherText.length); - - // Checksum - byte[] controlChecksum = Utils.sha256hash160(Utilities.concatByteArrays(payloadLength, payload)); - if (!Arrays.equals(checksum, controlChecksum)) - throw new CryptoException("The checksum is invalid."); - - try { - // Decrypt oneTimeKey key with asymmetric key - Cipher cipherAsym = Cipher.getInstance(CIPHER_ASYM); - cipherAsym.init(Cipher.DECRYPT_MODE, privateKey); - byte[] oneTimeKey = cipherAsym.doFinal(encryptedOneTimeKey); - - // Decrypt payload with symmetric key - Key key = new SecretKeySpec(oneTimeKey, ALGO_SYM); - Cipher cipherSym = Cipher.getInstance(CIPHER_SYM); - cipherSym.init(Cipher.DECRYPT_MODE, key); - byte[] plainText = cipherSym.doFinal(cipherText); - - // Verify signature - boolean verified = signatureService.verify(ECKey.fromPublicOnly(signaturePubKey).getPubKey(), plainText, ECKey.ECDSASignature.decodeFromDER(sig)); - if (!verified) - throw new CryptoException("Signature is not valid"); - - log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts); - return plainText; - } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) { - throw new CryptoException(e); - } - } - -} - diff --git a/core/src/main/java/io/bitsquare/crypto/KeyRing.java b/core/src/main/java/io/bitsquare/crypto/KeyRing.java new file mode 100644 index 0000000000..0ce1e159f5 --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/KeyRing.java @@ -0,0 +1,103 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; + +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KeyRing { + private static final Logger log = LoggerFactory.getLogger(KeyRing.class); + + // Public key is used as ID in DHT network. Used for data protection mechanism in TomP2P DHT + private KeyPair dhtSignatureKeyPair; + // Used for signing messages sent over the wire + private KeyPair msgSignatureKeyPair; + // Used for encrypting messages sent over the wire (hybrid encryption scheme is used, so it is used only to encrypt a symmetric session key) + private KeyPair msgEncryptionKeyPair; + + private PubKeyRing pubKeyRing; + private final KeyStorage keyStorage; + + @Inject + public KeyRing(KeyStorage keyStorage) throws CryptoException { + this.keyStorage = keyStorage; + + init(keyStorage); + } + + // consider extra thread for loading (takes about 264 ms at first startup, then load only takes nearly no time) + public void init(KeyStorage keyStorage) throws CryptoException { + if (keyStorage.allKeyFilesExist()) { + dhtSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.DHT_SIGNATURE); + msgSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_SIGNATURE); + msgEncryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_ENCRYPTION); + } + else { + // First time we create key pairs + try { + this.dhtSignatureKeyPair = CryptoService.generateDhtSignatureKeyPair(); + this.msgSignatureKeyPair = CryptoService.generateMsgSignatureKeyPair(); + this.msgEncryptionKeyPair = CryptoService.generateMsgEncryptionKeyPair(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw new CryptoException("Error at KeyRing constructor ", e); + } + keyStorage.saveKeyRing(this); + } + + pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic()); + } + + // For unit testing + KeyRing() throws NoSuchAlgorithmException { + keyStorage = null; + this.dhtSignatureKeyPair = CryptoService.generateDhtSignatureKeyPair(); + this.msgSignatureKeyPair = CryptoService.generateMsgSignatureKeyPair(); + this.msgEncryptionKeyPair = CryptoService.generateMsgEncryptionKeyPair(); + pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic()); + } + + KeyRing(KeyPair dhtSignatureKeyPair, KeyPair msgSignatureKeyPair, KeyPair msgEncryptionKeyPair) { + this.keyStorage = null; + this.dhtSignatureKeyPair = dhtSignatureKeyPair; + this.msgSignatureKeyPair = msgSignatureKeyPair; + this.msgEncryptionKeyPair = msgEncryptionKeyPair; + pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic()); + } + + public KeyPair getDhtSignatureKeyPair() { + return dhtSignatureKeyPair; + } + + public KeyPair getMsgSignatureKeyPair() { + return msgSignatureKeyPair; + } + + public KeyPair getMsgEncryptionKeyPair() { + return msgEncryptionKeyPair; + } + + public PubKeyRing getPubKeyRing() { + return pubKeyRing; + } +} diff --git a/core/src/main/java/io/bitsquare/crypto/KeyStorage.java b/core/src/main/java/io/bitsquare/crypto/KeyStorage.java new file mode 100644 index 0000000000..c9c9cb924a --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/KeyStorage.java @@ -0,0 +1,171 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import com.google.inject.Inject; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.CertificateException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.inject.Named; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class KeyStorage { + private static final Logger log = LoggerFactory.getLogger(KeyStorage.class); + + public static final String DIR_KEY = "key.storage.dir"; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + + public enum Key { + DHT_SIGNATURE("dhtSignature", CryptoService.DHT_SIGN_KEY_ALGO), + MSG_SIGNATURE("msgSignature", CryptoService.MSG_SIGN_KEY_ALGO), + MSG_ENCRYPTION("msgEncryption", CryptoService.MSG_ENCR_KEY_ALGO); + + private final String fileName; + private final String algorithm; + + Key(String fileName, String algorithm) { + this.fileName = fileName; + this.algorithm = algorithm; + } + + public String getFileName() { + return fileName; + } + + public String getAlgorithm() { + return algorithm; + } + + @Override + public String toString() { + return "Key{" + + "fileName='" + fileName + '\'' + + ", algorithm='" + algorithm + '\'' + + '}'; + } + } + + private final File storageDir; + + @Inject + public KeyStorage(@Named(DIR_KEY) File storageDir) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { + this.storageDir = storageDir; + } + + public boolean allKeyFilesExist() throws CryptoException { + return fileExists(KeyStorage.Key.DHT_SIGNATURE) && fileExists(KeyStorage.Key.MSG_SIGNATURE) && fileExists(KeyStorage.Key.MSG_ENCRYPTION); + } + + private boolean fileExists(Key key) throws CryptoException { + return new File(storageDir + "/" + key.getFileName() + "Pub.key").exists(); + } + + public KeyPair loadKeyPair(Key key) throws CryptoException { + long now = System.currentTimeMillis(); + try { + KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm()); + PublicKey publicKey; + PrivateKey privateKey; + + File filePublicKey = new File(storageDir + "/" + key.getFileName() + "Pub.key"); + try (FileInputStream fis = new FileInputStream(filePublicKey.getPath())) { + byte[] encodedPublicKey = new byte[(int) filePublicKey.length()]; + fis.read(encodedPublicKey); + + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey); + publicKey = keyFactory.generatePublic(publicKeySpec); + } catch (InvalidKeySpecException | IOException e) { + e.printStackTrace(); + log.error(e.getMessage()); + throw new CryptoException("Could not load key " + key.toString(), e); + } + + File filePrivateKey = new File(storageDir + "/" + key.getFileName() + "Priv.key"); + try (FileInputStream fis = new FileInputStream(filePrivateKey.getPath())) { + byte[] encodedPrivateKey = new byte[(int) filePrivateKey.length()]; + fis.read(encodedPrivateKey); + + PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey); + privateKey = keyFactory.generatePrivate(privateKeySpec); + } catch (InvalidKeySpecException | IOException e) { + e.printStackTrace(); + log.error(e.getMessage()); + throw new CryptoException("Could not load key " + key.toString(), e); + } + log.info("load completed in {} msec", System.currentTimeMillis() - now); + return new KeyPair(publicKey, privateKey); + + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + log.error(e.getMessage()); + throw new CryptoException("Could not load key " + key.toString(), e); + } + } + + public void saveKeyRing(KeyRing keyRing) throws CryptoException { + saveKeyPair(keyRing.getDhtSignatureKeyPair(), Key.DHT_SIGNATURE.getFileName()); + saveKeyPair(keyRing.getMsgSignatureKeyPair(), Key.MSG_SIGNATURE.getFileName()); + saveKeyPair(keyRing.getMsgEncryptionKeyPair(), Key.MSG_ENCRYPTION.getFileName()); + } + + public void saveKeyPair(KeyPair keyPair, String name) throws CryptoException { + if (!storageDir.exists()) + storageDir.mkdir(); + + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded()); + try (FileOutputStream fos = new FileOutputStream(storageDir + "/" + name + "Pub.key")) { + fos.write(x509EncodedKeySpec.getEncoded()); + } catch (IOException e) { + e.printStackTrace(); + log.error(e.getMessage()); + throw new CryptoException("Could not save key " + name, e); + } + + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded()); + try (FileOutputStream fos = new FileOutputStream(storageDir + "/" + name + "Priv.key")) { + fos.write(pkcs8EncodedKeySpec.getEncoded()); + } catch (IOException e) { + e.printStackTrace(); + log.error(e.getMessage()); + throw new CryptoException("Could not save key " + name, e); + } + } +} diff --git a/core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java b/core/src/main/java/io/bitsquare/crypto/MessageWithPubKey.java similarity index 56% rename from core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java rename to core/src/main/java/io/bitsquare/crypto/MessageWithPubKey.java index e30ebb46fd..5d18b1b0e4 100644 --- a/core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java +++ b/core/src/main/java/io/bitsquare/crypto/MessageWithPubKey.java @@ -15,27 +15,32 @@ * along with Bitsquare. If not, see . */ -package io.bitsquare.p2p; +package io.bitsquare.crypto; -import java.io.Serializable; +import io.bitsquare.p2p.Message; + +import java.security.PublicKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/* - Stores a message in encrypted form, so it never leaves the client in plain text. - */ -public class EncryptedMailboxMessage implements MailboxMessage, Serializable { - private static final long serialVersionUID = -3111178895546299769L; - private static final Logger log = LoggerFactory.getLogger(EncryptedMailboxMessage.class); +public class MessageWithPubKey implements Message { + private static final Logger log = LoggerFactory.getLogger(MessageWithPubKey.class); - private final byte[] bytes; - - public EncryptedMailboxMessage(byte[] bytes) { - this.bytes = bytes; + public Message getMessage() { + return message; } - public byte[] getBytes() { - return bytes; + public PublicKey getSignaturePubKey() { + return signaturePubKey; + } + + private final Message message; + private final PublicKey signaturePubKey; + + + public MessageWithPubKey(Message message, PublicKey signaturePubKey) { + this.message = message; + this.signaturePubKey = signaturePubKey; } } diff --git a/core/src/main/java/io/bitsquare/crypto/PubKeyRing.java b/core/src/main/java/io/bitsquare/crypto/PubKeyRing.java new file mode 100644 index 0000000000..8859779d0e --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/PubKeyRing.java @@ -0,0 +1,142 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import java.io.Serializable; + +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Same as KeyRing but with public keys only. + * Used to sent over the wire to other peer. + */ +public class PubKeyRing implements Serializable { + // That object is sent over the wire, so we need to take care of version compatibility. + private static final long serialVersionUID = 1L; + + private static final Logger log = LoggerFactory.getLogger(PubKeyRing.class); + + private final byte[] dhtSignaturePubKeyBytes; + private final byte[] msgSignaturePubKeyBytes; + private final byte[] msgEncryptionPubKeyBytes; + + transient private PublicKey dhtSignaturePubKey; + transient private PublicKey msgSignaturePubKey; + transient private PublicKey msgEncryptionPubKey; + + public PubKeyRing(PublicKey dhtSignaturePubKey, PublicKey msgSignaturePubKey, PublicKey msgEncryptionPubKey) { + this.dhtSignaturePubKey = dhtSignaturePubKey; + this.msgSignaturePubKey = msgSignaturePubKey; + this.msgEncryptionPubKey = msgEncryptionPubKey; + + this.dhtSignaturePubKeyBytes = new X509EncodedKeySpec(dhtSignaturePubKey.getEncoded()).getEncoded(); + this.msgSignaturePubKeyBytes = new X509EncodedKeySpec(msgSignaturePubKey.getEncoded()).getEncoded(); + this.msgEncryptionPubKeyBytes = new X509EncodedKeySpec(msgEncryptionPubKey.getEncoded()).getEncoded(); + } + + public PublicKey getDhtSignaturePubKey() { + if (dhtSignaturePubKey == null) { + try { + dhtSignaturePubKey = KeyFactory.getInstance(CryptoService.DHT_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(dhtSignaturePubKeyBytes)); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } + return dhtSignaturePubKey; + } + + + public PublicKey getMsgSignaturePubKey() { + if (msgSignaturePubKey == null) { + try { + msgSignaturePubKey = KeyFactory.getInstance(CryptoService.MSG_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgSignaturePubKeyBytes)); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } + return msgSignaturePubKey; + } + + public PublicKey getMsgEncryptionPubKey() { + if (msgEncryptionPubKey == null) { + try { + msgEncryptionPubKey = KeyFactory.getInstance(CryptoService.MSG_ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgEncryptionPubKeyBytes)); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + } + return msgEncryptionPubKey; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PubKeyRing that = (PubKeyRing) o; + + if (!Arrays.equals(dhtSignaturePubKeyBytes, that.dhtSignaturePubKeyBytes)) return false; + if (!Arrays.equals(msgSignaturePubKeyBytes, that.msgSignaturePubKeyBytes)) return false; + return Arrays.equals(msgEncryptionPubKeyBytes, that.msgEncryptionPubKeyBytes); + + } + + @Override + public int hashCode() { + int result = dhtSignaturePubKeyBytes != null ? Arrays.hashCode(dhtSignaturePubKeyBytes) : 0; + result = 31 * result + (msgSignaturePubKeyBytes != null ? Arrays.hashCode(msgSignaturePubKeyBytes) : 0); + result = 31 * result + (msgEncryptionPubKeyBytes != null ? Arrays.hashCode(msgEncryptionPubKeyBytes) : 0); + return result; + } + + + public String getHashString() { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(dhtSignaturePubKeyBytes); + messageDigest.update(msgSignaturePubKeyBytes); + messageDigest.update(msgEncryptionPubKeyBytes); + return new String(messageDigest.digest()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Hash Algorithm not found.", e); + } + } + + @Override + public String toString() { + return "PubKeyRing{" + + "\ndhtSignaturePubKey=\n" + Util.pubKeyToString(getDhtSignaturePubKey()) + + "\n\nmsgSignaturePubKey=\n" + Util.pubKeyToString(getMsgSignaturePubKey()) + + "\n\nmsgEncryptionPubKey=\n" + Util.pubKeyToString(getMsgEncryptionPubKey()) + + '}'; + } + +} diff --git a/core/src/main/java/io/bitsquare/crypto/SealedAndSignedMessage.java b/core/src/main/java/io/bitsquare/crypto/SealedAndSignedMessage.java new file mode 100644 index 0000000000..1956f9a420 --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/SealedAndSignedMessage.java @@ -0,0 +1,63 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import io.bitsquare.p2p.Message; + +import java.io.Serializable; + +import java.security.PublicKey; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.SealedObject; + +/** + * Packs the encrypted symmetric secretKey and the encrypted signed message into one object. + * SecretKey is encrypted with asymmetric pubKey of peer. Signed message is encrypted with secretKey. + * Using that hybrid encryption model we are not restricted by data size and performance as symmetric encryption is very fast. + */ +public class SealedAndSignedMessage implements Serializable, Message { + // That object is saved to disc. We need to take care of changes to not break deserialization. + private static final long serialVersionUID = 1L; + + private static final Logger log = LoggerFactory.getLogger(SealedAndSignedMessage.class); + + private final SealedObject sealedSecretKey; + private final SealedObject sealedMessage; + private final PublicKey signaturePubKey; + + public SealedAndSignedMessage(SealedObject sealedSecretKey, SealedObject sealedMessage, PublicKey signaturePubKey) { + this.sealedSecretKey = sealedSecretKey; + this.sealedMessage = sealedMessage; + this.signaturePubKey = signaturePubKey; + } + + public SealedObject getSealedSecretKey() { + return sealedSecretKey; + } + + public SealedObject getSealedMessage() { + return sealedMessage; + } + + public PublicKey getSignaturePubKey() { + return signaturePubKey; + } +} diff --git a/core/src/main/java/io/bitsquare/crypto/SignatureService.java b/core/src/main/java/io/bitsquare/crypto/SignatureService.java deleted file mode 100644 index a7dd4b807c..0000000000 --- a/core/src/main/java/io/bitsquare/crypto/SignatureService.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 . - */ - -package io.bitsquare.crypto; - -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Utils; - -import com.google.common.base.Charsets; - -import org.spongycastle.util.encoders.Base64; - -public class SignatureService { - - public String signMessage(ECKey key, String message) { - byte[] data = Utils.formatMessageForSigning(message); - Sha256Hash hash = Sha256Hash.hashTwice(data); - return signMessage(key, hash); - } - - public ECKey.ECDSASignature signBytes(ECKey key, byte[] data) { - return key.sign(Sha256Hash.hashTwice(data), null); - } - - public String signMessage(ECKey key, byte[] data) { - Sha256Hash hash = Sha256Hash.hashTwice(data); - return signMessage(key, hash); - } - - public String signMessage(ECKey key, Sha256Hash hash) { - ECKey.ECDSASignature sig = key.sign(hash, null); - // Now we have to work backwards to figure out the recId needed to recover the signature. - int recId = -1; - for (int i = 0; i < 4; i++) { - ECKey k = ECKey.recoverFromSignature(i, sig, hash, key.isCompressed()); - if (k != null && k.getPubKeyPoint().equals(key.getPubKeyPoint())) { - recId = i; - break; - } - } - if (recId == -1) - throw new RuntimeException("Could not construct a recoverable key. This should never happen."); - int headerByte = recId + 27 + (key.isCompressed() ? 4 : 0); - byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S - sigData[0] = (byte) headerByte; - System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32); - System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32); - return new String(Base64.encode(sigData), Charsets.UTF_8); - } - - - public byte[] digestMessageWithSignature(ECKey key, String message) { - String signedMessage = signMessage(key, message); - return Utils.sha256hash160(message.concat(signedMessage).getBytes(Charsets.UTF_8)); - } - - public boolean verify(byte[] signaturePubKey, byte[] data, ECKey.ECDSASignature sig) { - return ECKey.fromPublicOnly(signaturePubKey).verify(Sha256Hash.hashTwice(data), sig); - } -} diff --git a/core/src/main/java/io/bitsquare/crypto/Util.java b/core/src/main/java/io/bitsquare/crypto/Util.java index 689a6f3e4d..5a4714e737 100644 --- a/core/src/main/java/io/bitsquare/crypto/Util.java +++ b/core/src/main/java/io/bitsquare/crypto/Util.java @@ -25,24 +25,23 @@ import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Util { private static final Logger log = LoggerFactory.getLogger(Util.class); - public static String getHexFromPubKey(PublicKey publicKey) { + public static String pubKeyToString(PublicKey publicKey) { final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded()); - return Utils.HEX.encode(x509EncodedKeySpec.getEncoded()); + return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded()); } + // TODO just temp for arbitrator public static PublicKey decodeDSAPubKeyHex(String pubKeyHex) throws NoSuchAlgorithmException, InvalidKeySpecException { X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex)); KeyFactory keyFactory = KeyFactory.getInstance("DSA"); return keyFactory.generatePublic(pubKeySpec); } - - public static String encodePubKeyToHex(PublicKey pubKey) { - return Utils.HEX.encode(pubKey.getEncoded()); - } } diff --git a/core/src/main/java/io/bitsquare/offer/Offer.java b/core/src/main/java/io/bitsquare/offer/Offer.java index 01b7753bb8..36b2cb03f4 100644 --- a/core/src/main/java/io/bitsquare/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/offer/Offer.java @@ -18,6 +18,7 @@ package io.bitsquare.offer; import io.bitsquare.btc.Restrictions; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.locale.Country; @@ -28,8 +29,6 @@ import org.bitcoinj.utils.Fiat; import java.io.IOException; import java.io.Serializable; -import java.security.PublicKey; - import java.util.Date; import java.util.List; @@ -48,6 +47,7 @@ public class Offer implements Serializable { private static final long serialVersionUID = 1L; private transient static final Logger log = LoggerFactory.getLogger(Offer.class); + public enum Direction {BUY, SELL} public enum State { @@ -71,7 +71,7 @@ public class Offer implements Serializable { private final long fiatPrice; private final Coin amount; private final Coin minAmount; - private final PublicKey p2pSigPubKey; + private final PubKeyRing pubKeyRing; private final FiatAccount.Type fiatAccountType; private final Country bankAccountCountry; @@ -95,7 +95,7 @@ public class Offer implements Serializable { /////////////////////////////////////////////////////////////////////////////////////////// public Offer(String id, - PublicKey p2pSigPubKey, + PubKeyRing pubKeyRing, Direction direction, long fiatPrice, Coin amount, @@ -109,7 +109,7 @@ public class Offer implements Serializable { List acceptedCountries, List acceptedLanguageCodes) { this.id = id; - this.p2pSigPubKey = p2pSigPubKey; + this.pubKeyRing = pubKeyRing; this.direction = direction; this.fiatPrice = fiatPrice; this.amount = amount; @@ -144,7 +144,7 @@ public class Offer implements Serializable { checkNotNull(getCurrencyCode(), "Currency is null"); checkNotNull(getDirection(), "Direction is null"); checkNotNull(getId(), "Id is null"); - checkNotNull(getP2pSigPubKey(), "p2pSigPubKey is null"); + checkNotNull(getPubKeyRing(), "pubKeyRing is null"); checkNotNull(getMinAmount(), "MinAmount is null"); checkNotNull(getPrice(), "Price is null"); @@ -199,6 +199,11 @@ public class Offer implements Serializable { return id; } + public PubKeyRing getPubKeyRing() { + return pubKeyRing; + } + + public Fiat getPrice() { return Fiat.valueOf(currencyCode, fiatPrice); } @@ -256,10 +261,6 @@ public class Offer implements Serializable { return bankAccountUID; } - public PublicKey getP2pSigPubKey() { - return p2pSigPubKey; - } - public Date getCreationDate() { return creationDate; } @@ -282,7 +283,7 @@ public class Offer implements Serializable { ", fiatPrice=" + fiatPrice + ", amount=" + amount + ", minAmount=" + minAmount + - /* ", p2pSigPubKey=" + p2pSigPubKey +*/ + /* ", pubKeyRing=" + pubKeyRing +*/ ", fiatAccountType=" + fiatAccountType + ", bankAccountCountry=" + bankAccountCountry + ", securityDeposit=" + securityDeposit + diff --git a/core/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBookService.java b/core/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBookService.java index 7e2db74be3..d5ef1d16a4 100644 --- a/core/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBookService.java +++ b/core/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBookService.java @@ -19,11 +19,11 @@ package io.bitsquare.offer.tomp2p; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; +import io.bitsquare.crypto.KeyRing; import io.bitsquare.offer.Offer; import io.bitsquare.offer.OfferBookService; import io.bitsquare.p2p.tomp2p.TomP2PDHTService; import io.bitsquare.p2p.tomp2p.TomP2PNode; -import io.bitsquare.user.User; import java.io.IOException; @@ -58,8 +58,8 @@ public class TomP2POfferBookService extends TomP2PDHTService implements OfferBoo @Inject - public TomP2POfferBookService(TomP2PNode tomP2PNode, User user) { - super(tomP2PNode, user); + public TomP2POfferBookService(TomP2PNode tomP2PNode, KeyRing keyRing) { + super(tomP2PNode, keyRing); } @Override diff --git a/core/src/main/java/io/bitsquare/p2p/AddressService.java b/core/src/main/java/io/bitsquare/p2p/AddressService.java index 55d4c71fa8..4ad6ebe6bf 100644 --- a/core/src/main/java/io/bitsquare/p2p/AddressService.java +++ b/core/src/main/java/io/bitsquare/p2p/AddressService.java @@ -18,10 +18,9 @@ package io.bitsquare.p2p; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.p2p.listener.GetPeerAddressListener; -import java.security.PublicKey; - public interface AddressService extends DHTService { - void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener getPeerAddressListener); + void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener getPeerAddressListener); } diff --git a/core/src/main/java/io/bitsquare/crypto/HashService.java b/core/src/main/java/io/bitsquare/p2p/DecryptedMessageHandler.java similarity index 71% rename from core/src/main/java/io/bitsquare/crypto/HashService.java rename to core/src/main/java/io/bitsquare/p2p/DecryptedMessageHandler.java index e77d059441..43c83de302 100644 --- a/core/src/main/java/io/bitsquare/crypto/HashService.java +++ b/core/src/main/java/io/bitsquare/p2p/DecryptedMessageHandler.java @@ -15,15 +15,10 @@ * along with Bitsquare. If not, see . */ -package io.bitsquare.crypto; +package io.bitsquare.p2p; -import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Utils; +import io.bitsquare.crypto.MessageWithPubKey; -public class HashService { - - public Sha256Hash hash(String message) { - byte[] data = Utils.formatMessageForSigning(message); - return Sha256Hash.hashTwice(data); - } +public interface DecryptedMessageHandler { + void handleMessage(MessageWithPubKey message, Peer sender); } diff --git a/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java b/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java index 35d9e9dd27..ab0bd8ffbf 100644 --- a/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java +++ b/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java @@ -17,8 +17,10 @@ package io.bitsquare.p2p; +import io.bitsquare.crypto.SealedAndSignedMessage; + import java.util.List; public interface MailboxMessagesResultHandler { - void handleResult(List encryptedMailboxMessages); + void handleResult(List encryptedMessages); } diff --git a/core/src/main/java/io/bitsquare/p2p/MailboxService.java b/core/src/main/java/io/bitsquare/p2p/MailboxService.java index ce9e9ddd0e..5e99590e80 100644 --- a/core/src/main/java/io/bitsquare/p2p/MailboxService.java +++ b/core/src/main/java/io/bitsquare/p2p/MailboxService.java @@ -19,14 +19,14 @@ package io.bitsquare.p2p; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; - -import java.security.PublicKey; +import io.bitsquare.crypto.PubKeyRing; +import io.bitsquare.crypto.SealedAndSignedMessage; public interface MailboxService { - void addMessage(PublicKey recipientP2pSigPubKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler); + void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler); - void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler); + void getAllMessages(MailboxMessagesResultHandler resultHandler); - void removeAllMessages(PublicKey p2pSigPubKey, ResultHandler resultHandler, FaultHandler faultHandler); + void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler); } diff --git a/core/src/main/java/io/bitsquare/p2p/MessageService.java b/core/src/main/java/io/bitsquare/p2p/MessageService.java index 94cb1e5aac..4cc1ee8ada 100644 --- a/core/src/main/java/io/bitsquare/p2p/MessageService.java +++ b/core/src/main/java/io/bitsquare/p2p/MessageService.java @@ -18,20 +18,20 @@ package io.bitsquare.p2p; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.p2p.listener.SendMessageListener; -import org.bitcoinj.core.ECKey; - -import java.security.PublicKey; - public interface MessageService extends P2PService { void sendMessage(Peer peer, Message message, SendMessageListener listener); - void sendMessage(Peer peer, Message message, PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair, - SendMessageListener listener); + void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener); void addMessageHandler(MessageHandler listener); void removeMessageHandler(MessageHandler listener); -} + + void addDecryptedMessageHandler(DecryptedMessageHandler listener); + + void removeDecryptedMessageHandler(DecryptedMessageHandler listener); +} \ No newline at end of file diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java index 80040665b0..51cad07c08 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java @@ -17,16 +17,15 @@ package io.bitsquare.p2p.tomp2p; +import io.bitsquare.crypto.KeyRing; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.NetworkException; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.GetPeerAddressListener; -import io.bitsquare.user.User; import java.io.IOException; -import java.security.PublicKey; - import java.util.Timer; import java.util.TimerTask; @@ -63,10 +62,10 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public TomP2PAddressService(TomP2PNode tomP2PNode, User user) { - super(tomP2PNode, user); + public TomP2PAddressService(TomP2PNode tomP2PNode, KeyRing keyRing) { + super(tomP2PNode, keyRing); - locationKey = Utils.makeSHAHash(user.getP2pSigKeyPair().getPublic().getEncoded()); + locationKey = Utils.makeSHAHash(keyRing.getPubKeyRing().getDhtSignaturePubKey().getEncoded()); } @Override @@ -92,9 +91,9 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener listener) { - final Number160 locationKey = Utils.makeSHAHash(p2pSigPubKey.getEncoded()); - FutureGet futureGet = getDataOfProtectedDomain(locationKey, p2pSigPubKey); + public void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener listener) { + final Number160 locationKey = Utils.makeSHAHash(pubKeyRing.getDhtSignaturePubKey().getEncoded()); + FutureGet futureGet = getDataOfProtectedDomain(locationKey, pubKeyRing.getDhtSignaturePubKey()); log.trace("findPeerAddress called"); futureGet.addListener(new BaseFutureAdapter() { @Override diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java index c5844e10bd..80d8aec9df 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java @@ -17,8 +17,8 @@ package io.bitsquare.p2p.tomp2p; +import io.bitsquare.crypto.KeyRing; import io.bitsquare.p2p.DHTService; -import io.bitsquare.user.User; import java.security.KeyPair; import java.security.PublicKey; @@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory; public class TomP2PDHTService extends TomP2PService implements DHTService { private static final Logger log = LoggerFactory.getLogger(TomP2PDHTService.class); - private final KeyPair keyPair; + private final KeyPair dhtSignatureKeyPair; private final Number160 pubKeyHashForMyDomain; @@ -47,10 +47,11 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public TomP2PDHTService(TomP2PNode tomP2PNode, User user) { + public TomP2PDHTService(TomP2PNode tomP2PNode, KeyRing keyRing) { super(tomP2PNode); - keyPair = user.getP2pSigKeyPair(); - pubKeyHashForMyDomain = Utils.makeSHAHash(keyPair.getPublic().getEncoded()); + + dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair(); + pubKeyHashForMyDomain = Utils.makeSHAHash(dhtSignatureKeyPair.getPublic().getEncoded()); } @Override @@ -117,7 +118,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { */ public FuturePut putDataToMyProtectedDomain(Number160 locationKey, Data data) { log.trace("putDataToMyProtectedDomain"); - data.protectEntry(keyPair); + data.protectEntry(dhtSignatureKeyPair); return peerDHT.put(locationKey).data(data).protectDomain().domainKey(pubKeyHashForMyDomain).start(); } @@ -131,7 +132,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { public FutureRemove removeDataFromMyProtectedDomain(Number160 locationKey) { log.trace("removeDataOfProtectedDomain"); if (peerDHT != null) - return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start(); + return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start(); else return null; } @@ -167,10 +168,10 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { */ public FuturePut addProtectedDataToMap(Number160 locationKey, Data data) { log.trace("addProtectedDataToMap locationKey = " + locationKey); - data.protectEntry(keyPair); + data.protectEntry(dhtSignatureKeyPair); log.trace("addProtectedDataToMap with contentKey " + data.hash().toString()); - return peerDHT.add(locationKey).data(data).keyPair(keyPair).start(); + return peerDHT.add(locationKey).data(data).keyPair(dhtSignatureKeyPair).start(); } /** @@ -185,7 +186,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { log.trace("removeProtectedDataFromMap locationKey = " + locationKey); Number160 contentKey = data.hash(); log.trace("removeProtectedDataFromMap with contentKey " + contentKey.toString()); - return peerDHT.remove(locationKey).contentKey(contentKey).keyPair(keyPair).start(); + return peerDHT.remove(locationKey).contentKey(contentKey).keyPair(dhtSignatureKeyPair).start(); } /** @@ -221,8 +222,8 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { log.trace("addDataToMapOfProtectedDomain"); log.trace("addDataToMapOfProtectedDomain with contentKey " + data.hash().toString()); final Number160 pubKeyHashOfDomainOwner = Utils.makeSHAHash(publicKey.getEncoded()); - return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(keyPair) - .data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(keyPair).start(); + return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair) + .data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair).start(); } /** @@ -237,7 +238,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { log.trace("removeDataFromMapOfMyProtectedDomain"); Number160 contentKey = data.hash(); log.trace("removeDataFromMapOfMyProtectedDomain with contentKey " + contentKey.toString()); - return peerDHT.remove(locationKey).contentKey(contentKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start(); + return peerDHT.remove(locationKey).contentKey(contentKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start(); } /** @@ -261,7 +262,8 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { */ public FutureRemove removeAllDataFromMapOfMyProtectedDomain(Number160 locationKey) { log.trace("getDataFromMapOfMyProtectedDomain"); - return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).all().domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start(); + return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).all().domainKey(pubKeyHashForMyDomain).keyPair + (dhtSignatureKeyPair).start(); } diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java index 6055c7a08b..9f7b5641a7 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java @@ -19,14 +19,16 @@ package io.bitsquare.p2p.tomp2p; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; +import io.bitsquare.crypto.KeyRing; +import io.bitsquare.crypto.PubKeyRing; +import io.bitsquare.crypto.SealedAndSignedMessage; import io.bitsquare.offer.OfferBookService; -import io.bitsquare.p2p.EncryptedMailboxMessage; import io.bitsquare.p2p.MailboxMessagesResultHandler; import io.bitsquare.p2p.MailboxService; -import io.bitsquare.user.User; import java.io.IOException; +import java.security.KeyPair; import java.security.PublicKey; import java.util.ArrayList; @@ -53,10 +55,13 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer private static final int TTL = 21 * 24 * 60 * 60; // the message is default 21 days valid, as a max trade period might be about 2 weeks. private final List offerRepositoryListeners = new ArrayList<>(); + private final KeyPair dhtSignatureKeyPair; @Inject - public TomP2PMailboxService(TomP2PNode tomP2PNode, User user) { - super(tomP2PNode, user); + public TomP2PMailboxService(TomP2PNode tomP2PNode, KeyRing keyRing) { + super(tomP2PNode, keyRing); + + dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair(); } @Override @@ -70,25 +75,33 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer } @Override - public void addMessage(PublicKey recipientP2pSigPubKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { + public void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { try { final Data data = new Data(message); data.ttlSeconds(TTL); - log.trace("Add message to DHT requested. Added data: [locationKey: " + getLocationKey(recipientP2pSigPubKey) + + Number160 locationKey = getLocationKey(pubKeyRing.getDhtSignaturePubKey()); + log.trace("Add message to DHT requested. Added data: [locationKey: " + locationKey + ", hash: " + data.hash().toString() + "]"); - FuturePut futurePut = addDataToMapOfProtectedDomain(getLocationKey(recipientP2pSigPubKey), data, recipientP2pSigPubKey); + FuturePut futurePut = addDataToMapOfProtectedDomain(locationKey, + data, pubKeyRing.getDhtSignaturePubKey()); futurePut.addListener(new BaseFutureListener() { @Override public void operationComplete(BaseFuture future) throws Exception { if (future.isSuccess()) { executor.execute(() -> { + log.trace("Add message to mailbox was successful. Added data: [locationKey: " + locationKey + ", value: " + data + "]"); resultHandler.handleResult(); - - log.trace("Add message to mailbox was successful. Added data: [locationKey: " + getLocationKey(recipientP2pSigPubKey) + - ", value: " + data + "]"); }); } + else { + // Seems to be a bug in TomP2P that when one peer shuts down the expected nr of peers and the delivered are not matching + // As far tested the storage succeeded, so seems to be a wrong message. + //Future (compl/canc):true/false, FAILED, Expected 3 result, but got 2 + + log.warn("Ignoring isSuccess=false case. failedReason: {}", future.failedReason()); + resultHandler.handleResult(); + } } @Override @@ -102,21 +115,21 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer } @Override - public void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler) { - log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(p2pSigPubKey)); - FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey)); + public void getAllMessages(MailboxMessagesResultHandler resultHandler) { + log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic())); + FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic())); futureGet.addListener(new BaseFutureAdapter() { @Override public void operationComplete(BaseFuture future) throws Exception { if (future.isSuccess()) { final Map dataMap = futureGet.dataMap(); - List messages = new ArrayList<>(); + List messages = new ArrayList<>(); if (dataMap != null) { for (Data messageData : dataMap.values()) { try { Object messageDataObject = messageData.object(); - if (messageDataObject instanceof EncryptedMailboxMessage) { - messages.add((EncryptedMailboxMessage) messageDataObject); + if (messageDataObject instanceof SealedAndSignedMessage) { + messages.add((SealedAndSignedMessage) messageDataObject); } } catch (ClassNotFoundException | IOException e) { e.printStackTrace(); @@ -125,7 +138,7 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer executor.execute(() -> resultHandler.handleResult(messages)); } - log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(p2pSigPubKey) + log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(dhtSignatureKeyPair.getPublic()) + ", values: " + futureGet.dataMap() + "]"); } else { @@ -143,9 +156,9 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer } @Override - public void removeAllMessages(PublicKey p2pSigPubKey, ResultHandler resultHandler, FaultHandler faultHandler) { - log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(p2pSigPubKey)); - FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey)); + public void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler) { + log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic())); + FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic())); futureRemove.addListener(new BaseFutureListener() { @Override public void operationComplete(BaseFuture future) throws Exception { diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java index af59fbf858..2535e5b1ef 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java @@ -17,8 +17,12 @@ package io.bitsquare.p2p.tomp2p; -import io.bitsquare.crypto.EncryptionService; -import io.bitsquare.p2p.EncryptedMailboxMessage; +import io.bitsquare.crypto.CryptoException; +import io.bitsquare.crypto.CryptoService; +import io.bitsquare.crypto.MessageWithPubKey; +import io.bitsquare.crypto.PubKeyRing; +import io.bitsquare.crypto.SealedAndSignedMessage; +import io.bitsquare.p2p.DecryptedMessageHandler; import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.Message; @@ -26,10 +30,7 @@ import io.bitsquare.p2p.MessageHandler; import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.SendMessageListener; - -import org.bitcoinj.core.ECKey; - -import java.security.PublicKey; +import io.bitsquare.util.Utilities; import java.util.concurrent.CopyOnWriteArrayList; @@ -44,10 +45,12 @@ import org.slf4j.LoggerFactory; public class TomP2PMessageService extends TomP2PService implements MessageService { private static final Logger log = LoggerFactory.getLogger(TomP2PMessageService.class); + private static final int MAX_MESSAGE_SIZE = 100 * 1024; // 34 kb is currently the max size used private final CopyOnWriteArrayList messageHandlers = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList decryptedMessageHandlers = new CopyOnWriteArrayList<>(); private final MailboxService mailboxService; - private final EncryptionService encryptionService; + private final CryptoService cryptoService; /////////////////////////////////////////////////////////////////////////////////////////// @@ -55,10 +58,10 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, EncryptionService encryptionService) { + public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, CryptoService cryptoService) { super(tomP2PNode); this.mailboxService = mailboxService; - this.encryptionService = encryptionService; + this.cryptoService = cryptoService; } @@ -75,73 +78,73 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic @Override public void sendMessage(Peer peer, Message message, SendMessageListener listener) { - sendMessage(peer, message, null, null, null, listener); + doSendMessage(peer, null, message, listener); } @Override - public void sendMessage(Peer peer, Message message, PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair, - SendMessageListener listener) { - - if (peer == null) - throw new IllegalArgumentException("Peer must not be null"); - else if (!(peer instanceof TomP2PPeer)) - throw new IllegalArgumentException("Peer must be of type TomP2PPeer"); - - FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(message).start(); - futureDirect.addListener(new BaseFutureListener() { - @Override - public void operationComplete(BaseFuture future) throws Exception { - if (future.isSuccess()) { - log.debug("sendMessage completed"); - executor.execute(listener::handleResult); - } - else { - if (recipientP2pSigPubKey != null && recipientP2pEncryptPubKey != null) { - log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " + futureDirect.failedReason()); - sendMailboxMessage(recipientP2pSigPubKey, recipientP2pEncryptPubKey, registrationKeyPair, (MailboxMessage) message, listener); - } - else { - log.error("sendMessage failed with reason " + futureDirect.failedReason()); - executor.execute(listener::handleFault); - } - } - } - - @Override - public void exceptionCaught(Throwable t) throws Exception { - if (recipientP2pSigPubKey != null && recipientP2pEncryptPubKey != null) { - log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: " + t.getMessage()); - sendMailboxMessage(recipientP2pSigPubKey, recipientP2pEncryptPubKey, registrationKeyPair, (MailboxMessage) message, listener); - } - else { - log.error("sendMessage failed with exception " + t.getMessage()); - executor.execute(listener::handleFault); - } - } - }); - } - - private void sendMailboxMessage(PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair, MailboxMessage message, - SendMessageListener - listener) { - byte[] result = null; - log.info("sendMailboxMessage called"); + public void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener) { + assert pubKeyRing != null; try { - result = encryptionService.encryptMessage(recipientP2pEncryptPubKey, registrationKeyPair, message); + doSendMessage(peer, pubKeyRing, cryptoService.encryptAndSignMessage(pubKeyRing, message), listener); } catch (Throwable t) { t.printStackTrace(); log.error(t.getMessage()); executor.execute(listener::handleFault); } - EncryptedMailboxMessage encrypted = new EncryptedMailboxMessage(result); - mailboxService.addMessage(recipientP2pSigPubKey, - encrypted, + } + + private void doSendMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener) { + log.debug("sendMessage called"); + if (peer == null) + throw new IllegalArgumentException("Peer must not be null"); + else if (!(peer instanceof TomP2PPeer)) + throw new IllegalArgumentException("Peer must be of type TomP2PPeer"); + + try { + FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(message).start(); + futureDirect.addListener(new BaseFutureListener() { + @Override + public void operationComplete(BaseFuture future) throws Exception { + if (future.isSuccess()) { + log.debug("sendMessage completed"); + executor.execute(listener::handleResult); + } + else { + log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " + + futureDirect.failedReason()); + if (pubKeyRing != null) + sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) message, listener); + } + } + + @Override + public void exceptionCaught(Throwable t) throws Exception { + log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: " + + t.getMessage()); + if (pubKeyRing != null) + sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) message, listener); + } + } + ); + } catch (Throwable t) { + t.printStackTrace(); + log.error(t.getMessage()); + executor.execute(listener::handleFault); + } + } + + + private void sendMailboxMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, SendMessageListener listener) { + log.info("sendMailboxMessage called"); + mailboxService.addMessage( + pubKeyRing, + message, () -> { log.debug("Message successfully added to peers mailbox."); executor.execute(listener::handleResult); }, (errorMessage, throwable) -> { - log.error("Message failed to add to peers mailbox."); + log.error("Message failed to add to peers mailbox."); executor.execute(listener::handleFault); } ); @@ -159,6 +162,18 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic log.error("Try to remove listener which was never added."); } + @Override + public void addDecryptedMessageHandler(DecryptedMessageHandler listener) { + if (!decryptedMessageHandlers.add(listener)) + log.error("Add listener did not change list. Probably listener has been already added."); + } + + @Override + public void removeDecryptedMessageHandler(DecryptedMessageHandler listener) { + if (!decryptedMessageHandlers.remove(listener)) + log.error("Try to remove listener which was never added."); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Private @@ -166,21 +181,47 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic private void setupReplyHandler() { peerDHT.peer().objectDataReply((sender, message) -> { - log.debug("handleMessage peerAddress " + sender); - log.debug("handleMessage message " + message); + log.debug("Incoming message with peerAddress " + sender); + log.debug("Incoming message with type " + message); + + int messageSize = 0; + if (message != null) + messageSize = Utilities.objectToBytArray(message).length; + + log.debug("Incoming message with size " + messageSize); if (!sender.equals(peerDHT.peer().peerAddress())) { - if (message instanceof Message) + if (messageSize == 0) + log.warn("Received msg is null"); + else if (messageSize > MAX_MESSAGE_SIZE) + log.warn("Received msg size of {} is exceeding the max message size of {}.", + Utilities.objectToBytArray(message).length, MAX_MESSAGE_SIZE); + else if (message instanceof SealedAndSignedMessage) + executor.execute(() -> decryptedMessageHandlers.stream().forEach(e -> { + MessageWithPubKey messageWithPubKey = null; + try { + messageWithPubKey = getDecryptedMessageWithPubKey((SealedAndSignedMessage) message); + log.debug("decrypted message " + messageWithPubKey.getMessage()); + e.handleMessage(messageWithPubKey, new TomP2PPeer(sender)); + } catch (CryptoException e1) { + e1.printStackTrace(); + log.warn("decryptAndVerifyMessage msg failed", e1.getMessage()); + } + } + )); + else if (message instanceof Message) executor.execute(() -> messageHandlers.stream().forEach(e -> e.handleMessage((Message) message, new TomP2PPeer(sender)))); else - log.error("We got an object which is not type of Message. That must never happen. Request object = " + message); + log.warn("We got an object which is not type of Message. Object = " + message); } else { log.error("Received msg from myself. That must never happen."); } - return true; }); } + private MessageWithPubKey getDecryptedMessageWithPubKey(SealedAndSignedMessage message) throws CryptoException { + return cryptoService.decryptAndVerifyMessage((SealedAndSignedMessage) message); + } } diff --git a/core/src/main/java/io/bitsquare/trade/Contract.java b/core/src/main/java/io/bitsquare/trade/Contract.java index 68fb8db058..096beb8ab1 100644 --- a/core/src/main/java/io/bitsquare/trade/Contract.java +++ b/core/src/main/java/io/bitsquare/trade/Contract.java @@ -17,7 +17,7 @@ package io.bitsquare.trade; -import io.bitsquare.crypto.Util; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.offer.Offer; @@ -25,8 +25,6 @@ import org.bitcoinj.core.Coin; import java.io.Serializable; -import java.security.PublicKey; - import javax.annotation.concurrent.Immutable; @SuppressWarnings("WeakerAccess") @@ -52,8 +50,8 @@ public class Contract implements Serializable { String sellerAccountID, FiatAccount buyerFiatAccount, FiatAccount sellerFiatAccount, - PublicKey buyerP2pSigPubKey, - PublicKey sellerP2pSigPubKey) { + PubKeyRing buyerPubKeyRing, + PubKeyRing sellerPubKeyRing) { this.offer = offer; this.tradeAmount = tradeAmount; this.takeOfferFeeTxID = takeOfferFeeTxID; @@ -61,8 +59,8 @@ public class Contract implements Serializable { this.sellerAccountID = sellerAccountID; this.buyerFiatAccount = buyerFiatAccount; this.sellerFiatAccount = sellerFiatAccount; - this.buyerP2pSigPubKeyAsString = Util.getHexFromPubKey(buyerP2pSigPubKey); - this.sellerP2pSigPubKeyAsString = Util.getHexFromPubKey(sellerP2pSigPubKey); + this.buyerP2pSigPubKeyAsString = buyerPubKeyRing.toString(); + this.sellerP2pSigPubKeyAsString = sellerPubKeyRing.toString(); } @Override diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index 7e701b2c02..f49d782f88 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -22,10 +22,11 @@ import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.taskrunner.Model; -import io.bitsquare.crypto.SignatureService; +import io.bitsquare.crypto.CryptoService; +import io.bitsquare.crypto.KeyRing; +import io.bitsquare.crypto.MessageWithPubKey; import io.bitsquare.offer.Offer; import io.bitsquare.p2p.AddressService; -import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.Peer; import io.bitsquare.storage.Storage; @@ -95,10 +96,10 @@ abstract public class Trade implements Model, Serializable { private final Date creationDate; // Mutable + private MessageWithPubKey messageWithPubKey; private Date takeOfferDate; protected TradeState.ProcessState processState; protected TradeState.LifeCycleState lifeCycleState; - private MailboxMessage mailboxMessage; private Transaction depositTx; private Contract contract; private String contractAsJson; @@ -156,9 +157,10 @@ abstract public class Trade implements Model, Serializable { AddressService addressService, TradeWalletService tradeWalletService, BlockChainService blockChainService, - SignatureService signatureService, + CryptoService cryptoService, ArbitrationRepository arbitrationRepository, - User user) { + User user, + KeyRing keyRing) { processModel.onAllServicesInitialized(offer, messageService, @@ -166,18 +168,19 @@ abstract public class Trade implements Model, Serializable { walletService, tradeWalletService, blockChainService, - signatureService, + cryptoService, arbitrationRepository, - user); + user, + keyRing); createProtocol(); tradeProtocol.checkPayoutTxTimeLock(this); - if (mailboxMessage != null) { - tradeProtocol.applyMailboxMessage(mailboxMessage, this); + if (messageWithPubKey != null) { + tradeProtocol.applyMailboxMessage(messageWithPubKey, this); // After applied to protocol we remove it - mailboxMessage = null; + messageWithPubKey = null; } } @@ -220,8 +223,8 @@ abstract public class Trade implements Model, Serializable { } } - public void setMailboxMessage(MailboxMessage mailboxMessage) { - this.mailboxMessage = mailboxMessage; + public void setMailboxMessage(MessageWithPubKey messageWithPubKey) { + this.messageWithPubKey = messageWithPubKey; } public void setStorage(Storage storage) { @@ -463,7 +466,7 @@ abstract public class Trade implements Model, Serializable { ", processModel=" + processModel + ", processState=" + processState + ", lifeCycleState=" + lifeCycleState + - ", mailboxMessage=" + mailboxMessage + + ", messageWithPubKey=" + messageWithPubKey + ", depositTx=" + depositTx + /* ", contract=" + contract + ", contractAsJson='" + contractAsJson + '\'' +*/ diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index e9c0bbd29c..961d67f4ab 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -25,15 +25,17 @@ import io.bitsquare.btc.WalletService; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; -import io.bitsquare.crypto.EncryptionService; -import io.bitsquare.crypto.SignatureService; +import io.bitsquare.crypto.CryptoService; +import io.bitsquare.crypto.KeyRing; +import io.bitsquare.crypto.MessageWithPubKey; +import io.bitsquare.crypto.SealedAndSignedMessage; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.offer.Offer; import io.bitsquare.offer.OfferBookService; import io.bitsquare.p2p.AddressService; -import io.bitsquare.p2p.EncryptedMailboxMessage; import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MailboxService; +import io.bitsquare.p2p.Message; import io.bitsquare.p2p.MessageService; import io.bitsquare.storage.Storage; import io.bitsquare.trade.handlers.TakeOfferResultHandler; @@ -79,6 +81,7 @@ public class TradeManager { private static final Logger log = LoggerFactory.getLogger(TradeManager.class); private final User user; + private KeyRing keyRing; private final AccountSettings accountSettings; private final MessageService messageService; private final MailboxService mailboxService; @@ -86,8 +89,7 @@ public class TradeManager { private final BlockChainService blockChainService; private final WalletService walletService; private final TradeWalletService tradeWalletService; - private final SignatureService signatureService; - private final EncryptionService encryptionService; + private final CryptoService cryptoService; private final OfferBookService offerBookService; private final ArbitrationRepository arbitrationRepository; @@ -107,6 +109,7 @@ public class TradeManager { @Inject public TradeManager(User user, + KeyRing keyRing, AccountSettings accountSettings, MessageService messageService, MailboxService mailboxService, @@ -114,12 +117,12 @@ public class TradeManager { BlockChainService blockChainService, WalletService walletService, TradeWalletService tradeWalletService, - SignatureService signatureService, - EncryptionService encryptionService, + CryptoService cryptoService, OfferBookService offerBookService, ArbitrationRepository arbitrationRepository, @Named("storage.dir") File storageDir) { this.user = user; + this.keyRing = keyRing; this.accountSettings = accountSettings; this.messageService = messageService; this.mailboxService = mailboxService; @@ -127,8 +130,7 @@ public class TradeManager { this.blockChainService = blockChainService; this.walletService = walletService; this.tradeWalletService = tradeWalletService; - this.signatureService = signatureService; - this.encryptionService = encryptionService; + this.cryptoService = cryptoService; this.offerBookService = offerBookService; this.arbitrationRepository = arbitrationRepository; @@ -167,25 +169,27 @@ public class TradeManager { // If there are messages in our mailbox we apply it and remove them from the DHT // We run that before initializing the pending trades to be sure the state is correct - mailboxService.getAllMessages(user.getP2pSigPubKey(), + mailboxService.getAllMessages( (encryptedMailboxMessages) -> { log.trace("mailboxService.getAllMessages success"); setMailboxMessagesToTrades(encryptedMailboxMessages); - emptyMailbox(); + //TODO testing + //emptyMailbox(); initPendingTrades(); }); } - private void setMailboxMessagesToTrades(List encryptedMailboxMessages) { - log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMailboxMessages.size()); - for (EncryptedMailboxMessage encrypted : encryptedMailboxMessages) { + private void setMailboxMessagesToTrades(List encryptedMessages) { + log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMessages.size()); + for (SealedAndSignedMessage encrypted : encryptedMessages) { try { - MailboxMessage mailboxMessage = encryptionService.decryptToMessage(user.getP2pEncryptPrivateKey(), encrypted.getBytes()); - if (mailboxMessage instanceof TradeMessage) { - String tradeId = ((TradeMessage) mailboxMessage).tradeId; + MessageWithPubKey messageWithPubKey = cryptoService.decryptAndVerifyMessage(encrypted); + Message message = messageWithPubKey.getMessage(); + if (message instanceof MailboxMessage && message instanceof TradeMessage) { + String tradeId = ((TradeMessage) message).tradeId; Optional tradeOptional = pendingTrades.stream().filter(e -> e.getId().equals(tradeId)).findAny(); if (tradeOptional.isPresent()) - tradeOptional.get().setMailboxMessage(mailboxMessage); + tradeOptional.get().setMailboxMessage(messageWithPubKey); } } catch (Throwable e) { e.printStackTrace(); @@ -195,7 +199,7 @@ public class TradeManager { } private void emptyMailbox() { - mailboxService.removeAllMessages(user.getP2pSigPubKey(), + mailboxService.removeAllMessages( () -> log.debug("All mailbox entries removed"), (errorMessage, fault) -> { log.error(errorMessage); @@ -259,7 +263,7 @@ public class TradeManager { FiatAccount fiatAccount = user.currentFiatAccountProperty().get(); Offer offer = new Offer(id, - user.getP2pSigPubKey(), + keyRing.getPubKeyRing(), direction, price.getValue(), amount, @@ -352,6 +356,7 @@ public class TradeManager { if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel( offer, + keyRing.getPubKeyRing(), messageService, addressService); @@ -373,15 +378,15 @@ public class TradeManager { // First we check if offer is still available then we create the trade with the protocol public void requestTakeOffer(Coin amount, Offer offer, TakeOfferResultHandler takeOfferResultHandler) { - CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, messageService, addressService); + CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, keyRing.getPubKeyRing(), messageService, addressService); CheckOfferAvailabilityProtocol availabilityProtocol = new CheckOfferAvailabilityProtocol(model, - () -> handleCheckOfferAvailabilityResult(amount, offer, model, takeOfferResultHandler), + () -> createTrade(amount, offer, model, takeOfferResultHandler), (errorMessage) -> disposeCheckOfferAvailabilityRequest(offer)); checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol); availabilityProtocol.checkOfferAvailability(); } - private void handleCheckOfferAvailabilityResult(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler + private void createTrade(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler takeOfferResultHandler) { disposeCheckOfferAvailabilityRequest(offer); if (offer.getState() == Offer.State.AVAILABLE) { @@ -487,9 +492,13 @@ public class TradeManager { addressService, tradeWalletService, blockChainService, - signatureService, + cryptoService, arbitrationRepository, - user); + user, + keyRing); } + public boolean isMyOffer(Offer offer) { + return offer.getPubKeyRing().getHashString().equals(keyRing.getPubKeyRing().getHashString()); + } } \ No newline at end of file diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityModel.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityModel.java index 4e5df9eb80..ac20f8c675 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityModel.java @@ -18,6 +18,7 @@ package io.bitsquare.trade.protocol.availability; import io.bitsquare.common.taskrunner.Model; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.offer.Offer; import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.MessageService; @@ -31,14 +32,16 @@ public class CheckOfferAvailabilityModel implements Model { private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityModel.class); public final Offer offer; + private final PubKeyRing pubKeyRing; public final MessageService messageService; public final AddressService addressService; private Peer peer; private OfferMessage message; - public CheckOfferAvailabilityModel(Offer offer, MessageService messageService, AddressService addressService) { + public CheckOfferAvailabilityModel(Offer offer, PubKeyRing pubKeyRing, MessageService messageService, AddressService addressService) { this.offer = offer; + this.pubKeyRing = pubKeyRing; this.messageService = messageService; this.addressService = addressService; } @@ -68,4 +71,8 @@ public class CheckOfferAvailabilityModel implements Model { public void onComplete() { } + + public PubKeyRing getPubKeyRing() { + return pubKeyRing; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityProtocol.java index 53762689a9..23250e0c32 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/CheckOfferAvailabilityProtocol.java @@ -20,14 +20,16 @@ package io.bitsquare.trade.protocol.availability; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.taskrunner.TaskRunner; +import io.bitsquare.crypto.MessageWithPubKey; import io.bitsquare.offer.Offer; +import io.bitsquare.p2p.DecryptedMessageHandler; import io.bitsquare.p2p.Message; -import io.bitsquare.p2p.MessageHandler; import io.bitsquare.p2p.Peer; +import io.bitsquare.trade.protocol.availability.messages.OfferMessage; import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage; import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress; import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage; -import io.bitsquare.trade.protocol.availability.tasks.RequestIsOfferAvailable; +import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage; import org.bitcoinj.utils.Threading; @@ -37,6 +39,8 @@ import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static io.bitsquare.util.Validator.nonEmptyStringOf; + public class CheckOfferAvailabilityProtocol { private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityProtocol.class); @@ -45,7 +49,7 @@ public class CheckOfferAvailabilityProtocol { private final CheckOfferAvailabilityModel model; private final ResultHandler resultHandler; private final ErrorMessageHandler errorMessageHandler; - private final MessageHandler messageHandler; + private final DecryptedMessageHandler decryptedMessageHandler; private Timer timeoutTimer; private boolean isCanceled; @@ -60,12 +64,12 @@ public class CheckOfferAvailabilityProtocol { this.model = model; this.resultHandler = resultHandler; this.errorMessageHandler = errorMessageHandler; - messageHandler = this::handleMessage; + decryptedMessageHandler = this::handleDecryptedMessageWithPubKey; } private void cleanup() { stopTimeout(); - model.messageService.removeMessageHandler(messageHandler); + model.messageService.removeDecryptedMessageHandler(decryptedMessageHandler); } @@ -77,7 +81,7 @@ public class CheckOfferAvailabilityProtocol { // reset model.offer.setState(Offer.State.UNKNOWN); - model.messageService.addMessageHandler(messageHandler); + model.messageService.addDecryptedMessageHandler(decryptedMessageHandler); taskRunner = new TaskRunner<>(model, () -> log.debug("sequence at onCheckOfferAvailability completed"), @@ -85,7 +89,7 @@ public class CheckOfferAvailabilityProtocol { ); taskRunner.addTasks( GetPeerAddress.class, - RequestIsOfferAvailable.class + SendRequestIsOfferAvailableMessage.class ); startTimeout(); taskRunner.run(); @@ -102,14 +106,18 @@ public class CheckOfferAvailabilityProtocol { // Incoming message handling /////////////////////////////////////////////////////////////////////////////////////////// - private void handleMessage(Message message, @SuppressWarnings("UnusedParameters") Peer sender) { - if (!isCanceled) { - if (message instanceof ReportOfferAvailabilityMessage && model.offer.getId().equals(((ReportOfferAvailabilityMessage) message).offerId)) - handle((ReportOfferAvailabilityMessage) message); + protected void handleDecryptedMessageWithPubKey(MessageWithPubKey messageWithPubKey, Peer sender) { + Message message = messageWithPubKey.getMessage(); + log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); + if (message instanceof OfferMessage) { + nonEmptyStringOf(((OfferMessage) message).offerId); + if (message instanceof ReportOfferAvailabilityMessage && model.offer.getId().equals(((OfferMessage) message).offerId)) + handleDecryptedMessage((ReportOfferAvailabilityMessage) message); } } - private void handle(ReportOfferAvailabilityMessage message) { + + private void handleDecryptedMessage(ReportOfferAvailabilityMessage message) { stopTimeout(); model.setMessage(message); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/messages/RequestIsOfferAvailableMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/messages/RequestIsOfferAvailableMessage.java index 2d08e9699d..fe867ac1e0 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/messages/RequestIsOfferAvailableMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/messages/RequestIsOfferAvailableMessage.java @@ -17,6 +17,7 @@ package io.bitsquare.trade.protocol.availability.messages; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import java.io.Serializable; @@ -25,7 +26,14 @@ public class RequestIsOfferAvailableMessage extends TradeMessage implements Seri // That object is sent over the wire, so we need to take care of version compatibility. private static final long serialVersionUID = 1L; - public RequestIsOfferAvailableMessage(String tradeId) { + private final PubKeyRing pubKeyRing; + + public RequestIsOfferAvailableMessage(String tradeId, PubKeyRing pubKeyRing) { super(tradeId); + this.pubKeyRing = pubKeyRing; + } + + public PubKeyRing getPubKeyRing() { + return pubKeyRing; } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java index 3032fe6653..1a74c57f17 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java @@ -39,7 +39,7 @@ public class GetPeerAddress extends Task { @Override protected void doRun() { try { - model.addressService.findPeerAddress(model.offer.getP2pSigPubKey(), new GetPeerAddressListener() { + model.addressService.findPeerAddress(model.offer.getPubKeyRing(), new GetPeerAddressListener() { @Override public void onResult(Peer peer) { model.setPeer(peer); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/RequestIsOfferAvailable.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/SendRequestIsOfferAvailableMessage.java similarity index 74% rename from core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/RequestIsOfferAvailable.java rename to core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/SendRequestIsOfferAvailableMessage.java index 7948f058a0..5b2e3108f9 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/RequestIsOfferAvailable.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/SendRequestIsOfferAvailableMessage.java @@ -27,17 +27,21 @@ import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailable import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RequestIsOfferAvailable extends Task { - private static final Logger log = LoggerFactory.getLogger(RequestIsOfferAvailable.class); +public class SendRequestIsOfferAvailableMessage extends Task { + private static final Logger log = LoggerFactory.getLogger(SendRequestIsOfferAvailableMessage.class); + private static final long serialVersionUID = 1L; - public RequestIsOfferAvailable(TaskRunner taskHandler, CheckOfferAvailabilityModel model) { + public SendRequestIsOfferAvailableMessage(TaskRunner taskHandler, CheckOfferAvailabilityModel model) { super(taskHandler, model); } @Override protected void doRun() { try { - model.messageService.sendMessage(model.getPeer(), new RequestIsOfferAvailableMessage(model.offer.getId()), + RequestIsOfferAvailableMessage message = new RequestIsOfferAvailableMessage(model.offer.getId(), model.getPubKeyRing()); + model.messageService.sendEncryptedMessage(model.getPeer(), + model.offer.getPubKeyRing(), + message, new SendMessageListener() { @Override public void handleResult() { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java index 605bbb4c08..515c0e6308 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java @@ -17,7 +17,6 @@ package io.bitsquare.trade.protocol.trade; -import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.SendMessageListener; @@ -49,24 +48,23 @@ import io.bitsquare.trade.states.OffererTradeState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.bitsquare.util.Validator.*; +import static io.bitsquare.util.Validator.checkTradeId; public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtocol, OffererProtocol { private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererProtocol.class); private final BuyerAsOffererTrade buyerAsOffererTrade; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// public BuyerAsOffererProtocol(BuyerAsOffererTrade trade) { super(trade.getProcessModel()); + log.debug("New OffererProtocol " + this); this.buyerAsOffererTrade = trade; - messageHandler = this::handleMessage; - - processModel.getMessageService().addMessageHandler(messageHandler); } @@ -75,16 +73,14 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + public void doApplyMailboxMessage(Message message, Trade trade) { this.trade = trade; - log.debug("setMailboxMessage " + mailboxMessage); - // Find first the actual peer address, as it might have changed in the meantime - findPeerAddress(processModel.tradingPeer.getP2pSigPubKey(), + findPeerAddress(processModel.tradingPeer.getPubKeyRing(), () -> { - if (mailboxMessage instanceof RequestFinalizePayoutTxMessage) { - handle((RequestFinalizePayoutTxMessage) mailboxMessage); + if (message instanceof RequestFinalizePayoutTxMessage) { + handle((RequestFinalizePayoutTxMessage) message); } }, (errorMessage -> { @@ -98,28 +94,33 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc /////////////////////////////////////////////////////////////////////////////////////////// // OpenOffer requests - private void handle(RequestIsOfferAvailableMessage tradeMessage, Peer sender) { + // We get an encrypted message but don't do the signature check as we don't know the peer yet. + // A basic sig check is in done also at decryption time + private void handle(RequestIsOfferAvailableMessage message, Peer sender) { try { - checkTradeId(processModel.getId(), tradeMessage); + checkTradeId(processModel.getId(), message); // We don't store anything in the offererTradeProcessModel as we might be in a trade process and receive that request from another peer who wants - // to take the - // offer - // at the same time + // to take the offer at the same time boolean isOfferOpen = buyerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN; - ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen); - processModel.getMessageService().sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() { - @Override - public void handleResult() { - // Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade. - log.trace("ReportOfferAvailabilityMessage successfully arrived at peer"); - } - @Override - public void handleFault() { - log.warn("Sending ReportOfferAvailabilityMessage failed."); - } - }); + ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen); + processModel.getMessageService().sendEncryptedMessage(sender, + message.getPubKeyRing(), + reportOfferAvailabilityMessage, + new SendMessageListener() { + @Override + public void handleResult() { + // Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade. + log.trace("ReportOfferAvailabilityMessage successfully arrived at peer"); + } + + @Override + public void handleFault() { + // We don't handle the error as we might be in a trade process with another trader + log.warn("Sending ReportOfferAvailabilityMessage failed."); + } + }); } catch (Throwable t) { // We don't handle the error as we might be in a trade process with another trader t.printStackTrace(); @@ -214,29 +215,22 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc // Massage dispatcher /////////////////////////////////////////////////////////////////////////////////////////// - private void handleMessage(Message message, Peer sender) { - log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); - if (message instanceof TradeMessage) { - TradeMessage tradeMessage = (TradeMessage) message; - nonEmptyStringOf(tradeMessage.tradeId); - - if (tradeMessage.tradeId.equals(buyerAsOffererTrade.getId())) { - if (tradeMessage instanceof RequestIsOfferAvailableMessage) { - handle((RequestIsOfferAvailableMessage) tradeMessage, sender); - } - else if (tradeMessage instanceof RequestDepositTxInputsMessage) { - handle((RequestDepositTxInputsMessage) tradeMessage, sender); - } - else if (tradeMessage instanceof RequestPublishDepositTxMessage) { - handle((RequestPublishDepositTxMessage) tradeMessage); - } - else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) { - handle((RequestFinalizePayoutTxMessage) tradeMessage); - } - else { - log.error("Incoming tradeMessage not supported. " + tradeMessage); - } - } + @Override + protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) { + if (tradeMessage instanceof RequestIsOfferAvailableMessage) { + handle((RequestIsOfferAvailableMessage) tradeMessage, sender); + } + else if (tradeMessage instanceof RequestDepositTxInputsMessage) { + handle((RequestDepositTxInputsMessage) tradeMessage, sender); + } + else if (tradeMessage instanceof RequestPublishDepositTxMessage) { + handle((RequestPublishDepositTxMessage) tradeMessage); + } + else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) { + handle((RequestFinalizePayoutTxMessage) tradeMessage); + } + else { + log.error("Incoming decrypted tradeMessage not supported. " + tradeMessage); } } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java index 26c61bfa9e..251961a531 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java @@ -17,7 +17,6 @@ package io.bitsquare.trade.protocol.trade; -import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.trade.BuyerAsTakerTrade; @@ -46,8 +45,6 @@ import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.bitsquare.util.Validator.nonEmptyStringOf; - public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol, TakerProtocol { private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerProtocol.class); @@ -60,11 +57,11 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol public BuyerAsTakerProtocol(BuyerAsTakerTrade trade) { super(trade.getProcessModel()); + log.debug("New SellerAsTakerProtocol " + this); this.buyerAsTakerTrade = trade; - messageHandler = this::handleMessage; - processModel.getMessageService().addMessageHandler(messageHandler); + processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing()); } @@ -73,16 +70,14 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + public void doApplyMailboxMessage(Message message, Trade trade) { this.trade = trade; - log.debug("setMailboxMessage " + mailboxMessage); - // Find first the actual peer address, as it might have changed in the meantime - findPeerAddress(trade.getOffer().getP2pSigPubKey(), + findPeerAddress(trade.getOffer().getPubKeyRing(), () -> { - if (mailboxMessage instanceof RequestFinalizePayoutTxMessage) { - handle((RequestFinalizePayoutTxMessage) mailboxMessage); + if (message instanceof RequestFinalizePayoutTxMessage) { + handle((RequestFinalizePayoutTxMessage) message); } }, (errorMessage -> { @@ -177,23 +172,16 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol // Massage dispatcher /////////////////////////////////////////////////////////////////////////////////////////// - private void handleMessage(Message message, Peer sender) { - log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); - if (message instanceof TradeMessage) { - TradeMessage tradeMessage = (TradeMessage) message; - nonEmptyStringOf(tradeMessage.tradeId); - - if (tradeMessage.tradeId.equals(processModel.getId())) { - if (tradeMessage instanceof RequestPublishDepositTxMessage) { - handle((RequestPublishDepositTxMessage) tradeMessage); - } - else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) { - handle((RequestFinalizePayoutTxMessage) tradeMessage); - } - else { - log.error("Incoming message not supported. " + tradeMessage); - } - } + @Override + protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) { + if (tradeMessage instanceof RequestPublishDepositTxMessage) { + handle((RequestPublishDepositTxMessage) tradeMessage); + } + else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) { + handle((RequestFinalizePayoutTxMessage) tradeMessage); + } + else { + log.error("Incoming message not supported. " + tradeMessage); } } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java index 74a059b858..0c5bf17ef1 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java @@ -23,7 +23,9 @@ import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.taskrunner.Model; -import io.bitsquare.crypto.SignatureService; +import io.bitsquare.crypto.CryptoService; +import io.bitsquare.crypto.KeyRing; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.offer.Offer; import io.bitsquare.p2p.AddressService; @@ -39,8 +41,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.security.PublicKey; - import java.util.List; import javax.annotation.Nullable; @@ -60,10 +60,11 @@ public class ProcessModel implements Model, Serializable { transient private WalletService walletService; transient private TradeWalletService tradeWalletService; transient private BlockChainService blockChainService; - transient private SignatureService signatureService; + transient private CryptoService cryptoService; transient private ArbitrationRepository arbitrationRepository; transient private Offer offer; - private transient User user; + transient private User user; + transient private KeyRing keyRing; // Mutable public final TradingPeer tradingPeer; @@ -93,18 +94,20 @@ public class ProcessModel implements Model, Serializable { WalletService walletService, TradeWalletService tradeWalletService, BlockChainService blockChainService, - SignatureService signatureService, + CryptoService cryptoService, ArbitrationRepository arbitrationRepository, - User user) { + User user, + KeyRing keyRing) { this.offer = offer; this.messageService = messageService; this.addressService = addressService; this.walletService = walletService; this.tradeWalletService = tradeWalletService; this.blockChainService = blockChainService; - this.signatureService = signatureService; + this.cryptoService = cryptoService; this.arbitrationRepository = arbitrationRepository; this.user = user; + this.keyRing = keyRing; } @@ -128,10 +131,6 @@ public class ProcessModel implements Model, Serializable { return blockChainService; } - public SignatureService getSignatureService() { - return signatureService; - } - public byte[] getArbitratorPubKey() { return arbitrationRepository.getDefaultArbitrator().getPubKey(); } @@ -179,10 +178,6 @@ public class ProcessModel implements Model, Serializable { return user.getAccountId(); } - public PublicKey getP2pSigPubKey() { - return user.getP2pSigPubKey(); - } - public byte[] getRegistrationPubKey() { return walletService.getRegistrationAddressEntry().getPubKey(); } @@ -195,10 +190,6 @@ public class ProcessModel implements Model, Serializable { return getAddressEntry().getPubKey(); } - public PublicKey getP2pEncryptPubKey() { - return user.getP2pEncryptPubKey(); - } - @Nullable public List getConnectedOutputsForAllInputs() { return connectedOutputsForAllInputs; @@ -263,4 +254,16 @@ public class ProcessModel implements Model, Serializable { public AddressService getAddressService() { return addressService; } + + public PubKeyRing getPubKeyRing() { + return keyRing.getPubKeyRing(); + } + + public CryptoService getCryptoService() { + return cryptoService; + } + + public void setCryptoService(CryptoService cryptoService) { + this.cryptoService = cryptoService; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java index 0b5008fada..34498ef10a 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java @@ -17,7 +17,6 @@ package io.bitsquare.trade.protocol.trade; -import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.SendMessageListener; @@ -49,7 +48,7 @@ import io.bitsquare.trade.states.OffererTradeState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.bitsquare.util.Validator.*; +import static io.bitsquare.util.Validator.checkTradeId; public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol { private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class); @@ -63,11 +62,9 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt public SellerAsOffererProtocol(SellerAsOffererTrade trade) { super(trade.getProcessModel()); + log.debug("New OffererProtocol " + this); this.sellerAsOffererTrade = trade; - messageHandler = this::handleMessage; - - processModel.getMessageService().addMessageHandler(messageHandler); } @@ -76,22 +73,21 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + public void doApplyMailboxMessage(Message message, Trade trade) { this.trade = trade; - log.debug("setMailboxMessage " + mailboxMessage); - // Find first the actual peer address, as it might have changed in the meantime - if (mailboxMessage instanceof PayoutTxFinalizedMessage) { - handle((PayoutTxFinalizedMessage) mailboxMessage); + if (message instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) message); } else { - findPeerAddress(processModel.tradingPeer.getP2pSigPubKey(), + // Find first the actual peer address, as it might have changed in the meantime + findPeerAddress(processModel.tradingPeer.getPubKeyRing(), () -> { - if (mailboxMessage instanceof FiatTransferStartedMessage) { - handle((FiatTransferStartedMessage) mailboxMessage); + if (message instanceof FiatTransferStartedMessage) { + handle((FiatTransferStartedMessage) message); } - else if (mailboxMessage instanceof DepositTxPublishedMessage) { - handle((DepositTxPublishedMessage) mailboxMessage); + else if (message instanceof DepositTxPublishedMessage) { + handle((DepositTxPublishedMessage) message); } }, (errorMessage -> { @@ -109,29 +105,31 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt // IsOfferAvailable /////////////////////////////////////////////////////////////////////////////////////////// - private void handle(RequestIsOfferAvailableMessage tradeMessage, Peer sender) { + private void handle(RequestIsOfferAvailableMessage message, Peer sender) { try { - checkTradeId(processModel.getId(), tradeMessage); + checkTradeId(processModel.getId(), message); // We don't store anything in the offererTradeProcessModel as we might be in a trade process and receive that request from another peer who wants - // to take the - // offer - // at the same time + // to take the offer at the same time boolean isOfferOpen = sellerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN; ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen); - processModel.getMessageService().sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() { - @Override - public void handleResult() { - // Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade. - log.trace("ReportOfferAvailabilityMessage successfully arrived at peer"); - } + processModel.getMessageService().sendEncryptedMessage(sender, + message.getPubKeyRing(), + reportOfferAvailabilityMessage, + new SendMessageListener() { + @Override + public void handleResult() { + // Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade. + log.trace("ReportOfferAvailabilityMessage successfully arrived at peer"); + } - @Override - public void handleFault() { - log.warn("Sending ReportOfferAvailabilityMessage failed."); - } - }); + @Override + public void handleFault() { + // We don't handle the error as we might be in a trade process with another trader + log.warn("Sending ReportOfferAvailabilityMessage failed."); + } + }); } catch (Throwable t) { // We don't handle the error as we might be in a trade process with another trader t.printStackTrace(); @@ -239,31 +237,25 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt // Massage dispatcher /////////////////////////////////////////////////////////////////////////////////////////// - private void handleMessage(Message message, Peer sender) { - log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); - if (message instanceof TradeMessage) { - TradeMessage tradeMessage = (TradeMessage) message; - nonEmptyStringOf(tradeMessage.tradeId); - if (tradeMessage.tradeId.equals(sellerAsOffererTrade.getId())) { - if (tradeMessage instanceof RequestIsOfferAvailableMessage) { - handle((RequestIsOfferAvailableMessage) tradeMessage, sender); - } - else if (tradeMessage instanceof RequestPayDepositMessage) { - handle((RequestPayDepositMessage) tradeMessage, sender); - } - else if (tradeMessage instanceof DepositTxPublishedMessage) { - handle((DepositTxPublishedMessage) tradeMessage); - } - else if (tradeMessage instanceof FiatTransferStartedMessage) { - handle((FiatTransferStartedMessage) tradeMessage); - } - else if (tradeMessage instanceof PayoutTxFinalizedMessage) { - handle((PayoutTxFinalizedMessage) tradeMessage); - } - else { - log.error("Incoming tradeMessage not supported. " + tradeMessage); - } - } + @Override + protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) { + if (tradeMessage instanceof RequestIsOfferAvailableMessage) { + handle((RequestIsOfferAvailableMessage) tradeMessage, sender); + } + else if (tradeMessage instanceof RequestPayDepositMessage) { + handle((RequestPayDepositMessage) tradeMessage, sender); + } + else if (tradeMessage instanceof DepositTxPublishedMessage) { + handle((DepositTxPublishedMessage) tradeMessage); + } + else if (tradeMessage instanceof FiatTransferStartedMessage) { + handle((FiatTransferStartedMessage) tradeMessage); + } + else if (tradeMessage instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) tradeMessage); + } + else { + log.error("Incoming tradeMessage not supported. " + tradeMessage); } } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java index 5330a3e800..dba902e37a 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java @@ -17,7 +17,6 @@ package io.bitsquare.trade.protocol.trade; -import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.trade.SellerAsTakerTrade; @@ -48,8 +47,6 @@ import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.bitsquare.util.Validator.nonEmptyStringOf; - public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtocol, TakerProtocol { private static final Logger log = LoggerFactory.getLogger(SellerAsTakerProtocol.class); @@ -62,11 +59,11 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc public SellerAsTakerProtocol(SellerAsTakerTrade trade) { super(trade.getProcessModel()); + log.debug("New SellerAsTakerProtocol " + this); this.sellerAsTakerTrade = trade; - messageHandler = this::handleMessage; - processModel.getMessageService().addMessageHandler(messageHandler); + processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing()); } @@ -75,23 +72,21 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + public void doApplyMailboxMessage(Message message, Trade trade) { this.trade = trade; - log.debug("setMailboxMessage " + mailboxMessage); - - // Find first the actual peer address, as it might have changed in the meantime - if (mailboxMessage instanceof PayoutTxFinalizedMessage) { - handle((PayoutTxFinalizedMessage) mailboxMessage); + if (message instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) message); } else { - findPeerAddress(trade.getOffer().getP2pSigPubKey(), + // Find first the actual peer address, as it might have changed in the meantime + findPeerAddress(trade.getOffer().getPubKeyRing(), () -> { - if (mailboxMessage instanceof FiatTransferStartedMessage) { - handle((FiatTransferStartedMessage) mailboxMessage); + if (message instanceof FiatTransferStartedMessage) { + handle((FiatTransferStartedMessage) message); } - else if (mailboxMessage instanceof DepositTxPublishedMessage) { - handle((DepositTxPublishedMessage) mailboxMessage); + else if (message instanceof DepositTxPublishedMessage) { + handle((DepositTxPublishedMessage) message); } }, (errorMessage -> { @@ -217,30 +212,22 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc // Massage dispatcher /////////////////////////////////////////////////////////////////////////////////////////// - private void handleMessage(Message message, Peer sender) { - log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); - if (message instanceof TradeMessage) { - TradeMessage tradeMessage = (TradeMessage) message; - nonEmptyStringOf(tradeMessage.tradeId); - - if (tradeMessage.tradeId.equals(processModel.getId())) { - if (tradeMessage instanceof RequestPayDepositMessage) { - handle((RequestPayDepositMessage) tradeMessage); - } - else if (tradeMessage instanceof DepositTxPublishedMessage) { - handle((DepositTxPublishedMessage) tradeMessage); - } - else if (tradeMessage instanceof FiatTransferStartedMessage) { - handle((FiatTransferStartedMessage) tradeMessage); - } - else if (tradeMessage instanceof PayoutTxFinalizedMessage) { - handle((PayoutTxFinalizedMessage) tradeMessage); - } - else { - - log.error("Incoming message not supported. " + tradeMessage); - } - } + @Override + protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) { + if (tradeMessage instanceof RequestPayDepositMessage) { + handle((RequestPayDepositMessage) tradeMessage); + } + else if (tradeMessage instanceof DepositTxPublishedMessage) { + handle((DepositTxPublishedMessage) tradeMessage); + } + else if (tradeMessage instanceof FiatTransferStartedMessage) { + handle((FiatTransferStartedMessage) tradeMessage); + } + else if (tradeMessage instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) tradeMessage); + } + else { + log.error("Incoming message not supported. " + tradeMessage); } } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java index 89fb767a2a..dbd8fc10f2 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java @@ -19,51 +19,67 @@ package io.bitsquare.trade.protocol.trade; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; -import io.bitsquare.p2p.MailboxMessage; -import io.bitsquare.p2p.MessageHandler; +import io.bitsquare.crypto.MessageWithPubKey; +import io.bitsquare.crypto.PubKeyRing; +import io.bitsquare.p2p.DecryptedMessageHandler; +import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.GetPeerAddressListener; import io.bitsquare.trade.OffererTrade; import io.bitsquare.trade.TakerTrade; import io.bitsquare.trade.Trade; +import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.TakerTradeState; import org.bitcoinj.utils.Threading; -import java.security.PublicKey; - import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static io.bitsquare.util.Validator.nonEmptyStringOf; + public abstract class TradeProtocol { private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class); private static final long TIMEOUT = 10000; protected final ProcessModel processModel; - protected MessageHandler messageHandler; + private DecryptedMessageHandler decryptedMessageHandler; protected Timer timeoutTimer; protected Trade trade; public TradeProtocol(ProcessModel processModel) { this.processModel = processModel; + + decryptedMessageHandler = this::handleMessageWithPubKey; + + processModel.getMessageService().addDecryptedMessageHandler(decryptedMessageHandler); } public void cleanup() { log.debug("cleanup " + this); stopTimeout(); - processModel.getMessageService().removeMessageHandler(messageHandler); + if (decryptedMessageHandler != null) + processModel.getMessageService().removeDecryptedMessageHandler(decryptedMessageHandler); } - abstract public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade); + public void applyMailboxMessage(MessageWithPubKey messageWithPubKey, Trade trade) { + log.debug("applyMailboxMessage " + messageWithPubKey.getMessage()); + if (messageWithPubKey.getSignaturePubKey().equals(processModel.tradingPeer.getPubKeyRing().getMsgSignaturePubKey())) + doApplyMailboxMessage(messageWithPubKey.getMessage(), trade); + else + log.error("SignaturePubKey in message does not match the SignaturePubKey we have stored to that trading peer."); + } - protected void findPeerAddress(PublicKey p2pSigPubKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { + protected abstract void doApplyMailboxMessage(Message message, Trade trade); + + protected void findPeerAddress(PubKeyRing pubKeyRing, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { try { - processModel.getAddressService().findPeerAddress(p2pSigPubKey, new GetPeerAddressListener() { + processModel.getAddressService().findPeerAddress(pubKeyRing, new GetPeerAddressListener() { @Override public void onResult(Peer peer) { trade.setTradingPeer(peer); @@ -80,6 +96,28 @@ public abstract class TradeProtocol { } } + protected void handleMessageWithPubKey(MessageWithPubKey messageWithPubKey, Peer sender) { + // We check the sig only as soon we have stored the peers pubKeyRing. + if (processModel.tradingPeer.getPubKeyRing() != null && + !messageWithPubKey.getSignaturePubKey().equals(processModel.tradingPeer.getPubKeyRing().getMsgSignaturePubKey())) { + log.error("Signature used in seal message does not match the one stored with that trade for the trading peer."); + } + else { + Message message = messageWithPubKey.getMessage(); + log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); + if (message instanceof TradeMessage) { + TradeMessage tradeMessage = (TradeMessage) message; + nonEmptyStringOf(tradeMessage.tradeId); + + if (tradeMessage.tradeId.equals(processModel.getId())) { + doHandleDecryptedMessage(tradeMessage, sender); + } + } + } + } + + protected abstract void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender); + public void checkPayoutTxTimeLock(Trade trade) { this.trade = trade; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradingPeer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradingPeer.java index 8a939f5c74..7f607229cc 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradingPeer.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradingPeer.java @@ -17,6 +17,7 @@ package io.bitsquare.trade.protocol.trade; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.fiat.FiatAccount; import org.bitcoinj.core.Transaction; @@ -26,8 +27,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.security.PublicKey; - import java.util.List; import org.slf4j.Logger; @@ -41,9 +40,6 @@ public class TradingPeer implements Serializable { // Mutable private String accountId; - private PublicKey p2pSigPubKey; - private PublicKey p2pEncryptPubKey; - private byte[] tradeWalletPubKey; private FiatAccount fiatAccount; private Transaction preparedDepositTx; private List connectedOutputsForAllInputs; @@ -54,6 +50,8 @@ public class TradingPeer implements Serializable { private String contractAsJson; private String contractSignature; private byte[] signature; + private PubKeyRing pubKeyRing; + private byte[] tradeWalletPubKey; /////////////////////////////////////////////////////////////////////////////////////////// @@ -81,23 +79,7 @@ public class TradingPeer implements Serializable { public void setAccountId(String accountId) { this.accountId = accountId; } - - public PublicKey getP2pSigPubKey() { - return p2pSigPubKey; - } - - public void setP2pSigPubKey(PublicKey p2pSigPubKey) { - this.p2pSigPubKey = p2pSigPubKey; - } - - public PublicKey getP2pEncryptPubKey() { - return p2pEncryptPubKey; - } - - public void setP2pEncryptPubKey(PublicKey p2pEncryptPubKey) { - this.p2pEncryptPubKey = p2pEncryptPubKey; - } - + public byte[] getTradeWalletPubKey() { return tradeWalletPubKey; } @@ -105,7 +87,7 @@ public class TradingPeer implements Serializable { public void setTradeWalletPubKey(byte[] tradeWalletPubKey) { this.tradeWalletPubKey = tradeWalletPubKey; } - + public FiatAccount getFiatAccount() { return fiatAccount; } @@ -185,4 +167,12 @@ public class TradingPeer implements Serializable { public byte[] getSignature() { return signature; } + + public PubKeyRing getPubKeyRing() { + return pubKeyRing; + } + + public void setPubKeyRing(PubKeyRing pubKeyRing) { + this.pubKeyRing = pubKeyRing; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestDepositTxInputsMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestDepositTxInputsMessage.java index 5b6aa58bbf..610eaa570a 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestDepositTxInputsMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestDepositTxInputsMessage.java @@ -17,6 +17,8 @@ package io.bitsquare.trade.protocol.trade.messages; +import io.bitsquare.crypto.PubKeyRing; + import org.bitcoinj.core.Coin; import java.io.Serializable; @@ -29,11 +31,14 @@ public class RequestDepositTxInputsMessage extends TradeMessage implements Seria private static final long serialVersionUID = 1L; public final Coin tradeAmount; + public final PubKeyRing sellerPubKeyRing; public final String sellerOfferFeeTxId; public final byte[] sellerTradeWalletPubKey; - public RequestDepositTxInputsMessage(String tradeId, String sellerOfferFeeTxId, Coin tradeAmount, byte[] sellerTradeWalletPubKey) { + public RequestDepositTxInputsMessage(String tradeId, PubKeyRing sellerPubKeyRing, String sellerOfferFeeTxId, Coin tradeAmount, byte[] + sellerTradeWalletPubKey) { super(tradeId); + this.sellerPubKeyRing = sellerPubKeyRing; this.sellerOfferFeeTxId = sellerOfferFeeTxId; this.tradeAmount = tradeAmount; this.sellerTradeWalletPubKey = sellerTradeWalletPubKey; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPayDepositMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPayDepositMessage.java index 2bc444c84a..57adfdd351 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPayDepositMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPayDepositMessage.java @@ -17,6 +17,7 @@ package io.bitsquare.trade.protocol.trade.messages; +import io.bitsquare.crypto.PubKeyRing; import io.bitsquare.fiat.FiatAccount; import org.bitcoinj.core.Coin; @@ -24,8 +25,6 @@ import org.bitcoinj.core.TransactionOutput; import java.io.Serializable; -import java.security.PublicKey; - import java.util.List; import javax.annotation.concurrent.Immutable; @@ -38,8 +37,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab public final List buyerConnectedOutputsForAllInputs; public final List buyerOutputs; public final byte[] buyerTradeWalletPubKey; - public final PublicKey buyerP2pSigPublicKey; - public final PublicKey buyerP2pEncryptPublicKey; + public final PubKeyRing buyerPubKeyRing; public final FiatAccount buyerFiatAccount; public final String buyerAccountId; public final Coin tradeAmount; @@ -49,14 +47,12 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab List buyerConnectedOutputsForAllInputs, List buyerOutputs, byte[] buyerTradeWalletPubKey, - PublicKey buyerP2pSigPublicKey, - PublicKey buyerP2pEncryptPublicKey, + PubKeyRing buyerPubKeyRing, FiatAccount buyerFiatAccount, String buyerAccountId) { super(tradeId); this.tradeAmount = tradeAmount; - this.buyerP2pSigPublicKey = buyerP2pSigPublicKey; - this.buyerP2pEncryptPublicKey = buyerP2pEncryptPublicKey; + this.buyerPubKeyRing = buyerPubKeyRing; this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs; this.buyerOutputs = buyerOutputs; this.buyerTradeWalletPubKey = buyerTradeWalletPubKey; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPublishDepositTxMessage.java index 556118e7a5..24f55ffd27 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestPublishDepositTxMessage.java @@ -24,8 +24,6 @@ import org.bitcoinj.core.TransactionOutput; import java.io.Serializable; -import java.security.PublicKey; - import java.util.List; import javax.annotation.concurrent.Immutable; @@ -37,8 +35,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri public final FiatAccount sellerFiatAccount; public final String sellerAccountId; - public final PublicKey sellerP2pSigPublicKey; - public final PublicKey sellerP2pEncryptPublicKey; public final String sellerContractAsJson; public final String sellerContractSignature; public final String sellerPayoutAddressString; @@ -50,8 +46,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri FiatAccount sellerFiatAccount, String sellerAccountId, byte[] sellerTradeWalletPubKey, - PublicKey sellerP2pSigPublicKey, - PublicKey sellerP2pEncryptPublicKey, String sellerContractAsJson, String sellerContractSignature, String sellerPayoutAddressString, @@ -61,8 +55,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri this.sellerFiatAccount = sellerFiatAccount; this.sellerAccountId = sellerAccountId; this.sellerTradeWalletPubKey = sellerTradeWalletPubKey; - this.sellerP2pSigPublicKey = sellerP2pSigPublicKey; - this.sellerP2pEncryptPublicKey = sellerP2pEncryptPublicKey; this.sellerContractAsJson = sellerContractAsJson; this.sellerContractSignature = sellerContractSignature; this.sellerPayoutAddressString = sellerPayoutAddressString; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestDepositTxInputsMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestDepositTxInputsMessage.java index f25a8ab70f..5499199119 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestDepositTxInputsMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestDepositTxInputsMessage.java @@ -43,8 +43,9 @@ public class ProcessRequestDepositTxInputsMessage extends TradeTask { checkTradeId(processModel.getId(), message); checkNotNull(message); - trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount))); + processModel.tradingPeer.setPubKeyRing(checkNotNull(message.sellerPubKeyRing)); processModel.setTakeOfferFeeTxId(nonEmptyStringOf(message.sellerOfferFeeTxId)); + trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount))); processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey)); complete(); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestPublishDepositTxMessage.java index 130c4b41c0..a62e5bc349 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessRequestPublishDepositTxMessage.java @@ -45,9 +45,7 @@ public class ProcessRequestPublishDepositTxMessage extends TradeTask { processModel.tradingPeer.setFiatAccount(checkNotNull(message.sellerFiatAccount)); processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.sellerAccountId)); - processModel.tradingPeer.setP2pSigPubKey(checkNotNull(message.sellerP2pSigPublicKey)); processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey)); - processModel.tradingPeer.setP2pEncryptPubKey(checkNotNull(message.sellerP2pEncryptPublicKey)); processModel.tradingPeer.setContractAsJson(nonEmptyStringOf(message.sellerContractAsJson)); processModel.tradingPeer.setContractSignature(nonEmptyStringOf(message.sellerContractSignature)); processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddressString)); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendDepositTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendDepositTxPublishedMessage.java index 8d09b29ba1..f8416fceea 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendDepositTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendDepositTxPublishedMessage.java @@ -39,24 +39,28 @@ public class SendDepositTxPublishedMessage extends TradeTask { try { DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(), trade.getDepositTx()); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() { - @Override - public void handleResult() { - log.trace("DepositTxPublishedMessage successfully arrived at peer"); + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), + tradeMessage, + new SendMessageListener() { + @Override + public void handleResult() { + log.trace("DepositTxPublishedMessage successfully arrived at peer"); - complete(); - } + complete(); + } - @Override - public void handleFault() { - appendToErrorMessage("Sending DepositTxPublishedMessage failed"); - trade.setErrorMessage(errorMessage); + @Override + public void handleFault() { + appendToErrorMessage("Sending DepositTxPublishedMessage failed"); + trade.setErrorMessage(errorMessage); - StateUtil.setSendFailedState(trade); + StateUtil.setSendFailedState(trade); - failed(); - } - }); + failed(); + } + }); } catch (Throwable t) { t.printStackTrace(); trade.setThrowable(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java index 6b9376f847..e3ee5a0eb7 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendFiatTransferStartedMessage.java @@ -45,10 +45,10 @@ public class SendFiatTransferStartedMessage extends TradeTask { processModel.getAddressEntry().getAddressString() ); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, - processModel.tradingPeer.getP2pSigPubKey(), - processModel.tradingPeer.getP2pEncryptPubKey(), - processModel.getRegistrationKeyPair(), + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), + tradeMessage, new SendMessageListener() { @Override public void handleResult() { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java index 4767e11435..e363b0d14f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendPayoutTxFinalizedMessage.java @@ -38,11 +38,10 @@ public class SendPayoutTxFinalizedMessage extends TradeTask { protected void doRun() { try { PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx()); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), tradeMessage, - processModel.tradingPeer.getP2pSigPubKey(), - processModel.tradingPeer.getP2pEncryptPubKey(), - processModel.getRegistrationKeyPair(), new SendMessageListener() { @Override public void handleResult() { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendRequestPayDepositMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendRequestPayDepositMessage.java index 97d1fac0e3..7bc5667ca1 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendRequestPayDepositMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SendRequestPayDepositMessage.java @@ -43,27 +43,30 @@ public class SendRequestPayDepositMessage extends TradeTask { processModel.getConnectedOutputsForAllInputs(), processModel.getOutputs(), processModel.getTradeWalletPubKey(), - processModel.getP2pSigPubKey(), - processModel.getP2pEncryptPubKey(), + processModel.getPubKeyRing(), processModel.getFiatAccount(), processModel.getAccountId()); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() { - @Override - public void handleResult() { - log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer"); - complete(); - } + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), + tradeMessage, + new SendMessageListener() { + @Override + public void handleResult() { + log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer"); + complete(); + } - @Override - public void handleFault() { - appendToErrorMessage("Sending RequestTakerDepositPaymentMessage failed"); - trade.setErrorMessage(errorMessage); - StateUtil.setOfferOpenState(trade); - StateUtil.setSendFailedState(trade); - failed(); - } - }); + @Override + public void handleFault() { + appendToErrorMessage("Sending RequestTakerDepositPaymentMessage failed"); + trade.setErrorMessage(errorMessage); + StateUtil.setOfferOpenState(trade); + StateUtil.setSendFailedState(trade); + failed(); + } + }); } catch (Throwable t) { t.printStackTrace(); trade.setThrowable(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/VerifyAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/VerifyAndSignContract.java index c2ee496b44..400719068e 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/VerifyAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/VerifyAndSignContract.java @@ -45,10 +45,10 @@ public class VerifyAndSignContract extends TradeTask { processModel.tradingPeer.getAccountId(), processModel.getFiatAccount(), processModel.tradingPeer.getFiatAccount(), - processModel.getP2pSigPubKey(), - processModel.tradingPeer.getP2pSigPubKey()); + processModel.getPubKeyRing(), + processModel.tradingPeer.getPubKeyRing()); String contractAsJson = Utilities.objectToJson(contract); - String signature = processModel.getSignatureService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson); + String signature = processModel.getCryptoService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson); trade.setContract(contract); trade.setContractAsJson(contractAsJson); trade.setBuyerContractSignature(signature); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignContract.java index 285a79de19..4f18737230 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/CreateAndSignContract.java @@ -45,10 +45,10 @@ public class CreateAndSignContract extends TradeTask { processModel.getAccountId(), processModel.tradingPeer.getFiatAccount(), processModel.getFiatAccount(), - processModel.tradingPeer.getP2pSigPubKey(), - processModel.getP2pSigPubKey()); + processModel.tradingPeer.getPubKeyRing(), + processModel.getPubKeyRing()); String contractAsJson = Utilities.objectToJson(contract); - String signature = processModel.getSignatureService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson); + String signature = processModel.getCryptoService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson); trade.setContract(contract); trade.setContractAsJson(contractAsJson); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessRequestPayDepositMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessRequestPayDepositMessage.java index 301aee486a..3667674fa1 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessRequestPayDepositMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessRequestPayDepositMessage.java @@ -46,8 +46,7 @@ public class ProcessRequestPayDepositMessage extends TradeTask { checkArgument(message.buyerConnectedOutputsForAllInputs.size() > 0); processModel.tradingPeer.setOutputs(checkNotNull(message.buyerOutputs)); processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.buyerTradeWalletPubKey)); - processModel.tradingPeer.setP2pSigPubKey(checkNotNull(message.buyerP2pSigPublicKey)); - processModel.tradingPeer.setP2pEncryptPubKey(checkNotNull(message.buyerP2pEncryptPublicKey)); + processModel.tradingPeer.setPubKeyRing(checkNotNull(message.buyerPubKeyRing)); processModel.tradingPeer.setFiatAccount(checkNotNull(message.buyerFiatAccount)); processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.buyerAccountId)); trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount))); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestDepositTxInputsMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestDepositTxInputsMessage.java index 65ed9e40da..4654cc80d6 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestDepositTxInputsMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestDepositTxInputsMessage.java @@ -44,37 +44,42 @@ public class SendRequestDepositTxInputsMessage extends TradeTask { assert processModel.getTakeOfferFeeTx() != null; RequestDepositTxInputsMessage message = new RequestDepositTxInputsMessage( processModel.getId(), + processModel.getPubKeyRing(), processModel.getTakeOfferFeeTx().getHashAsString(), trade.getTradeAmount(), processModel.getTradeWalletPubKey()); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), message, new SendMessageListener() { - @Override - public void handleResult() { - log.trace("Sending TakeOfferFeePayedMessage succeeded."); - complete(); - } + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), + message, + new SendMessageListener() { + @Override + public void handleResult() { + log.trace("Sending TakeOfferFeePayedMessage succeeded."); + complete(); + } - @Override - public void handleFault() { - log.warn("Sending TakeOfferFeePayedMessage failed. We try a second time."); - // Take offer fee is already paid, so we need to try to get that trade to succeed. - // We try to repeat once and if that fails as well we persist the state for a later retry. - if (retryCounter == 0) { - retryCounter++; - Threading.USER_THREAD.execute(SendRequestDepositTxInputsMessage.this::doRun); - } - else { - appendToErrorMessage("Sending TakeOfferFeePayedMessage to offerer failed. Maybe the network connection was " + - "lost or the offerer lost his connection. We persisted the state of the trade, please try again later " + - "or cancel that trade."); + @Override + public void handleFault() { + log.warn("Sending TakeOfferFeePayedMessage failed. We try a second time."); + // Take offer fee is already paid, so we need to try to get that trade to succeed. + // We try to repeat once and if that fails as well we persist the state for a later retry. + if (retryCounter == 0) { + retryCounter++; + Threading.USER_THREAD.execute(SendRequestDepositTxInputsMessage.this::doRun); + } + else { + appendToErrorMessage("Sending TakeOfferFeePayedMessage to offerer failed. Maybe the network connection was " + + "lost or the offerer lost his connection. We persisted the state of the trade, please try again later " + + "or cancel that trade."); - trade.setErrorMessage(errorMessage); - StateUtil.setSendFailedState(trade); - failed(); - } - } - }); + trade.setErrorMessage(errorMessage); + StateUtil.setSendFailedState(trade); + failed(); + } + } + }); } catch (Throwable t) { t.printStackTrace(); trade.setThrowable(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestFinalizePayoutTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestFinalizePayoutTxMessage.java index 625f01138c..72936edeb8 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestFinalizePayoutTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestFinalizePayoutTxMessage.java @@ -48,11 +48,10 @@ public class SendRequestFinalizePayoutTxMessage extends TradeTask { trade.getLockTime() ); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), message, - processModel.tradingPeer.getP2pSigPubKey(), - processModel.tradingPeer.getP2pEncryptPubKey(), - processModel.getRegistrationKeyPair(), new SendMessageListener() { @Override public void handleResult() { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestPublishDepositTxMessage.java index a9afc963f3..3e21cf2b74 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendRequestPublishDepositTxMessage.java @@ -42,8 +42,6 @@ public class SendRequestPublishDepositTxMessage extends TradeTask { processModel.getFiatAccount(), processModel.getAccountId(), processModel.getTradeWalletPubKey(), - processModel.getP2pSigPubKey(), - processModel.getP2pEncryptPubKey(), trade.getContractAsJson(), trade.getSellerContractSignature(), processModel.getAddressEntry().getAddressString(), @@ -51,22 +49,26 @@ public class SendRequestPublishDepositTxMessage extends TradeTask { processModel.getConnectedOutputsForAllInputs() ); - processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() { - @Override - public void handleResult() { - complete(); - } + processModel.getMessageService().sendEncryptedMessage( + trade.getTradingPeer(), + processModel.tradingPeer.getPubKeyRing(), + tradeMessage, + new SendMessageListener() { + @Override + public void handleResult() { + complete(); + } - @Override - public void handleFault() { - appendToErrorMessage("Sending RequestOffererPublishDepositTxMessage failed"); - trade.setErrorMessage(errorMessage); + @Override + public void handleFault() { + appendToErrorMessage("Sending RequestOffererPublishDepositTxMessage failed"); + trade.setErrorMessage(errorMessage); - StateUtil.setSendFailedState(trade); + StateUtil.setSendFailedState(trade); - failed(); - } - }); + failed(); + } + }); } catch (Throwable t) { t.printStackTrace(); trade.setThrowable(t); diff --git a/core/src/main/java/io/bitsquare/user/User.java b/core/src/main/java/io/bitsquare/user/User.java index 37b2165b37..79c355d803 100644 --- a/core/src/main/java/io/bitsquare/user/User.java +++ b/core/src/main/java/io/bitsquare/user/User.java @@ -17,18 +17,12 @@ package io.bitsquare.user; -import io.bitsquare.crypto.EncryptionService; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.storage.Storage; -import com.google.common.base.Throwables; - import java.io.Serializable; -import java.security.KeyPair; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.util.ArrayList; import java.util.List; @@ -56,28 +50,24 @@ public class User implements Serializable { transient private static final Logger log = LoggerFactory.getLogger(User.class); - transient private Storage storage; + // Transient immutable fields + transient final private Storage storage; // Persisted fields - private KeyPair p2pSigKeyPair; - private KeyPair p2pEncryptKeyPair; private String accountID; private List fiatAccounts = new ArrayList<>(); private FiatAccount currentFiatAccount; // Observable wrappers - final transient private ObservableList fiatAccountsObservableList = FXCollections.observableArrayList(fiatAccounts); - final transient private ObjectProperty currentFiatAccountProperty = new SimpleObjectProperty<>(currentFiatAccount); + transient final private ObservableList fiatAccountsObservableList = FXCollections.observableArrayList(fiatAccounts); + transient final private ObjectProperty currentFiatAccountProperty = new SimpleObjectProperty<>(currentFiatAccount); @Inject - public User(Storage storage, EncryptionService encryptionService) { + public User(Storage storage) throws NoSuchAlgorithmException { this.storage = storage; - EncryptionService encryptionService1 = encryptionService; User persisted = storage.initAndGetPersisted(this); if (persisted != null) { - p2pSigKeyPair = persisted.getP2pSigKeyPair(); - p2pEncryptKeyPair = persisted.getP2pEncryptKeyPair(); accountID = persisted.getAccountId(); fiatAccounts = new ArrayList<>(persisted.getFiatAccounts()); @@ -86,17 +76,6 @@ public class User implements Serializable { currentFiatAccount = persisted.getCurrentFiatAccount(); currentFiatAccountProperty.set(currentFiatAccount); } - else { - // First time we create key pairs - try { - p2pSigKeyPair = encryptionService.getGeneratedDSAKeyPair(); - p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - log.error(e.getMessage()); - Throwables.propagate(e); - } - } storage.queueUpForSave(); // Use that to guarantee update of the serializable field and to make a storage update in case of a change fiatAccountsObservableList.addListener((ListChangeListener) change -> { @@ -111,6 +90,7 @@ public class User implements Serializable { // for unit tests public User() { + this.storage = null; } @@ -196,26 +176,6 @@ public class User implements Serializable { return getAccountId() != null; } - public KeyPair getP2pSigKeyPair() { - return p2pSigKeyPair; - } - - public PublicKey getP2pSigPubKey() { - return p2pSigKeyPair.getPublic(); - } - - public PublicKey getP2pEncryptPubKey() { - return p2pEncryptKeyPair.getPublic(); - } - - public PrivateKey getP2pEncryptPrivateKey() { - return p2pEncryptKeyPair.getPrivate(); - } - - private KeyPair getP2pEncryptKeyPair() { - return p2pEncryptKeyPair; - } - private List getFiatAccounts() { return fiatAccounts; } @@ -231,5 +191,4 @@ public class User implements Serializable { public ObservableList fiatAccountsObservableList() { return fiatAccountsObservableList; } - } diff --git a/core/src/main/java/io/bitsquare/util/Utilities.java b/core/src/main/java/io/bitsquare/util/Utilities.java index 0a01eca58b..f2f2baa97b 100644 --- a/core/src/main/java/io/bitsquare/util/Utilities.java +++ b/core/src/main/java/io/bitsquare/util/Utilities.java @@ -26,6 +26,7 @@ import java.awt.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; @@ -200,6 +201,15 @@ public class Utilities { return result; } + public static void deleteDirectory(File file) throws IOException { + if (file.isDirectory()) { + for (File c : file.listFiles()) + deleteDirectory(c); + } + if (!file.delete()) + throw new FileNotFoundException("Failed to delete file: " + file); + } + private static void printElapsedTime(String msg) { if (!msg.isEmpty()) { msg += " / "; diff --git a/core/src/test/java/io/bitsquare/crypto/CryptoServiceTests.java b/core/src/test/java/io/bitsquare/crypto/CryptoServiceTests.java new file mode 100644 index 0000000000..70c34b8a5e --- /dev/null +++ b/core/src/test/java/io/bitsquare/crypto/CryptoServiceTests.java @@ -0,0 +1,81 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import io.bitsquare.p2p.MailboxMessage; +import io.bitsquare.util.Utilities; + +import java.io.File; +import java.io.IOException; + +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.*; + +public class CryptoServiceTests { + private static final Logger log = LoggerFactory.getLogger(CryptoServiceTests.class); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private PubKeyRing pubKeyRing; + private KeyRing keyRing; + private File dir = new File("/tmp/bitsquare_tests"); + + @Before + public void setup() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, CryptoException { + dir.mkdir(); + KeyStorage keyStorage = new KeyStorage(dir); + keyRing = new KeyRing(keyStorage); + pubKeyRing = keyRing.getPubKeyRing(); + } + + @After + public void tearDown() throws IOException { + Utilities.deleteDirectory(dir); + } + + @Test + public void testDecryptAndVerifyMessage() throws CryptoException { + CryptoService encryptionService = new CryptoService<>(keyRing); + TestMessage data = new TestMessage("test"); + SealedAndSignedMessage encrypted = encryptionService.encryptAndSignMessage(pubKeyRing, data); + MessageWithPubKey decrypted = encryptionService.decryptAndVerifyMessage(encrypted); + assertEquals("", data.data, ((TestMessage) decrypted.getMessage()).data); + } + +} + +class TestMessage implements MailboxMessage { + public String data = "test"; + + public TestMessage(String data) { + this.data = data; + } +} diff --git a/core/src/test/java/io/bitsquare/crypto/EncryptionServiceTests.java b/core/src/test/java/io/bitsquare/crypto/EncryptionServiceTests.java deleted file mode 100644 index 1a6bf3c20c..0000000000 --- a/core/src/test/java/io/bitsquare/crypto/EncryptionServiceTests.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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 . - */ - -package io.bitsquare.crypto; - -import io.bitsquare.p2p.MailboxMessage; - -import org.bitcoinj.core.ECKey; - -import java.security.KeyPair; - -import java.util.Arrays; -import java.util.Random; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.Assert.*; - -public class EncryptionServiceTests { - private static final Logger log = LoggerFactory.getLogger(EncryptionServiceTests.class); - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Test - public void testEncryptionWithMailboxMessage() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - TestMessage data = new TestMessage("test"); - byte[] encrypted = encryptionService.encryptMessage(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - MailboxMessage decrypted = encryptionService.decryptToMessage(p2pEncryptKeyPair.getPrivate(), encrypted); - assertEquals("", data.data, ((TestMessage) decrypted).data); - } - - @Test - public void testEncryptionWithInteger() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - Integer data = 1234; - byte[] encrypted = encryptionService.encryptObject(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - Integer decrypted = encryptionService.decryptToMessage(p2pEncryptKeyPair.getPrivate(), encrypted); - assertEquals("", data, decrypted); - } - - @Test - public void testEncryptionWithBytes() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04}; - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted); - assertTrue(Arrays.equals(data, decrypted)); - } - - @Test - public void testEncryptionWithLargeData() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[2000]; - new Random().nextBytes(data); - - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted); - assertTrue(Arrays.equals(data, decrypted)); - } - - @Test - public void testEncryptionWithTooMuchData() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[11000]; - new Random().nextBytes(data); - - thrown.expect(CryptoException.class); - thrown.expectMessage("The data exceeds the max. size."); - - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted); - } - - @Test - public void testEncryptionWithTooLessData() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[11000]; - new Random().nextBytes(data); - - thrown.expect(CryptoException.class); - thrown.expectMessage("The data is shorter as the min. overhead length."); - - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), new byte[24]); - } - - @Test - public void testEncryptionWithManipulatedPayload() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[110]; - new Random().nextBytes(data); - - thrown.expect(CryptoException.class); - thrown.expectMessage("The checksum is invalid."); - - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] manipulated = new byte[encrypted.length]; - System.arraycopy(encrypted, 0, manipulated, 0, manipulated.length); - manipulated[manipulated.length - 1] = 0x31; - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), manipulated); - } - - @Test - public void testEncryptionWithWrongVersion() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[1100]; - new Random().nextBytes(data); - - thrown.expect(CryptoException.class); - thrown.expectMessage("Incorrect version."); - - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] manipulated = new byte[encrypted.length]; - System.arraycopy(encrypted, 0, manipulated, 0, manipulated.length); - manipulated[0] = 0x02; - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), manipulated); - } - - @Test - public void testEncryptionWithNoData() throws Exception { - SignatureService signatureService = new SignatureService(); - EncryptionService encryptionService = new EncryptionService<>(signatureService); - KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair(); - ECKey signatureKeyPair = new ECKey(); - - byte[] data = new byte[0]; - - thrown.expect(CryptoException.class); - thrown.expectMessage("Input data is null."); - - byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data); - byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted); - } -} - -class TestMessage implements MailboxMessage { - public String data = "test"; - - public TestMessage(String data) { - this.data = data; - } -} diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareAppEnvironment.java b/gui/src/main/java/io/bitsquare/app/BitsquareAppEnvironment.java index 1887e66433..18e95a3987 100644 --- a/gui/src/main/java/io/bitsquare/app/BitsquareAppEnvironment.java +++ b/gui/src/main/java/io/bitsquare/app/BitsquareAppEnvironment.java @@ -19,6 +19,7 @@ package io.bitsquare.app; import io.bitsquare.btc.UserAgent; import io.bitsquare.btc.WalletService; +import io.bitsquare.crypto.KeyStorage; import io.bitsquare.gui.main.MainView; import io.bitsquare.p2p.tomp2p.TomP2PModule; import io.bitsquare.storage.Storage; @@ -59,6 +60,7 @@ public class BitsquareAppEnvironment extends BitsquareEnvironment { setProperty(WalletService.PREFIX_KEY, appName); setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString()); + setProperty(KeyStorage.DIR_KEY, Paths.get(appDataDir, "keys").toString()); setProperty(MainView.TITLE_KEY, appName); diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java b/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java index 3a37f1fe81..1a132c9c20 100644 --- a/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java +++ b/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java @@ -22,6 +22,8 @@ import io.bitsquare.arbitration.ArbitratorModule; import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule; import io.bitsquare.btc.BitcoinModule; import io.bitsquare.crypto.CryptoModule; +import io.bitsquare.crypto.KeyRing; +import io.bitsquare.crypto.KeyStorage; import io.bitsquare.gui.GuiModule; import io.bitsquare.offer.OfferModule; import io.bitsquare.offer.tomp2p.TomP2POfferModule; @@ -59,12 +61,17 @@ class BitsquareAppModule extends BitsquareModule { @Override protected void configure() { + bind(KeyStorage.class).in(Singleton.class); + bind(KeyRing.class).in(Singleton.class); bind(User.class).in(Singleton.class); bind(Preferences.class).in(Singleton.class); bind(AccountSettings.class).in(Singleton.class); File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY)); bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir); + + File keyStorageDir = new File(env.getRequiredProperty(KeyStorage.DIR_KEY)); + bind(File.class).annotatedWith(named(KeyStorage.DIR_KEY)).toInstance(keyStorageDir); bind(Environment.class).toInstance(env); bind(UpdateProcess.class).in(Singleton.class); diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index b8ff5ed9c8..5048fc9686 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -21,6 +21,7 @@ import io.bitsquare.app.UpdateProcess; import io.bitsquare.arbitration.ArbitrationRepository; import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.WalletService; +import io.bitsquare.crypto.KeyRing; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.util.BSFormatter; @@ -85,6 +86,7 @@ class MainViewModel implements ViewModel { final String bitcoinNetworkAsString; private final User user; + private KeyRing keyRing; private final WalletService walletService; private final ArbitrationRepository arbitrationRepository; private final ClientNode clientNode; @@ -93,10 +95,11 @@ class MainViewModel implements ViewModel { private final BSFormatter formatter; @Inject - public MainViewModel(User user, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode, + public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode, TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess, BSFormatter formatter) { this.user = user; + this.keyRing = keyRing; this.walletService = walletService; this.arbitrationRepository = arbitrationRepository; this.clientNode = clientNode; @@ -137,7 +140,7 @@ class MainViewModel implements ViewModel { // Set executor for all P2PServices BaseP2PService.setUserThread(Platform::runLater); - Observable bootstrapStateAsObservable = clientNode.bootstrap(user.getP2pSigKeyPair()); + Observable bootstrapStateAsObservable = clientNode.bootstrap(keyRing.getDhtSignatureKeyPair()); bootstrapStateAsObservable.publish(); bootstrapStateAsObservable.subscribe( state -> Platform.runLater(() -> setBootstrapState(state)), diff --git a/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java b/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java index 0870f63184..87fdeb6e40 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java @@ -22,7 +22,7 @@ import io.bitsquare.gui.common.view.FxmlView; import io.bitsquare.gui.common.view.InitializableView; import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol; import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage; -import io.bitsquare.trade.protocol.availability.tasks.RequestIsOfferAvailable; +import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol; import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook; import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx; @@ -82,7 +82,7 @@ public class DebugView extends InitializableView { /*---- Protocol ----*/ CheckOfferAvailabilityProtocol.class, io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class, - RequestIsOfferAvailable.class, + SendRequestIsOfferAvailableMessage.class, ProcessReportOfferAvailabilityMessage.class, Boolean.class, /* used as seperator*/ diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookDataModel.java index ceafa309db..5b63c602d5 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookDataModel.java @@ -210,7 +210,7 @@ class OfferBookDataModel implements Activatable, DataModel { } boolean isMyOffer(Offer offer) { - return offer.getP2pSigPubKey() != null && offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()); + return tradeManager.isMyOffer(offer); } Coin getAmountAsCoin() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesDataModel.java index d8089fe9d4..929968a2ad 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/closedtrades/ClosedTradesDataModel.java @@ -64,8 +64,7 @@ class ClosedTradesDataModel implements Activatable, DataModel { } public Offer.Direction getDirection(Offer offer) { - return offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()) ? - offer.getDirection() : offer.getMirroredDirection(); + return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection(); } private void applyList() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersDataModel.java index 0df509bce4..22808cb867 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/openoffer/OpenOffersDataModel.java @@ -75,8 +75,7 @@ class OpenOffersDataModel implements Activatable, DataModel { } public Offer.Direction getDirection(Offer offer) { - return offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()) ? - offer.getDirection() : offer.getMirroredDirection(); + return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection(); } private void applyList() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java index 8fc04804d1..d43d9dceb7 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -145,7 +145,7 @@ class PendingTradesDataModel implements Activatable, DataModel { trade = item.getTrade(); tradeProperty.set(trade); - isOffererRole = trade.getOffer().getP2pSigPubKey().equals(user.getP2pSigPubKey()); + isOffererRole = tradeManager.isMyOffer(trade.getOffer()); if (trade instanceof SellerTrade) sellerProcessState.bind(trade.processStateProperty()); diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java index 34833145ca..b63a93cd1b 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -402,6 +402,9 @@ public class PendingTradesViewModel extends ActivatableWithDataModel