From b5e47716d6deedff6b3e2abb3280bef43bc6b15c Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 20 Mar 2015 17:30:58 +0100 Subject: [PATCH] Add mailbox service and message encryption --- .../io/bitsquare/crypto/CryptoModule.java | 1 + .../bitsquare/crypto/EncryptionService.java | 88 ++++++++++ .../io/bitsquare/gui/main/MainViewModel.java | 4 +- .../content/irc/IrcAccountDataModel.java | 2 +- .../restrictions/RestrictionsDataModel.java | 2 +- .../offer/tomp2p/TomP2POfferBookService.java | 7 +- .../java/io/bitsquare/p2p/MailboxMessage.java | 41 +++++ .../java/io/bitsquare/p2p/MailboxService.java | 32 ++++ .../p2p/tomp2p/TomP2PAddressService.java | 2 +- .../p2p/tomp2p/TomP2PDHTService.java | 2 +- .../p2p/tomp2p/TomP2PMailboxService.java | 158 +++++------------- .../io/bitsquare/p2p/tomp2p/TomP2PModule.java | 6 +- .../src/main/java/io/bitsquare/user/User.java | 30 +++- .../java/io/bitsquare/util/Utilities.java | 55 ++++++ .../placeoffer/PlaceOfferProtocolTest.java | 2 +- 15 files changed, 297 insertions(+), 135 deletions(-) create mode 100644 core/src/main/java/io/bitsquare/crypto/EncryptionService.java create mode 100644 core/src/main/java/io/bitsquare/p2p/MailboxMessage.java create mode 100644 core/src/main/java/io/bitsquare/p2p/MailboxService.java diff --git a/core/src/main/java/io/bitsquare/crypto/CryptoModule.java b/core/src/main/java/io/bitsquare/crypto/CryptoModule.java index 81c0d9902c..a0d68dc24b 100644 --- a/core/src/main/java/io/bitsquare/crypto/CryptoModule.java +++ b/core/src/main/java/io/bitsquare/crypto/CryptoModule.java @@ -33,5 +33,6 @@ public class CryptoModule extends BitsquareModule { protected void configure() { bind(SignatureService.class).in(Singleton.class); bind(HashService.class).in(Singleton.class); + bind(EncryptionService.class).in(Singleton.class); } } diff --git a/core/src/main/java/io/bitsquare/crypto/EncryptionService.java b/core/src/main/java/io/bitsquare/crypto/EncryptionService.java new file mode 100644 index 0000000000..1e7f74ebbc --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/EncryptionService.java @@ -0,0 +1,88 @@ +/* + * 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.util.Utilities; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; + +import javax.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; + +public class EncryptionService { + private static final Logger log = LoggerFactory.getLogger(EncryptionService.class); + + @Inject + public EncryptionService() { + } + + public KeyPair getKeyPair() { + KeyPair keyPair = null; + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + keyPair = keyPairGenerator.genKeyPair(); + } catch (Exception e) { + e.printStackTrace(); + log.error("Exception at init " + e.getMessage()); + } + return keyPair; + } + + public byte[] encryptObject(PublicKey publicKey, Object object) { + return encrypt(publicKey, Utilities.objectToBytArray(object)); + } + + public Object decryptObject(PrivateKey privateKey, byte[] cipherMessage) { + return Utilities.byteArrayToObject(decrypt(privateKey, cipherMessage)); + } + + public byte[] encrypt(PublicKey publicKey, byte[] data) { + byte[] cipherData = null; + try { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + cipherData = cipher.doFinal(data); + } catch (Exception e) { + e.printStackTrace(); + log.error("Exception at encrypt " + e.getMessage()); + } + return cipherData; + } + + public byte[] decrypt(PrivateKey privateKey, byte[] cipherText) { + byte[] data = null; + try { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + data = cipher.doFinal(cipherText); + } catch (Exception e) { + e.printStackTrace(); + log.error("Exception at decrypt " + e.getMessage()); + } + return data; + } + +} diff --git a/core/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/core/src/main/java/io/bitsquare/gui/main/MainViewModel.java index 7231f54027..49b894d0d5 100644 --- a/core/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -161,7 +161,7 @@ class MainViewModel implements ViewModel { // Set executor for all P2PServices BaseP2PService.setUserThread(Platform::runLater); - Observable bootstrapStateAsObservable = clientNode.bootstrap(user.getMessageKeyPair()); + Observable bootstrapStateAsObservable = clientNode.bootstrap(user.getP2pSigKeyPair()); bootstrapStateAsObservable.publish(); bootstrapStateAsObservable.subscribe( state -> Platform.runLater(() -> setBootstrapState(state)), @@ -354,7 +354,7 @@ class MainViewModel implements ViewModel { } private void addMockArbitrator() { - if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getMessageKeyPair() != null) { + if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getP2pSigKeyPair() != null) { byte[] pubKey = new ECKey().getPubKey(); String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePubKey()); List languages = new ArrayList<>(); diff --git a/core/src/main/java/io/bitsquare/gui/main/account/content/irc/IrcAccountDataModel.java b/core/src/main/java/io/bitsquare/gui/main/account/content/irc/IrcAccountDataModel.java index 284ec70970..6a0e5e2653 100644 --- a/core/src/main/java/io/bitsquare/gui/main/account/content/irc/IrcAccountDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/account/content/irc/IrcAccountDataModel.java @@ -122,7 +122,7 @@ class IrcAccountDataModel implements Activatable, DataModel { } private void addMockArbitrator() { - if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getMessageKeyPair() != null) { + if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getP2pSigKeyPair() != null) { byte[] pubKey = new ECKey().getPubKey(); String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePubKey()); List languages = new ArrayList<>(); diff --git a/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java b/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java index 7561c563e2..e9c64eaa28 100644 --- a/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java @@ -152,7 +152,7 @@ class RestrictionsDataModel implements Activatable, DataModel { // TODO Remove mock later private void addMockArbitrator() { - if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getMessageKeyPair() != null) { + if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getP2pSigKeyPair() != null) { byte[] pubKey = new ECKey().getPubKey(); String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePubKey()); List languages = new ArrayList<>(); 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 93023450c8..d167dc98a3 100644 --- a/core/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBookService.java +++ b/core/src/main/java/io/bitsquare/offer/tomp2p/TomP2POfferBookService.java @@ -50,8 +50,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TomP2POfferBookService extends TomP2PDHTService implements OfferBookService { - private static final Logger log = LoggerFactory.getLogger(TomP2POfferBookService.class); + private static final int TTL = 30 * 24 * 60 * 60; // the offer is default 30 days valid private final List offerRepositoryListeners = new ArrayList<>(); private final LongProperty invalidationTimestamp = new SimpleLongProperty(0); @@ -67,10 +67,7 @@ public class TomP2POfferBookService extends TomP2PDHTService implements OfferBoo Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode()); try { final Data offerData = new Data(offer); - - // the offer is default 30 days valid - int defaultOfferTTL = 30 * 24 * 60 * 60; - offerData.ttlSeconds(defaultOfferTTL); + offerData.ttlSeconds(TTL); log.trace("Add offer to DHT requested. Added data: [locationKey: " + locationKey + ", hash: " + offerData.hash().toString() + "]"); FuturePut futurePut = addProtectedDataToMap(locationKey, offerData); diff --git a/core/src/main/java/io/bitsquare/p2p/MailboxMessage.java b/core/src/main/java/io/bitsquare/p2p/MailboxMessage.java new file mode 100644 index 0000000000..9722bbc4bb --- /dev/null +++ b/core/src/main/java/io/bitsquare/p2p/MailboxMessage.java @@ -0,0 +1,41 @@ +/* + * 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.p2p; + +import java.io.Serializable; + +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 MailboxMessage implements Message, Serializable { + private static final long serialVersionUID = -3111178895546299769L; + private static final Logger log = LoggerFactory.getLogger(MailboxMessage.class); + + private byte[] cipherMessage; + + public MailboxMessage(byte[] cipherMessage) { + this.cipherMessage = cipherMessage; + } + + public byte[] getCipherMessage() { + return cipherMessage; + } +} diff --git a/core/src/main/java/io/bitsquare/p2p/MailboxService.java b/core/src/main/java/io/bitsquare/p2p/MailboxService.java new file mode 100644 index 0000000000..ca4e43efd5 --- /dev/null +++ b/core/src/main/java/io/bitsquare/p2p/MailboxService.java @@ -0,0 +1,32 @@ +/* + * 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.p2p; + +import io.bitsquare.common.handlers.FaultHandler; +import io.bitsquare.common.handlers.ResultHandler; +import io.bitsquare.p2p.tomp2p.TomP2PMailboxService; + +import java.security.PublicKey; + +public interface MailboxService { + void addMessage(PublicKey publicKey, MailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler); + + void removeMessage(PublicKey publicKey, MailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler); + + void getMessages(PublicKey publicKey, TomP2PMailboxService.MailboxMessagesResultHandler resultHandler); +} 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 bc92949b4d..e99a7919a1 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java @@ -66,7 +66,7 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer public TomP2PAddressService(TomP2PNode tomP2PNode, User user) { super(tomP2PNode, user); - locationKey = Utils.makeSHAHash(user.getMessageKeyPair().getPublic().getEncoded()); + locationKey = Utils.makeSHAHash(user.getP2pSigKeyPair().getPublic().getEncoded()); } @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 b02deb9e27..120424d23e 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java @@ -49,7 +49,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { @Inject public TomP2PDHTService(TomP2PNode tomP2PNode, User user) { super(tomP2PNode); - keyPair = user.getMessageKeyPair(); + keyPair = user.getP2pSigKeyPair(); pubKeyHashForMyDomain = Utils.makeSHAHash(keyPair.getPublic().getEncoded()); } 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 4bea102f5d..6372f0cdbf 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java @@ -19,12 +19,9 @@ package io.bitsquare.p2p.tomp2p; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; -import io.bitsquare.offer.Offer; import io.bitsquare.offer.OfferBookService; -import io.bitsquare.p2p.AddressService; -import io.bitsquare.p2p.Message; -import io.bitsquare.p2p.Peer; -import io.bitsquare.p2p.listener.GetPeerAddressListener; +import io.bitsquare.p2p.MailboxMessage; +import io.bitsquare.p2p.MailboxService; import io.bitsquare.user.User; import java.io.IOException; @@ -46,21 +43,16 @@ import net.tomp2p.futures.BaseFutureListener; import net.tomp2p.peers.Number160; import net.tomp2p.peers.Number640; import net.tomp2p.storage.Data; -import net.tomp2p.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class TomP2PMailboxService extends TomP2PDHTService implements AddressService { +public class TomP2PMailboxService extends TomP2PDHTService implements MailboxService { private static final Logger log = LoggerFactory.getLogger(TomP2PMailboxService.class); - + private static final int TTL = 15 * 24 * 60 * 60; // the message is default 15 days valid, as a max trade period might be about 2 weeks. private final List offerRepositoryListeners = new ArrayList<>(); - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - @Inject public TomP2PMailboxService(TomP2PNode tomP2PNode, User user) { super(tomP2PNode, user); @@ -76,69 +68,45 @@ public class TomP2PMailboxService extends TomP2PDHTService implements AddressSer super.shutDown(); } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Find peer address by publicKey - /////////////////////////////////////////////////////////////////////////////////////////// - - // public void findPeerAddress(PublicKey publicKey, GetPeerAddressListener listener) { - // final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded()); - - public void saveMessage(PublicKey publicKey, Message message, ResultHandler resultHandler, FaultHandler faultHandler) { - final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded()); - - // Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode()); + @Override + public void addMessage(PublicKey publicKey, MailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { try { - final Data offerData = new Data(message); + final Data data = new Data(message); + data.ttlSeconds(TTL); + log.trace("Add message to DHT requested. Added data: [locationKey: " + getLocationKey(publicKey) + + ", hash: " + data.hash().toString() + "]"); - // the offer is default 30 days valid - int defaultOfferTTL = 30 * 24 * 60 * 60; - offerData.ttlSeconds(defaultOfferTTL); - log.trace("Add offer to DHT requested. Added data: [locationKey: " + locationKey + - ", hash: " + offerData.hash().toString() + "]"); - FuturePut futurePut = addProtectedDataToMap(locationKey, offerData); + FuturePut futurePut = addDataToMapOfProtectedDomain(getLocationKey(publicKey), data, publicKey); futurePut.addListener(new BaseFutureListener() { @Override public void operationComplete(BaseFuture future) throws Exception { if (future.isSuccess()) { executor.execute(() -> { resultHandler.handleResult(); - offerRepositoryListeners.stream().forEach(listener -> { - try { - Object offerDataObject = offerData.object(); - if (offerDataObject instanceof Offer) { - log.info("Added offer to DHT with ID: " + offerDataObject); - listener.onOfferAdded((Offer) offerDataObject); - } - } catch (ClassNotFoundException | IOException e) { - e.printStackTrace(); - log.error("Add offer to DHT failed: " + e.getMessage()); - } - }); - log.trace("Add offer to DHT was successful. Added data: [locationKey: " + locationKey + - ", value: " + offerData + "]"); + log.trace("Add message to mailbox was successful. Added data: [locationKey: " + getLocationKey(publicKey) + + ", value: " + data + "]"); }); } } @Override public void exceptionCaught(Throwable ex) throws Exception { - executor.execute(() -> faultHandler.handleFault("Failed to add offer to DHT", ex)); + executor.execute(() -> faultHandler.handleFault("Add message to mailbox failed.", ex)); } }); } catch (IOException ex) { - executor.execute(() -> faultHandler.handleFault("Failed to add offer to DHT", ex)); + executor.execute(() -> faultHandler.handleFault("Add message to mailbox failed.", ex)); } } - public void removeOffer(Offer offer, ResultHandler resultHandler, FaultHandler faultHandler) { - Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode()); + @Override + public void removeMessage(PublicKey publicKey, MailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { try { - final Data offerData = new Data(offer); - log.trace("Remove offer from DHT requested. Removed data: [locationKey: " + locationKey + - ", hash: " + offerData.hash().toString() + "]"); - FutureRemove futureRemove = removeProtectedDataFromMap(locationKey, offerData); + final Data data = new Data(message); + log.trace("Remove message from DHT requested. Removed data: [locationKey: " + getLocationKey(publicKey) + + ", hash: " + data.hash().toString() + "]"); + FutureRemove futureRemove = removeDataFromMapOfMyProtectedDomain(getLocationKey(publicKey), data); futureRemove.addListener(new BaseFutureListener() { @Override public void operationComplete(BaseFuture future) throws Exception { @@ -148,109 +116,73 @@ public class TomP2PMailboxService extends TomP2PDHTService implements AddressSer log.trace("isRemoved? " + futureRemove.isRemoved()); executor.execute(() -> { resultHandler.handleResult(); - offerRepositoryListeners.stream().forEach(listener -> { - try { - Object offerDataObject = offerData.object(); - if (offerDataObject instanceof Offer) { - log.trace("Remove offer from DHT was successful. Removed data: [key: " + - locationKey + ", " + - "offer: " + offerDataObject + "]"); - listener.onOfferRemoved((Offer) offerDataObject); - } - } catch (ClassNotFoundException | IOException e) { - e.printStackTrace(); - log.error("Remove offer from DHT failed. Error: " + e.getMessage()); - faultHandler.handleFault("Remove offer from DHT failed. Error: " + e.getMessage(), e); - } - }); }); } @Override public void exceptionCaught(Throwable t) throws Exception { - log.error("Remove offer from DHT failed. Error: " + t.getMessage()); - faultHandler.handleFault("Remove offer from DHT failed. Error: " + t.getMessage(), t); + log.error("Remove message from DHT failed. Error: " + t.getMessage()); + faultHandler.handleFault("Remove message from DHT failed. Error: " + t.getMessage(), t); } }); } catch (IOException e) { e.printStackTrace(); - log.error("Remove offer from DHT failed. Error: " + e.getMessage()); - faultHandler.handleFault("Remove offer from DHT failed. Error: " + e.getMessage(), e); + log.error("Remove message from DHT failed. Error: " + e.getMessage()); + faultHandler.handleFault("Remove message from DHT failed. Error: " + e.getMessage(), e); } } - public void getOffers(String currencyCode) { - Number160 locationKey = Number160.createHash(currencyCode); - log.trace("Get offers from DHT requested for locationKey: " + locationKey); - FutureGet futureGet = getMap(locationKey); + @Override + public void getMessages(PublicKey publicKey, MailboxMessagesResultHandler resultHandler) { + log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(publicKey)); + FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(publicKey)); futureGet.addListener(new BaseFutureAdapter() { @Override public void operationComplete(BaseFuture future) throws Exception { if (future.isSuccess()) { final Map dataMap = futureGet.dataMap(); - final List offers = new ArrayList<>(); + List messages = new ArrayList<>(); if (dataMap != null) { - for (Data offerData : dataMap.values()) { + for (Data messageData : dataMap.values()) { try { - Object offerDataObject = offerData.object(); - if (offerDataObject instanceof Offer) { - offers.add((Offer) offerDataObject); + Object messageDataObject = messageData.object(); + if (messageDataObject instanceof MailboxMessage) { + messages.add((MailboxMessage) messageDataObject); } } catch (ClassNotFoundException | IOException e) { e.printStackTrace(); } } - - executor.execute(() -> offerRepositoryListeners.stream().forEach(listener -> - listener.onOffersReceived(offers))); + executor.execute(() -> resultHandler.handleResult(messages)); } - log.trace("Get offers from DHT was successful. Stored data: [key: " + locationKey + log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(publicKey) + ", values: " + futureGet.dataMap() + "]"); } else { final Map dataMap = futureGet.dataMap(); if (dataMap == null || dataMap.size() == 0) { - log.trace("Get offers from DHT delivered empty dataMap."); - executor.execute(() -> offerRepositoryListeners.stream().forEach(listener -> - listener.onOffersReceived(new ArrayList<>()))); + log.trace("Get messages from DHT delivered empty dataMap."); + executor.execute(() -> resultHandler.handleResult(new ArrayList<>())); } else { - log.error("Get offers from DHT was not successful with reason:" + future.failedReason()); + log.error("Get messages from DHT was not successful with reason:" + future.failedReason()); } } } }); } - @Override - public void findPeerAddress(PublicKey publicKey, GetPeerAddressListener listener) { - final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded()); - FutureGet futureGet = getDataOfProtectedDomain(locationKey, publicKey); - log.trace("findPeerAddress called"); - futureGet.addListener(new BaseFutureAdapter() { - @Override - public void operationComplete(BaseFuture baseFuture) throws Exception { - if (baseFuture.isSuccess() && futureGet.data() != null) { - final Peer peer = (Peer) futureGet.data().object(); - log.trace("Peer found in DHT. Peer = " + peer); - executor.execute(() -> listener.onResult(peer)); - } - else { - log.error("getPeerAddress failed. failedReason = " + baseFuture.failedReason()); - executor.execute(listener::onFailed); - } - } - }); - } - /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// - private Number160 getLocationKey(String currencyCode) { - return Number160.createHash(currencyCode + "mailbox"); + private Number160 getLocationKey(PublicKey publicKey) { + return Number160.createHash("mailbox" + publicKey.hashCode()); } -} + public interface MailboxMessagesResultHandler { + void handleResult(List messages); + } +} \ No newline at end of file diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PModule.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PModule.java index d4eb963997..46c8f900b8 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PModule.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PModule.java @@ -20,9 +20,10 @@ package io.bitsquare.p2p.tomp2p; import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.BootstrapNodes; import io.bitsquare.p2p.ClientNode; +import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MessageService; -import io.bitsquare.p2p.P2PModule; import io.bitsquare.p2p.Node; +import io.bitsquare.p2p.P2PModule; import com.google.inject.Injector; import com.google.inject.Singleton; @@ -53,7 +54,8 @@ public class TomP2PModule extends P2PModule { bind(AddressService.class).to(TomP2PAddressService.class).in(Singleton.class); bind(MessageService.class).to(TomP2PMessageService.class).in(Singleton.class); - + bind(MailboxService.class).to(TomP2PMailboxService.class).in(Singleton.class); + bind(int.class).annotatedWith(Names.named(Node.PORT_KEY)).toInstance(env.getProperty(Node.PORT_KEY, int.class, Node.DEFAULT_PORT)); bind(boolean.class).annotatedWith(Names.named(USE_MANUAL_PORT_FORWARDING_KEY)).toInstance( env.getProperty(USE_MANUAL_PORT_FORWARDING_KEY, boolean.class, false)); diff --git a/core/src/main/java/io/bitsquare/user/User.java b/core/src/main/java/io/bitsquare/user/User.java index b4255da557..84d0042ab9 100644 --- a/core/src/main/java/io/bitsquare/user/User.java +++ b/core/src/main/java/io/bitsquare/user/User.java @@ -17,6 +17,7 @@ package io.bitsquare.user; +import io.bitsquare.crypto.EncryptionService; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.util.DSAKeyUtil; @@ -30,6 +31,8 @@ import java.util.List; import javax.annotation.Nullable; +import javax.inject.Inject; + import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; @@ -43,7 +46,8 @@ import javafx.collections.ObservableList; public class User implements Serializable { private static final long serialVersionUID = 7409078808248518638L; - private KeyPair messageKeyPair; + private KeyPair p2pSigKeyPair; + private KeyPair p2pEncryptKeyPair; private String accountID; // Used for serialisation (ObservableList cannot be serialized) -> serialisation will change anyway so that is @@ -53,8 +57,11 @@ public class User implements Serializable { private final transient ObservableList fiatAccounts = FXCollections.observableArrayList(); private final transient ObjectProperty currentBankAccount = new SimpleObjectProperty<>(); + transient private EncryptionService encryptionService; - public User() { + @Inject + public User(EncryptionService encryptionService) { + this.encryptionService = encryptionService; // Used for serialisation (ObservableList cannot be serialized) -> serialisation will change anyway so that is // only temporary fiatAccounts.addListener((ListChangeListener) change -> _fiatAccounts = new ArrayList<>(fiatAccounts)); @@ -62,6 +69,9 @@ public class User implements Serializable { currentBankAccount.addListener((ov) -> _currentFiatAccount = currentBankAccount.get()); } + // for unit tests + public User() { + } /////////////////////////////////////////////////////////////////////////////////////////// // Public Methods @@ -71,13 +81,14 @@ public class User implements Serializable { if (persistedUser != null) { fiatAccounts.setAll(persistedUser.getSerializedBankAccounts()); setCurrentBankAccount(persistedUser.getSerializedCurrentBankAccount()); - messageKeyPair = persistedUser.getMessageKeyPair(); + p2pSigKeyPair = persistedUser.getP2pSigKeyPair(); + p2pEncryptKeyPair = persistedUser.getP2pEncryptKeyPair(); accountID = persistedUser.getAccountId(); } else { // First time - // TODO use separate thread. DSAKeyUtil.getKeyPair() runs in same thread now - messageKeyPair = DSAKeyUtil.generateKeyPair(); + p2pSigKeyPair = DSAKeyUtil.generateKeyPair(); + p2pEncryptKeyPair = encryptionService.getKeyPair(); } } @@ -160,12 +171,12 @@ public class User implements Serializable { return null; } - public KeyPair getMessageKeyPair() { - return messageKeyPair; + public KeyPair getP2pSigKeyPair() { + return p2pSigKeyPair; } public PublicKey getMessagePubKey() { - return messageKeyPair.getPublic(); + return p2pSigKeyPair.getPublic(); } public ObjectProperty currentBankAccountProperty() { @@ -181,4 +192,7 @@ public class User implements Serializable { return _currentFiatAccount; } + public KeyPair getP2pEncryptKeyPair() { + return p2pEncryptKeyPair; + } } diff --git a/core/src/main/java/io/bitsquare/util/Utilities.java b/core/src/main/java/io/bitsquare/util/Utilities.java index e55c2fdd52..c4fc8e8c2b 100644 --- a/core/src/main/java/io/bitsquare/util/Utilities.java +++ b/core/src/main/java/io/bitsquare/util/Utilities.java @@ -27,7 +27,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.ObjectInput; import java.io.ObjectInputStream; +import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -145,6 +147,59 @@ public class Utilities { return result; } + public static Object byteArrayToObject(byte[] data) { + ByteArrayInputStream bis = new ByteArrayInputStream(data); + ObjectInput in = null; + Object result = null; + try { + in = new ObjectInputStream(bis); + result = in.readObject(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + bis.close(); + } catch (IOException ex) { + // ignore close exception + } + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { + // ignore close exception + } + } + return result; + } + + public static byte[] objectToBytArray(Object object) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + byte[] result = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(object); + result = bos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ex) { + // ignore close exception + } + try { + bos.close(); + } catch (IOException ex) { + // ignore close exception + } + } + return result; + } + private static void printElapsedTime(String msg) { if (!msg.isEmpty()) { msg += " / "; diff --git a/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java b/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java index e6c5cc48c3..e702507d51 100644 --- a/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java +++ b/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java @@ -98,7 +98,7 @@ public class PlaceOfferProtocolTest { tomP2PNode = new TomP2PNode(bootstrappedPeerBuilder); messageService = new TomP2PMessageService(tomP2PNode); - Observable messageObservable = tomP2PNode.bootstrap(user.getMessageKeyPair()); + Observable messageObservable = tomP2PNode.bootstrap(user.getP2pSigKeyPair()); messageObservable.publish(); messageObservable.subscribe( state -> log.trace("state changed: " + state),