Use encryption/sig for all messages

This commit is contained in:
Manfred Karrer 2015-04-10 23:48:52 +02:00
parent 32c56dad1a
commit 0ec86550b0
68 changed files with 1494 additions and 1152 deletions

View file

@ -22,6 +22,7 @@ import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.RegTestHost; import io.bitsquare.btc.RegTestHost;
import io.bitsquare.btc.UserAgent; import io.bitsquare.btc.UserAgent;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.KeyStorage;
import io.bitsquare.p2p.BootstrapNodes; import io.bitsquare.p2p.BootstrapNodes;
import io.bitsquare.p2p.tomp2p.TomP2PModule; import io.bitsquare.p2p.tomp2p.TomP2PModule;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
@ -166,6 +167,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
setProperty(WalletService.PREFIX_KEY, appName); setProperty(WalletService.PREFIX_KEY, appName);
setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString()); 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); setProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY, bootstrapNodePort);
} }

View file

@ -21,9 +21,9 @@ import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.arbitration.ArbitratorService; import io.bitsquare.arbitration.ArbitratorService;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.p2p.tomp2p.TomP2PDHTService; import io.bitsquare.p2p.tomp2p.TomP2PDHTService;
import io.bitsquare.p2p.tomp2p.TomP2PNode; import io.bitsquare.p2p.tomp2p.TomP2PNode;
import io.bitsquare.user.User;
import java.io.IOException; import java.io.IOException;
@ -53,8 +53,8 @@ public class TomP2PArbitratorService extends TomP2PDHTService implements Arbitra
private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
@Inject @Inject
public TomP2PArbitratorService(TomP2PNode tomP2PNode, User user) { public TomP2PArbitratorService(TomP2PNode tomP2PNode, KeyRing keyRing) {
super(tomP2PNode, user); super(tomP2PNode, keyRing);
} }
public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {

View file

@ -20,7 +20,7 @@ package io.bitsquare.btc;
import io.bitsquare.btc.listeners.AddressConfidenceListener; import io.bitsquare.btc.listeners.AddressConfidenceListener;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.listeners.TxConfidenceListener; 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.AbstractWalletEventListener;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
@ -95,7 +95,7 @@ public class WalletService {
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final AddressEntryList addressEntryList; private final AddressEntryList addressEntryList;
private final NetworkParameters params; private final NetworkParameters params;
private final SignatureService signatureService; private final CryptoService cryptoService;
private final File walletDir; private final File walletDir;
private final String walletPrefix; private final String walletPrefix;
private final UserAgent userAgent; private final UserAgent userAgent;
@ -111,14 +111,14 @@ public class WalletService {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, SignatureService signatureService, public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, CryptoService cryptoService,
TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent, TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent,
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) { @Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) {
this.regTestHost = regTestHost; this.regTestHost = regTestHost;
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.addressEntryList = addressEntryList; this.addressEntryList = addressEntryList;
this.params = bitcoinNetwork.getParameters(); this.params = bitcoinNetwork.getParameters();
this.signatureService = signatureService; this.cryptoService = cryptoService;
this.walletDir = walletDir; this.walletDir = walletDir;
this.walletPrefix = walletPrefix; this.walletPrefix = walletPrefix;
this.userAgent = userAgent; this.userAgent = userAgent;
@ -208,7 +208,8 @@ public class WalletService {
}, Threading.USER_THREAD); }, Threading.USER_THREAD);
walletAppKit.startAsync(); 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() { private void initWallet() {
@ -457,7 +458,7 @@ public class WalletService {
Transaction tx = new Transaction(params); 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()); tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build());
// We don't take a fee at the moment // We don't take a fee at the moment

View file

@ -31,8 +31,6 @@ public class CryptoModule extends BitsquareModule {
@Override @Override
protected void configure() { protected void configure() {
bind(SignatureService.class).in(Singleton.class); bind(CryptoService.class).in(Singleton.class);
bind(HashService.class).in(Singleton.class);
bind(EncryptionService.class).in(Singleton.class);
} }
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<T> {
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);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<T> {
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.<T>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);
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View file

@ -15,27 +15,32 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>. * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/ */
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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/* public class MessageWithPubKey implements Message {
Stores a message in encrypted form, so it never leaves the client in plain text. private static final Logger log = LoggerFactory.getLogger(MessageWithPubKey.class);
*/
public class EncryptedMailboxMessage implements MailboxMessage, Serializable {
private static final long serialVersionUID = -3111178895546299769L;
private static final Logger log = LoggerFactory.getLogger(EncryptedMailboxMessage.class);
private final byte[] bytes; public Message getMessage() {
return message;
public EncryptedMailboxMessage(byte[] bytes) {
this.bytes = bytes;
} }
public byte[] getBytes() { public PublicKey getSignaturePubKey() {
return bytes; return signaturePubKey;
}
private final Message message;
private final PublicKey signaturePubKey;
public MessageWithPubKey(Message message, PublicKey signaturePubKey) {
this.message = message;
this.signaturePubKey = signaturePubKey;
} }
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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()) +
'}';
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}

View file

@ -25,24 +25,23 @@ import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class Util { public class Util {
private static final Logger log = LoggerFactory.getLogger(Util.class); 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()); 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 { public static PublicKey decodeDSAPubKeyHex(String pubKeyHex) throws NoSuchAlgorithmException, InvalidKeySpecException {
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex)); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex));
KeyFactory keyFactory = KeyFactory.getInstance("DSA"); KeyFactory keyFactory = KeyFactory.getInstance("DSA");
return keyFactory.generatePublic(pubKeySpec); return keyFactory.generatePublic(pubKeySpec);
} }
public static String encodePubKeyToHex(PublicKey pubKey) {
return Utils.HEX.encode(pubKey.getEncoded());
}
} }

View file

@ -18,6 +18,7 @@
package io.bitsquare.offer; package io.bitsquare.offer;
import io.bitsquare.btc.Restrictions; import io.bitsquare.btc.Restrictions;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.locale.Country; import io.bitsquare.locale.Country;
@ -28,8 +29,6 @@ import org.bitcoinj.utils.Fiat;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.security.PublicKey;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -48,6 +47,7 @@ public class Offer implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private transient static final Logger log = LoggerFactory.getLogger(Offer.class); private transient static final Logger log = LoggerFactory.getLogger(Offer.class);
public enum Direction {BUY, SELL} public enum Direction {BUY, SELL}
public enum State { public enum State {
@ -71,7 +71,7 @@ public class Offer implements Serializable {
private final long fiatPrice; private final long fiatPrice;
private final Coin amount; private final Coin amount;
private final Coin minAmount; private final Coin minAmount;
private final PublicKey p2pSigPubKey; private final PubKeyRing pubKeyRing;
private final FiatAccount.Type fiatAccountType; private final FiatAccount.Type fiatAccountType;
private final Country bankAccountCountry; private final Country bankAccountCountry;
@ -95,7 +95,7 @@ public class Offer implements Serializable {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public Offer(String id, public Offer(String id,
PublicKey p2pSigPubKey, PubKeyRing pubKeyRing,
Direction direction, Direction direction,
long fiatPrice, long fiatPrice,
Coin amount, Coin amount,
@ -109,7 +109,7 @@ public class Offer implements Serializable {
List<Country> acceptedCountries, List<Country> acceptedCountries,
List<String> acceptedLanguageCodes) { List<String> acceptedLanguageCodes) {
this.id = id; this.id = id;
this.p2pSigPubKey = p2pSigPubKey; this.pubKeyRing = pubKeyRing;
this.direction = direction; this.direction = direction;
this.fiatPrice = fiatPrice; this.fiatPrice = fiatPrice;
this.amount = amount; this.amount = amount;
@ -144,7 +144,7 @@ public class Offer implements Serializable {
checkNotNull(getCurrencyCode(), "Currency is null"); checkNotNull(getCurrencyCode(), "Currency is null");
checkNotNull(getDirection(), "Direction is null"); checkNotNull(getDirection(), "Direction is null");
checkNotNull(getId(), "Id is null"); checkNotNull(getId(), "Id is null");
checkNotNull(getP2pSigPubKey(), "p2pSigPubKey is null"); checkNotNull(getPubKeyRing(), "pubKeyRing is null");
checkNotNull(getMinAmount(), "MinAmount is null"); checkNotNull(getMinAmount(), "MinAmount is null");
checkNotNull(getPrice(), "Price is null"); checkNotNull(getPrice(), "Price is null");
@ -199,6 +199,11 @@ public class Offer implements Serializable {
return id; return id;
} }
public PubKeyRing getPubKeyRing() {
return pubKeyRing;
}
public Fiat getPrice() { public Fiat getPrice() {
return Fiat.valueOf(currencyCode, fiatPrice); return Fiat.valueOf(currencyCode, fiatPrice);
} }
@ -256,10 +261,6 @@ public class Offer implements Serializable {
return bankAccountUID; return bankAccountUID;
} }
public PublicKey getP2pSigPubKey() {
return p2pSigPubKey;
}
public Date getCreationDate() { public Date getCreationDate() {
return creationDate; return creationDate;
} }
@ -282,7 +283,7 @@ public class Offer implements Serializable {
", fiatPrice=" + fiatPrice + ", fiatPrice=" + fiatPrice +
", amount=" + amount + ", amount=" + amount +
", minAmount=" + minAmount + ", minAmount=" + minAmount +
/* ", p2pSigPubKey=" + p2pSigPubKey +*/ /* ", pubKeyRing=" + pubKeyRing +*/
", fiatAccountType=" + fiatAccountType + ", fiatAccountType=" + fiatAccountType +
", bankAccountCountry=" + bankAccountCountry + ", bankAccountCountry=" + bankAccountCountry +
", securityDeposit=" + securityDeposit + ", securityDeposit=" + securityDeposit +

View file

@ -19,11 +19,11 @@ package io.bitsquare.offer.tomp2p;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferBookService; import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.tomp2p.TomP2PDHTService; import io.bitsquare.p2p.tomp2p.TomP2PDHTService;
import io.bitsquare.p2p.tomp2p.TomP2PNode; import io.bitsquare.p2p.tomp2p.TomP2PNode;
import io.bitsquare.user.User;
import java.io.IOException; import java.io.IOException;
@ -58,8 +58,8 @@ public class TomP2POfferBookService extends TomP2PDHTService implements OfferBoo
@Inject @Inject
public TomP2POfferBookService(TomP2PNode tomP2PNode, User user) { public TomP2POfferBookService(TomP2PNode tomP2PNode, KeyRing keyRing) {
super(tomP2PNode, user); super(tomP2PNode, keyRing);
} }
@Override @Override

View file

@ -18,10 +18,9 @@
package io.bitsquare.p2p; package io.bitsquare.p2p;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.p2p.listener.GetPeerAddressListener; import io.bitsquare.p2p.listener.GetPeerAddressListener;
import java.security.PublicKey;
public interface AddressService extends DHTService { public interface AddressService extends DHTService {
void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener getPeerAddressListener); void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener getPeerAddressListener);
} }

View file

@ -15,15 +15,10 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>. * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/ */
package io.bitsquare.crypto; package io.bitsquare.p2p;
import org.bitcoinj.core.Sha256Hash; import io.bitsquare.crypto.MessageWithPubKey;
import org.bitcoinj.core.Utils;
public class HashService { public interface DecryptedMessageHandler {
void handleMessage(MessageWithPubKey message, Peer sender);
public Sha256Hash hash(String message) {
byte[] data = Utils.formatMessageForSigning(message);
return Sha256Hash.hashTwice(data);
}
} }

View file

@ -17,8 +17,10 @@
package io.bitsquare.p2p; package io.bitsquare.p2p;
import io.bitsquare.crypto.SealedAndSignedMessage;
import java.util.List; import java.util.List;
public interface MailboxMessagesResultHandler { public interface MailboxMessagesResultHandler {
void handleResult(List<EncryptedMailboxMessage> encryptedMailboxMessages); void handleResult(List<SealedAndSignedMessage> encryptedMessages);
} }

View file

@ -19,14 +19,14 @@ package io.bitsquare.p2p;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.PubKeyRing;
import java.security.PublicKey; import io.bitsquare.crypto.SealedAndSignedMessage;
public interface MailboxService { 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);
} }

View file

@ -18,20 +18,20 @@
package io.bitsquare.p2p; package io.bitsquare.p2p;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.p2p.listener.SendMessageListener;
import org.bitcoinj.core.ECKey;
import java.security.PublicKey;
public interface MessageService extends P2PService { public interface MessageService extends P2PService {
void sendMessage(Peer peer, Message message, SendMessageListener listener); void sendMessage(Peer peer, Message message, SendMessageListener listener);
void sendMessage(Peer peer, Message message, PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair, void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener);
SendMessageListener listener);
void addMessageHandler(MessageHandler listener); void addMessageHandler(MessageHandler listener);
void removeMessageHandler(MessageHandler listener); void removeMessageHandler(MessageHandler listener);
void addDecryptedMessageHandler(DecryptedMessageHandler listener);
void removeDecryptedMessageHandler(DecryptedMessageHandler listener);
} }

View file

@ -17,16 +17,15 @@
package io.bitsquare.p2p.tomp2p; package io.bitsquare.p2p.tomp2p;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.NetworkException; import io.bitsquare.p2p.NetworkException;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.GetPeerAddressListener; import io.bitsquare.p2p.listener.GetPeerAddressListener;
import io.bitsquare.user.User;
import java.io.IOException; import java.io.IOException;
import java.security.PublicKey;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@ -63,10 +62,10 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public TomP2PAddressService(TomP2PNode tomP2PNode, User user) { public TomP2PAddressService(TomP2PNode tomP2PNode, KeyRing keyRing) {
super(tomP2PNode, user); super(tomP2PNode, keyRing);
locationKey = Utils.makeSHAHash(user.getP2pSigKeyPair().getPublic().getEncoded()); locationKey = Utils.makeSHAHash(keyRing.getPubKeyRing().getDhtSignaturePubKey().getEncoded());
} }
@Override @Override
@ -92,9 +91,9 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener listener) { public void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener listener) {
final Number160 locationKey = Utils.makeSHAHash(p2pSigPubKey.getEncoded()); final Number160 locationKey = Utils.makeSHAHash(pubKeyRing.getDhtSignaturePubKey().getEncoded());
FutureGet futureGet = getDataOfProtectedDomain(locationKey, p2pSigPubKey); FutureGet futureGet = getDataOfProtectedDomain(locationKey, pubKeyRing.getDhtSignaturePubKey());
log.trace("findPeerAddress called"); log.trace("findPeerAddress called");
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() { futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
@Override @Override

View file

@ -17,8 +17,8 @@
package io.bitsquare.p2p.tomp2p; package io.bitsquare.p2p.tomp2p;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.p2p.DHTService; import io.bitsquare.p2p.DHTService;
import io.bitsquare.user.User;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.PublicKey; import java.security.PublicKey;
@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
public class TomP2PDHTService extends TomP2PService implements DHTService { public class TomP2PDHTService extends TomP2PService implements DHTService {
private static final Logger log = LoggerFactory.getLogger(TomP2PDHTService.class); private static final Logger log = LoggerFactory.getLogger(TomP2PDHTService.class);
private final KeyPair keyPair; private final KeyPair dhtSignatureKeyPair;
private final Number160 pubKeyHashForMyDomain; private final Number160 pubKeyHashForMyDomain;
@ -47,10 +47,11 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public TomP2PDHTService(TomP2PNode tomP2PNode, User user) { public TomP2PDHTService(TomP2PNode tomP2PNode, KeyRing keyRing) {
super(tomP2PNode); super(tomP2PNode);
keyPair = user.getP2pSigKeyPair();
pubKeyHashForMyDomain = Utils.makeSHAHash(keyPair.getPublic().getEncoded()); dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair();
pubKeyHashForMyDomain = Utils.makeSHAHash(dhtSignatureKeyPair.getPublic().getEncoded());
} }
@Override @Override
@ -117,7 +118,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
*/ */
public FuturePut putDataToMyProtectedDomain(Number160 locationKey, Data data) { public FuturePut putDataToMyProtectedDomain(Number160 locationKey, Data data) {
log.trace("putDataToMyProtectedDomain"); log.trace("putDataToMyProtectedDomain");
data.protectEntry(keyPair); data.protectEntry(dhtSignatureKeyPair);
return peerDHT.put(locationKey).data(data).protectDomain().domainKey(pubKeyHashForMyDomain).start(); 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) { public FutureRemove removeDataFromMyProtectedDomain(Number160 locationKey) {
log.trace("removeDataOfProtectedDomain"); log.trace("removeDataOfProtectedDomain");
if (peerDHT != null) if (peerDHT != null)
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start(); return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start();
else else
return null; return null;
} }
@ -167,10 +168,10 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
*/ */
public FuturePut addProtectedDataToMap(Number160 locationKey, Data data) { public FuturePut addProtectedDataToMap(Number160 locationKey, Data data) {
log.trace("addProtectedDataToMap locationKey = " + locationKey); log.trace("addProtectedDataToMap locationKey = " + locationKey);
data.protectEntry(keyPair); data.protectEntry(dhtSignatureKeyPair);
log.trace("addProtectedDataToMap with contentKey " + data.hash().toString()); 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); log.trace("removeProtectedDataFromMap locationKey = " + locationKey);
Number160 contentKey = data.hash(); Number160 contentKey = data.hash();
log.trace("removeProtectedDataFromMap with contentKey " + contentKey.toString()); 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");
log.trace("addDataToMapOfProtectedDomain with contentKey " + data.hash().toString()); log.trace("addDataToMapOfProtectedDomain with contentKey " + data.hash().toString());
final Number160 pubKeyHashOfDomainOwner = Utils.makeSHAHash(publicKey.getEncoded()); final Number160 pubKeyHashOfDomainOwner = Utils.makeSHAHash(publicKey.getEncoded());
return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(keyPair) return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair)
.data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(keyPair).start(); .data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair).start();
} }
/** /**
@ -237,7 +238,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
log.trace("removeDataFromMapOfMyProtectedDomain"); log.trace("removeDataFromMapOfMyProtectedDomain");
Number160 contentKey = data.hash(); Number160 contentKey = data.hash();
log.trace("removeDataFromMapOfMyProtectedDomain with contentKey " + contentKey.toString()); 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) { public FutureRemove removeAllDataFromMapOfMyProtectedDomain(Number160 locationKey) {
log.trace("getDataFromMapOfMyProtectedDomain"); 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();
} }

View file

@ -19,14 +19,16 @@ package io.bitsquare.p2p.tomp2p;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; 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.offer.OfferBookService;
import io.bitsquare.p2p.EncryptedMailboxMessage;
import io.bitsquare.p2p.MailboxMessagesResultHandler; import io.bitsquare.p2p.MailboxMessagesResultHandler;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.user.User;
import java.io.IOException; import java.io.IOException;
import java.security.KeyPair;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.ArrayList; 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 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<OfferBookService.Listener> offerRepositoryListeners = new ArrayList<>(); private final List<OfferBookService.Listener> offerRepositoryListeners = new ArrayList<>();
private final KeyPair dhtSignatureKeyPair;
@Inject @Inject
public TomP2PMailboxService(TomP2PNode tomP2PNode, User user) { public TomP2PMailboxService(TomP2PNode tomP2PNode, KeyRing keyRing) {
super(tomP2PNode, user); super(tomP2PNode, keyRing);
dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair();
} }
@Override @Override
@ -70,25 +75,33 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
} }
@Override @Override
public void addMessage(PublicKey recipientP2pSigPubKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { public void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler) {
try { try {
final Data data = new Data(message); final Data data = new Data(message);
data.ttlSeconds(TTL); 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() + "]"); ", hash: " + data.hash().toString() + "]");
FuturePut futurePut = addDataToMapOfProtectedDomain(getLocationKey(recipientP2pSigPubKey), data, recipientP2pSigPubKey); FuturePut futurePut = addDataToMapOfProtectedDomain(locationKey,
data, pubKeyRing.getDhtSignaturePubKey());
futurePut.addListener(new BaseFutureListener<BaseFuture>() { futurePut.addListener(new BaseFutureListener<BaseFuture>() {
@Override @Override
public void operationComplete(BaseFuture future) throws Exception { public void operationComplete(BaseFuture future) throws Exception {
if (future.isSuccess()) { if (future.isSuccess()) {
executor.execute(() -> { executor.execute(() -> {
log.trace("Add message to mailbox was successful. Added data: [locationKey: " + locationKey + ", value: " + data + "]");
resultHandler.handleResult(); 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 @Override
@ -102,21 +115,21 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
} }
@Override @Override
public void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler) { public void getAllMessages(MailboxMessagesResultHandler resultHandler) {
log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(p2pSigPubKey)); log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic()));
FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey)); FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic()));
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() { futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
@Override @Override
public void operationComplete(BaseFuture future) throws Exception { public void operationComplete(BaseFuture future) throws Exception {
if (future.isSuccess()) { if (future.isSuccess()) {
final Map<Number640, Data> dataMap = futureGet.dataMap(); final Map<Number640, Data> dataMap = futureGet.dataMap();
List<EncryptedMailboxMessage> messages = new ArrayList<>(); List<SealedAndSignedMessage> messages = new ArrayList<>();
if (dataMap != null) { if (dataMap != null) {
for (Data messageData : dataMap.values()) { for (Data messageData : dataMap.values()) {
try { try {
Object messageDataObject = messageData.object(); Object messageDataObject = messageData.object();
if (messageDataObject instanceof EncryptedMailboxMessage) { if (messageDataObject instanceof SealedAndSignedMessage) {
messages.add((EncryptedMailboxMessage) messageDataObject); messages.add((SealedAndSignedMessage) messageDataObject);
} }
} catch (ClassNotFoundException | IOException e) { } catch (ClassNotFoundException | IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -125,7 +138,7 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
executor.execute(() -> resultHandler.handleResult(messages)); 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() + "]"); + ", values: " + futureGet.dataMap() + "]");
} }
else { else {
@ -143,9 +156,9 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
} }
@Override @Override
public void removeAllMessages(PublicKey p2pSigPubKey, ResultHandler resultHandler, FaultHandler faultHandler) { public void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler) {
log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(p2pSigPubKey)); log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic()));
FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey)); FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic()));
futureRemove.addListener(new BaseFutureListener<BaseFuture>() { futureRemove.addListener(new BaseFutureListener<BaseFuture>() {
@Override @Override
public void operationComplete(BaseFuture future) throws Exception { public void operationComplete(BaseFuture future) throws Exception {

View file

@ -17,8 +17,12 @@
package io.bitsquare.p2p.tomp2p; package io.bitsquare.p2p.tomp2p;
import io.bitsquare.crypto.EncryptionService; import io.bitsquare.crypto.CryptoException;
import io.bitsquare.p2p.EncryptedMailboxMessage; 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.MailboxMessage;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
@ -26,10 +30,7 @@ import io.bitsquare.p2p.MessageHandler;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.p2p.listener.SendMessageListener;
import io.bitsquare.util.Utilities;
import org.bitcoinj.core.ECKey;
import java.security.PublicKey;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@ -44,10 +45,12 @@ import org.slf4j.LoggerFactory;
public class TomP2PMessageService extends TomP2PService implements MessageService { public class TomP2PMessageService extends TomP2PService implements MessageService {
private static final Logger log = LoggerFactory.getLogger(TomP2PMessageService.class); 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<MessageHandler> messageHandlers = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<MessageHandler> messageHandlers = new CopyOnWriteArrayList<>();
private final CopyOnWriteArrayList<DecryptedMessageHandler> decryptedMessageHandlers = new CopyOnWriteArrayList<>();
private final MailboxService mailboxService; private final MailboxService mailboxService;
private final EncryptionService<MailboxMessage> encryptionService; private final CryptoService<MailboxMessage> cryptoService;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -55,10 +58,10 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, EncryptionService<MailboxMessage> encryptionService) { public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, CryptoService<MailboxMessage> cryptoService) {
super(tomP2PNode); super(tomP2PNode);
this.mailboxService = mailboxService; this.mailboxService = mailboxService;
this.encryptionService = encryptionService; this.cryptoService = cryptoService;
} }
@ -75,18 +78,29 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
@Override @Override
public void sendMessage(Peer peer, Message message, SendMessageListener listener) { public void sendMessage(Peer peer, Message message, SendMessageListener listener) {
sendMessage(peer, message, null, null, null, listener); doSendMessage(peer, null, message, listener);
} }
@Override @Override
public void sendMessage(Peer peer, Message message, PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair, public void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener) {
SendMessageListener listener) { assert pubKeyRing != null;
try {
doSendMessage(peer, pubKeyRing, cryptoService.encryptAndSignMessage(pubKeyRing, message), listener);
} catch (Throwable t) {
t.printStackTrace();
log.error(t.getMessage());
executor.execute(listener::handleFault);
}
}
private void doSendMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener) {
log.debug("sendMessage called");
if (peer == null) if (peer == null)
throw new IllegalArgumentException("Peer must not be null"); throw new IllegalArgumentException("Peer must not be null");
else if (!(peer instanceof TomP2PPeer)) else if (!(peer instanceof TomP2PPeer))
throw new IllegalArgumentException("Peer must be of type TomP2PPeer"); throw new IllegalArgumentException("Peer must be of type TomP2PPeer");
try {
FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(message).start(); FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(message).start();
futureDirect.addListener(new BaseFutureListener<BaseFuture>() { futureDirect.addListener(new BaseFutureListener<BaseFuture>() {
@Override @Override
@ -96,46 +110,35 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
executor.execute(listener::handleResult); executor.execute(listener::handleResult);
} }
else { else {
if (recipientP2pSigPubKey != null && recipientP2pEncryptPubKey != null) { log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " +
log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " + futureDirect.failedReason()); futureDirect.failedReason());
sendMailboxMessage(recipientP2pSigPubKey, recipientP2pEncryptPubKey, registrationKeyPair, (MailboxMessage) message, listener); if (pubKeyRing != null)
} sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) message, listener);
else {
log.error("sendMessage failed with reason " + futureDirect.failedReason());
executor.execute(listener::handleFault);
}
} }
} }
@Override @Override
public void exceptionCaught(Throwable t) throws Exception { 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: "
log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: " + t.getMessage()); + t.getMessage());
sendMailboxMessage(recipientP2pSigPubKey, recipientP2pEncryptPubKey, registrationKeyPair, (MailboxMessage) message, listener); if (pubKeyRing != null)
} sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) 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");
try {
result = encryptionService.encryptMessage(recipientP2pEncryptPubKey, registrationKeyPair, message);
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
log.error(t.getMessage()); log.error(t.getMessage());
executor.execute(listener::handleFault); executor.execute(listener::handleFault);
} }
EncryptedMailboxMessage encrypted = new EncryptedMailboxMessage(result); }
mailboxService.addMessage(recipientP2pSigPubKey,
encrypted,
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."); log.debug("Message successfully added to peers mailbox.");
executor.execute(listener::handleResult); executor.execute(listener::handleResult);
@ -159,6 +162,18 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
log.error("Try to remove listener which was never added."); 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 // Private
@ -166,21 +181,47 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
private void setupReplyHandler() { private void setupReplyHandler() {
peerDHT.peer().objectDataReply((sender, message) -> { peerDHT.peer().objectDataReply((sender, message) -> {
log.debug("handleMessage peerAddress " + sender); log.debug("Incoming message with peerAddress " + sender);
log.debug("handleMessage message " + message); 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 (!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)))); executor.execute(() -> messageHandlers.stream().forEach(e -> e.handleMessage((Message) message, new TomP2PPeer(sender))));
else 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 { else {
log.error("Received msg from myself. That must never happen."); log.error("Received msg from myself. That must never happen.");
} }
return true; return true;
}); });
} }
private MessageWithPubKey getDecryptedMessageWithPubKey(SealedAndSignedMessage message) throws CryptoException {
return cryptoService.decryptAndVerifyMessage((SealedAndSignedMessage) message);
}
} }

View file

@ -17,7 +17,7 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import io.bitsquare.crypto.Util; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
@ -25,8 +25,6 @@ import org.bitcoinj.core.Coin;
import java.io.Serializable; import java.io.Serializable;
import java.security.PublicKey;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -52,8 +50,8 @@ public class Contract implements Serializable {
String sellerAccountID, String sellerAccountID,
FiatAccount buyerFiatAccount, FiatAccount buyerFiatAccount,
FiatAccount sellerFiatAccount, FiatAccount sellerFiatAccount,
PublicKey buyerP2pSigPubKey, PubKeyRing buyerPubKeyRing,
PublicKey sellerP2pSigPubKey) { PubKeyRing sellerPubKeyRing) {
this.offer = offer; this.offer = offer;
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
this.takeOfferFeeTxID = takeOfferFeeTxID; this.takeOfferFeeTxID = takeOfferFeeTxID;
@ -61,8 +59,8 @@ public class Contract implements Serializable {
this.sellerAccountID = sellerAccountID; this.sellerAccountID = sellerAccountID;
this.buyerFiatAccount = buyerFiatAccount; this.buyerFiatAccount = buyerFiatAccount;
this.sellerFiatAccount = sellerFiatAccount; this.sellerFiatAccount = sellerFiatAccount;
this.buyerP2pSigPubKeyAsString = Util.getHexFromPubKey(buyerP2pSigPubKey); this.buyerP2pSigPubKeyAsString = buyerPubKeyRing.toString();
this.sellerP2pSigPubKeyAsString = Util.getHexFromPubKey(sellerP2pSigPubKey); this.sellerP2pSigPubKeyAsString = sellerPubKeyRing.toString();
} }
@Override @Override

View file

@ -22,10 +22,11 @@ import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.taskrunner.Model; 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.offer.Offer;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
@ -95,10 +96,10 @@ abstract public class Trade implements Model, Serializable {
private final Date creationDate; private final Date creationDate;
// Mutable // Mutable
private MessageWithPubKey messageWithPubKey;
private Date takeOfferDate; private Date takeOfferDate;
protected TradeState.ProcessState processState; protected TradeState.ProcessState processState;
protected TradeState.LifeCycleState lifeCycleState; protected TradeState.LifeCycleState lifeCycleState;
private MailboxMessage mailboxMessage;
private Transaction depositTx; private Transaction depositTx;
private Contract contract; private Contract contract;
private String contractAsJson; private String contractAsJson;
@ -156,9 +157,10 @@ abstract public class Trade implements Model, Serializable {
AddressService addressService, AddressService addressService,
TradeWalletService tradeWalletService, TradeWalletService tradeWalletService,
BlockChainService blockChainService, BlockChainService blockChainService,
SignatureService signatureService, CryptoService cryptoService,
ArbitrationRepository arbitrationRepository, ArbitrationRepository arbitrationRepository,
User user) { User user,
KeyRing keyRing) {
processModel.onAllServicesInitialized(offer, processModel.onAllServicesInitialized(offer,
messageService, messageService,
@ -166,18 +168,19 @@ abstract public class Trade implements Model, Serializable {
walletService, walletService,
tradeWalletService, tradeWalletService,
blockChainService, blockChainService,
signatureService, cryptoService,
arbitrationRepository, arbitrationRepository,
user); user,
keyRing);
createProtocol(); createProtocol();
tradeProtocol.checkPayoutTxTimeLock(this); tradeProtocol.checkPayoutTxTimeLock(this);
if (mailboxMessage != null) { if (messageWithPubKey != null) {
tradeProtocol.applyMailboxMessage(mailboxMessage, this); tradeProtocol.applyMailboxMessage(messageWithPubKey, this);
// After applied to protocol we remove it // 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) { public void setMailboxMessage(MessageWithPubKey messageWithPubKey) {
this.mailboxMessage = mailboxMessage; this.messageWithPubKey = messageWithPubKey;
} }
public void setStorage(Storage<? extends TradeList> storage) { public void setStorage(Storage<? extends TradeList> storage) {
@ -463,7 +466,7 @@ abstract public class Trade implements Model, Serializable {
", processModel=" + processModel + ", processModel=" + processModel +
", processState=" + processState + ", processState=" + processState +
", lifeCycleState=" + lifeCycleState + ", lifeCycleState=" + lifeCycleState +
", mailboxMessage=" + mailboxMessage + ", messageWithPubKey=" + messageWithPubKey +
", depositTx=" + depositTx + ", depositTx=" + depositTx +
/* ", contract=" + contract + /* ", contract=" + contract +
", contractAsJson='" + contractAsJson + '\'' +*/ ", contractAsJson='" + contractAsJson + '\'' +*/

View file

@ -25,15 +25,17 @@ import io.bitsquare.btc.WalletService;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.FaultHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.crypto.EncryptionService; import io.bitsquare.crypto.CryptoService;
import io.bitsquare.crypto.SignatureService; import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.crypto.SealedAndSignedMessage;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.offer.OfferBookService; import io.bitsquare.offer.OfferBookService;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.EncryptedMailboxMessage;
import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MailboxService;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.handlers.TakeOfferResultHandler; import io.bitsquare.trade.handlers.TakeOfferResultHandler;
@ -79,6 +81,7 @@ public class TradeManager {
private static final Logger log = LoggerFactory.getLogger(TradeManager.class); private static final Logger log = LoggerFactory.getLogger(TradeManager.class);
private final User user; private final User user;
private KeyRing keyRing;
private final AccountSettings accountSettings; private final AccountSettings accountSettings;
private final MessageService messageService; private final MessageService messageService;
private final MailboxService mailboxService; private final MailboxService mailboxService;
@ -86,8 +89,7 @@ public class TradeManager {
private final BlockChainService blockChainService; private final BlockChainService blockChainService;
private final WalletService walletService; private final WalletService walletService;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final SignatureService signatureService; private final CryptoService<MailboxMessage> cryptoService;
private final EncryptionService<MailboxMessage> encryptionService;
private final OfferBookService offerBookService; private final OfferBookService offerBookService;
private final ArbitrationRepository arbitrationRepository; private final ArbitrationRepository arbitrationRepository;
@ -107,6 +109,7 @@ public class TradeManager {
@Inject @Inject
public TradeManager(User user, public TradeManager(User user,
KeyRing keyRing,
AccountSettings accountSettings, AccountSettings accountSettings,
MessageService messageService, MessageService messageService,
MailboxService mailboxService, MailboxService mailboxService,
@ -114,12 +117,12 @@ public class TradeManager {
BlockChainService blockChainService, BlockChainService blockChainService,
WalletService walletService, WalletService walletService,
TradeWalletService tradeWalletService, TradeWalletService tradeWalletService,
SignatureService signatureService, CryptoService<MailboxMessage> cryptoService,
EncryptionService<MailboxMessage> encryptionService,
OfferBookService offerBookService, OfferBookService offerBookService,
ArbitrationRepository arbitrationRepository, ArbitrationRepository arbitrationRepository,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
this.user = user; this.user = user;
this.keyRing = keyRing;
this.accountSettings = accountSettings; this.accountSettings = accountSettings;
this.messageService = messageService; this.messageService = messageService;
this.mailboxService = mailboxService; this.mailboxService = mailboxService;
@ -127,8 +130,7 @@ public class TradeManager {
this.blockChainService = blockChainService; this.blockChainService = blockChainService;
this.walletService = walletService; this.walletService = walletService;
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.signatureService = signatureService; this.cryptoService = cryptoService;
this.encryptionService = encryptionService;
this.offerBookService = offerBookService; this.offerBookService = offerBookService;
this.arbitrationRepository = arbitrationRepository; 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 // 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 // We run that before initializing the pending trades to be sure the state is correct
mailboxService.getAllMessages(user.getP2pSigPubKey(), mailboxService.getAllMessages(
(encryptedMailboxMessages) -> { (encryptedMailboxMessages) -> {
log.trace("mailboxService.getAllMessages success"); log.trace("mailboxService.getAllMessages success");
setMailboxMessagesToTrades(encryptedMailboxMessages); setMailboxMessagesToTrades(encryptedMailboxMessages);
emptyMailbox(); //TODO testing
//emptyMailbox();
initPendingTrades(); initPendingTrades();
}); });
} }
private void setMailboxMessagesToTrades(List<EncryptedMailboxMessage> encryptedMailboxMessages) { private void setMailboxMessagesToTrades(List<SealedAndSignedMessage> encryptedMessages) {
log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMailboxMessages.size()); log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMessages.size());
for (EncryptedMailboxMessage encrypted : encryptedMailboxMessages) { for (SealedAndSignedMessage encrypted : encryptedMessages) {
try { try {
MailboxMessage mailboxMessage = encryptionService.decryptToMessage(user.getP2pEncryptPrivateKey(), encrypted.getBytes()); MessageWithPubKey messageWithPubKey = cryptoService.decryptAndVerifyMessage(encrypted);
if (mailboxMessage instanceof TradeMessage) { Message message = messageWithPubKey.getMessage();
String tradeId = ((TradeMessage) mailboxMessage).tradeId; if (message instanceof MailboxMessage && message instanceof TradeMessage) {
String tradeId = ((TradeMessage) message).tradeId;
Optional<Trade> tradeOptional = pendingTrades.stream().filter(e -> e.getId().equals(tradeId)).findAny(); Optional<Trade> tradeOptional = pendingTrades.stream().filter(e -> e.getId().equals(tradeId)).findAny();
if (tradeOptional.isPresent()) if (tradeOptional.isPresent())
tradeOptional.get().setMailboxMessage(mailboxMessage); tradeOptional.get().setMailboxMessage(messageWithPubKey);
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -195,7 +199,7 @@ public class TradeManager {
} }
private void emptyMailbox() { private void emptyMailbox() {
mailboxService.removeAllMessages(user.getP2pSigPubKey(), mailboxService.removeAllMessages(
() -> log.debug("All mailbox entries removed"), () -> log.debug("All mailbox entries removed"),
(errorMessage, fault) -> { (errorMessage, fault) -> {
log.error(errorMessage); log.error(errorMessage);
@ -259,7 +263,7 @@ public class TradeManager {
FiatAccount fiatAccount = user.currentFiatAccountProperty().get(); FiatAccount fiatAccount = user.currentFiatAccountProperty().get();
Offer offer = new Offer(id, Offer offer = new Offer(id,
user.getP2pSigPubKey(), keyRing.getPubKeyRing(),
direction, direction,
price.getValue(), price.getValue(),
amount, amount,
@ -352,6 +356,7 @@ public class TradeManager {
if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) {
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel( CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(
offer, offer,
keyRing.getPubKeyRing(),
messageService, messageService,
addressService); addressService);
@ -373,15 +378,15 @@ public class TradeManager {
// First we check if offer is still available then we create the trade with the protocol // 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) { 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, CheckOfferAvailabilityProtocol availabilityProtocol = new CheckOfferAvailabilityProtocol(model,
() -> handleCheckOfferAvailabilityResult(amount, offer, model, takeOfferResultHandler), () -> createTrade(amount, offer, model, takeOfferResultHandler),
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer)); (errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol); checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol);
availabilityProtocol.checkOfferAvailability(); availabilityProtocol.checkOfferAvailability();
} }
private void handleCheckOfferAvailabilityResult(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler private void createTrade(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler
takeOfferResultHandler) { takeOfferResultHandler) {
disposeCheckOfferAvailabilityRequest(offer); disposeCheckOfferAvailabilityRequest(offer);
if (offer.getState() == Offer.State.AVAILABLE) { if (offer.getState() == Offer.State.AVAILABLE) {
@ -487,9 +492,13 @@ public class TradeManager {
addressService, addressService,
tradeWalletService, tradeWalletService,
blockChainService, blockChainService,
signatureService, cryptoService,
arbitrationRepository, arbitrationRepository,
user); user,
keyRing);
} }
public boolean isMyOffer(Offer offer) {
return offer.getPubKeyRing().getHashString().equals(keyRing.getPubKeyRing().getHashString());
}
} }

View file

@ -18,6 +18,7 @@
package io.bitsquare.trade.protocol.availability; package io.bitsquare.trade.protocol.availability;
import io.bitsquare.common.taskrunner.Model; import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.MessageService;
@ -31,14 +32,16 @@ public class CheckOfferAvailabilityModel implements Model {
private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityModel.class); private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityModel.class);
public final Offer offer; public final Offer offer;
private final PubKeyRing pubKeyRing;
public final MessageService messageService; public final MessageService messageService;
public final AddressService addressService; public final AddressService addressService;
private Peer peer; private Peer peer;
private OfferMessage message; 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.offer = offer;
this.pubKeyRing = pubKeyRing;
this.messageService = messageService; this.messageService = messageService;
this.addressService = addressService; this.addressService = addressService;
} }
@ -68,4 +71,8 @@ public class CheckOfferAvailabilityModel implements Model {
public void onComplete() { public void onComplete() {
} }
public PubKeyRing getPubKeyRing() {
return pubKeyRing;
}
} }

View file

@ -20,14 +20,16 @@ package io.bitsquare.trade.protocol.availability;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.DecryptedMessageHandler;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.MessageHandler;
import io.bitsquare.p2p.Peer; 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.messages.ReportOfferAvailabilityMessage;
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress; import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage; 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; import org.bitcoinj.utils.Threading;
@ -37,6 +39,8 @@ import java.util.TimerTask;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class CheckOfferAvailabilityProtocol { public class CheckOfferAvailabilityProtocol {
private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityProtocol.class); private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityProtocol.class);
@ -45,7 +49,7 @@ public class CheckOfferAvailabilityProtocol {
private final CheckOfferAvailabilityModel model; private final CheckOfferAvailabilityModel model;
private final ResultHandler resultHandler; private final ResultHandler resultHandler;
private final ErrorMessageHandler errorMessageHandler; private final ErrorMessageHandler errorMessageHandler;
private final MessageHandler messageHandler; private final DecryptedMessageHandler decryptedMessageHandler;
private Timer timeoutTimer; private Timer timeoutTimer;
private boolean isCanceled; private boolean isCanceled;
@ -60,12 +64,12 @@ public class CheckOfferAvailabilityProtocol {
this.model = model; this.model = model;
this.resultHandler = resultHandler; this.resultHandler = resultHandler;
this.errorMessageHandler = errorMessageHandler; this.errorMessageHandler = errorMessageHandler;
messageHandler = this::handleMessage; decryptedMessageHandler = this::handleDecryptedMessageWithPubKey;
} }
private void cleanup() { private void cleanup() {
stopTimeout(); stopTimeout();
model.messageService.removeMessageHandler(messageHandler); model.messageService.removeDecryptedMessageHandler(decryptedMessageHandler);
} }
@ -77,7 +81,7 @@ public class CheckOfferAvailabilityProtocol {
// reset // reset
model.offer.setState(Offer.State.UNKNOWN); model.offer.setState(Offer.State.UNKNOWN);
model.messageService.addMessageHandler(messageHandler); model.messageService.addDecryptedMessageHandler(decryptedMessageHandler);
taskRunner = new TaskRunner<>(model, taskRunner = new TaskRunner<>(model,
() -> log.debug("sequence at onCheckOfferAvailability completed"), () -> log.debug("sequence at onCheckOfferAvailability completed"),
@ -85,7 +89,7 @@ public class CheckOfferAvailabilityProtocol {
); );
taskRunner.addTasks( taskRunner.addTasks(
GetPeerAddress.class, GetPeerAddress.class,
RequestIsOfferAvailable.class SendRequestIsOfferAvailableMessage.class
); );
startTimeout(); startTimeout();
taskRunner.run(); taskRunner.run();
@ -102,14 +106,18 @@ public class CheckOfferAvailabilityProtocol {
// Incoming message handling // Incoming message handling
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handleMessage(Message message, @SuppressWarnings("UnusedParameters") Peer sender) { protected void handleDecryptedMessageWithPubKey(MessageWithPubKey messageWithPubKey, Peer sender) {
if (!isCanceled) { Message message = messageWithPubKey.getMessage();
if (message instanceof ReportOfferAvailabilityMessage && model.offer.getId().equals(((ReportOfferAvailabilityMessage) message).offerId)) log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
handle((ReportOfferAvailabilityMessage) message); 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(); stopTimeout();
model.setMessage(message); model.setMessage(message);

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.availability.messages; package io.bitsquare.trade.protocol.availability.messages;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import java.io.Serializable; 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. // 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 long serialVersionUID = 1L;
public RequestIsOfferAvailableMessage(String tradeId) { private final PubKeyRing pubKeyRing;
public RequestIsOfferAvailableMessage(String tradeId, PubKeyRing pubKeyRing) {
super(tradeId); super(tradeId);
this.pubKeyRing = pubKeyRing;
}
public PubKeyRing getPubKeyRing() {
return pubKeyRing;
} }
} }

View file

@ -39,7 +39,7 @@ public class GetPeerAddress extends Task<CheckOfferAvailabilityModel> {
@Override @Override
protected void doRun() { protected void doRun() {
try { try {
model.addressService.findPeerAddress(model.offer.getP2pSigPubKey(), new GetPeerAddressListener() { model.addressService.findPeerAddress(model.offer.getPubKeyRing(), new GetPeerAddressListener() {
@Override @Override
public void onResult(Peer peer) { public void onResult(Peer peer) {
model.setPeer(peer); model.setPeer(peer);

View file

@ -27,17 +27,21 @@ import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailable
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class RequestIsOfferAvailable extends Task<CheckOfferAvailabilityModel> { public class SendRequestIsOfferAvailableMessage extends Task<CheckOfferAvailabilityModel> {
private static final Logger log = LoggerFactory.getLogger(RequestIsOfferAvailable.class); 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); super(taskHandler, model);
} }
@Override @Override
protected void doRun() { protected void doRun() {
try { 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() { new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {

View file

@ -17,7 +17,6 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.p2p.listener.SendMessageListener;
@ -49,24 +48,23 @@ import io.bitsquare.trade.states.OffererTradeState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 { public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtocol, OffererProtocol {
private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererProtocol.class); private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererProtocol.class);
private final BuyerAsOffererTrade buyerAsOffererTrade; private final BuyerAsOffererTrade buyerAsOffererTrade;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsOffererProtocol(BuyerAsOffererTrade trade) { public BuyerAsOffererProtocol(BuyerAsOffererTrade trade) {
super(trade.getProcessModel()); super(trade.getProcessModel());
log.debug("New OffererProtocol " + this); log.debug("New OffererProtocol " + this);
this.buyerAsOffererTrade = trade; this.buyerAsOffererTrade = trade;
messageHandler = this::handleMessage;
processModel.getMessageService().addMessageHandler(messageHandler);
} }
@ -75,16 +73,14 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade; this.trade = trade;
log.debug("setMailboxMessage " + mailboxMessage);
// Find first the actual peer address, as it might have changed in the meantime // 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) { if (message instanceof RequestFinalizePayoutTxMessage) {
handle((RequestFinalizePayoutTxMessage) mailboxMessage); handle((RequestFinalizePayoutTxMessage) message);
} }
}, },
(errorMessage -> { (errorMessage -> {
@ -98,17 +94,21 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// OpenOffer requests // 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 { 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 // 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 // to take the offer at the same time
// offer
// at the same time
boolean isOfferOpen = buyerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN; boolean isOfferOpen = buyerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN;
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen); ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
processModel.getMessageService().sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() { processModel.getMessageService().sendEncryptedMessage(sender,
message.getPubKeyRing(),
reportOfferAvailabilityMessage,
new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade. // Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
@ -117,6 +117,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
@Override @Override
public void handleFault() { 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."); log.warn("Sending ReportOfferAvailabilityMessage failed.");
} }
}); });
@ -214,13 +215,8 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// Massage dispatcher // Massage dispatcher
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handleMessage(Message message, Peer sender) { @Override
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message;
nonEmptyStringOf(tradeMessage.tradeId);
if (tradeMessage.tradeId.equals(buyerAsOffererTrade.getId())) {
if (tradeMessage instanceof RequestIsOfferAvailableMessage) { if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
handle((RequestIsOfferAvailableMessage) tradeMessage, sender); handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
} }
@ -234,9 +230,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
handle((RequestFinalizePayoutTxMessage) tradeMessage); handle((RequestFinalizePayoutTxMessage) tradeMessage);
} }
else { else {
log.error("Incoming tradeMessage not supported. " + tradeMessage); log.error("Incoming decrypted tradeMessage not supported. " + tradeMessage);
}
}
} }
} }
} }

View file

@ -17,7 +17,6 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.trade.BuyerAsTakerTrade; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol, TakerProtocol { public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol, TakerProtocol {
private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerProtocol.class); private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerProtocol.class);
@ -60,11 +57,11 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
public BuyerAsTakerProtocol(BuyerAsTakerTrade trade) { public BuyerAsTakerProtocol(BuyerAsTakerTrade trade) {
super(trade.getProcessModel()); super(trade.getProcessModel());
log.debug("New SellerAsTakerProtocol " + this); log.debug("New SellerAsTakerProtocol " + this);
this.buyerAsTakerTrade = trade; this.buyerAsTakerTrade = trade;
messageHandler = this::handleMessage; processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing());
processModel.getMessageService().addMessageHandler(messageHandler);
} }
@ -73,16 +70,14 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade; this.trade = trade;
log.debug("setMailboxMessage " + mailboxMessage);
// Find first the actual peer address, as it might have changed in the meantime // 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) { if (message instanceof RequestFinalizePayoutTxMessage) {
handle((RequestFinalizePayoutTxMessage) mailboxMessage); handle((RequestFinalizePayoutTxMessage) message);
} }
}, },
(errorMessage -> { (errorMessage -> {
@ -177,13 +172,8 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
// Massage dispatcher // Massage dispatcher
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handleMessage(Message message, Peer sender) { @Override
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message;
nonEmptyStringOf(tradeMessage.tradeId);
if (tradeMessage.tradeId.equals(processModel.getId())) {
if (tradeMessage instanceof RequestPublishDepositTxMessage) { if (tradeMessage instanceof RequestPublishDepositTxMessage) {
handle((RequestPublishDepositTxMessage) tradeMessage); handle((RequestPublishDepositTxMessage) tradeMessage);
} }
@ -194,6 +184,4 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
log.error("Incoming message not supported. " + tradeMessage); log.error("Incoming message not supported. " + tradeMessage);
} }
} }
}
}
} }

View file

@ -23,7 +23,9 @@ import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.taskrunner.Model; 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.fiat.FiatAccount;
import io.bitsquare.offer.Offer; import io.bitsquare.offer.Offer;
import io.bitsquare.p2p.AddressService; import io.bitsquare.p2p.AddressService;
@ -39,8 +41,6 @@ import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serializable; import java.io.Serializable;
import java.security.PublicKey;
import java.util.List; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -60,10 +60,11 @@ public class ProcessModel implements Model, Serializable {
transient private WalletService walletService; transient private WalletService walletService;
transient private TradeWalletService tradeWalletService; transient private TradeWalletService tradeWalletService;
transient private BlockChainService blockChainService; transient private BlockChainService blockChainService;
transient private SignatureService signatureService; transient private CryptoService cryptoService;
transient private ArbitrationRepository arbitrationRepository; transient private ArbitrationRepository arbitrationRepository;
transient private Offer offer; transient private Offer offer;
private transient User user; transient private User user;
transient private KeyRing keyRing;
// Mutable // Mutable
public final TradingPeer tradingPeer; public final TradingPeer tradingPeer;
@ -93,18 +94,20 @@ public class ProcessModel implements Model, Serializable {
WalletService walletService, WalletService walletService,
TradeWalletService tradeWalletService, TradeWalletService tradeWalletService,
BlockChainService blockChainService, BlockChainService blockChainService,
SignatureService signatureService, CryptoService cryptoService,
ArbitrationRepository arbitrationRepository, ArbitrationRepository arbitrationRepository,
User user) { User user,
KeyRing keyRing) {
this.offer = offer; this.offer = offer;
this.messageService = messageService; this.messageService = messageService;
this.addressService = addressService; this.addressService = addressService;
this.walletService = walletService; this.walletService = walletService;
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.blockChainService = blockChainService; this.blockChainService = blockChainService;
this.signatureService = signatureService; this.cryptoService = cryptoService;
this.arbitrationRepository = arbitrationRepository; this.arbitrationRepository = arbitrationRepository;
this.user = user; this.user = user;
this.keyRing = keyRing;
} }
@ -128,10 +131,6 @@ public class ProcessModel implements Model, Serializable {
return blockChainService; return blockChainService;
} }
public SignatureService getSignatureService() {
return signatureService;
}
public byte[] getArbitratorPubKey() { public byte[] getArbitratorPubKey() {
return arbitrationRepository.getDefaultArbitrator().getPubKey(); return arbitrationRepository.getDefaultArbitrator().getPubKey();
} }
@ -179,10 +178,6 @@ public class ProcessModel implements Model, Serializable {
return user.getAccountId(); return user.getAccountId();
} }
public PublicKey getP2pSigPubKey() {
return user.getP2pSigPubKey();
}
public byte[] getRegistrationPubKey() { public byte[] getRegistrationPubKey() {
return walletService.getRegistrationAddressEntry().getPubKey(); return walletService.getRegistrationAddressEntry().getPubKey();
} }
@ -195,10 +190,6 @@ public class ProcessModel implements Model, Serializable {
return getAddressEntry().getPubKey(); return getAddressEntry().getPubKey();
} }
public PublicKey getP2pEncryptPubKey() {
return user.getP2pEncryptPubKey();
}
@Nullable @Nullable
public List<TransactionOutput> getConnectedOutputsForAllInputs() { public List<TransactionOutput> getConnectedOutputsForAllInputs() {
return connectedOutputsForAllInputs; return connectedOutputsForAllInputs;
@ -263,4 +254,16 @@ public class ProcessModel implements Model, Serializable {
public AddressService getAddressService() { public AddressService getAddressService() {
return addressService; return addressService;
} }
public PubKeyRing getPubKeyRing() {
return keyRing.getPubKeyRing();
}
public CryptoService getCryptoService() {
return cryptoService;
}
public void setCryptoService(CryptoService cryptoService) {
this.cryptoService = cryptoService;
}
} }

View file

@ -17,7 +17,6 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.p2p.listener.SendMessageListener;
@ -49,7 +48,7 @@ import io.bitsquare.trade.states.OffererTradeState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 { public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class); private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class);
@ -63,11 +62,9 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
public SellerAsOffererProtocol(SellerAsOffererTrade trade) { public SellerAsOffererProtocol(SellerAsOffererTrade trade) {
super(trade.getProcessModel()); super(trade.getProcessModel());
log.debug("New OffererProtocol " + this); log.debug("New OffererProtocol " + this);
this.sellerAsOffererTrade = trade; this.sellerAsOffererTrade = trade;
messageHandler = this::handleMessage;
processModel.getMessageService().addMessageHandler(messageHandler);
} }
@ -76,22 +73,21 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade; this.trade = trade;
log.debug("setMailboxMessage " + mailboxMessage); if (message instanceof PayoutTxFinalizedMessage) {
// Find first the actual peer address, as it might have changed in the meantime handle((PayoutTxFinalizedMessage) message);
if (mailboxMessage instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) mailboxMessage);
} }
else { 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) { if (message instanceof FiatTransferStartedMessage) {
handle((FiatTransferStartedMessage) mailboxMessage); handle((FiatTransferStartedMessage) message);
} }
else if (mailboxMessage instanceof DepositTxPublishedMessage) { else if (message instanceof DepositTxPublishedMessage) {
handle((DepositTxPublishedMessage) mailboxMessage); handle((DepositTxPublishedMessage) message);
} }
}, },
(errorMessage -> { (errorMessage -> {
@ -109,18 +105,19 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// IsOfferAvailable // IsOfferAvailable
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(RequestIsOfferAvailableMessage tradeMessage, Peer sender) { private void handle(RequestIsOfferAvailableMessage message, Peer sender) {
try { 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 // 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 // to take the offer at the same time
// offer
// at the same time
boolean isOfferOpen = sellerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN; boolean isOfferOpen = sellerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN;
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen); ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
processModel.getMessageService().sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() { processModel.getMessageService().sendEncryptedMessage(sender,
message.getPubKeyRing(),
reportOfferAvailabilityMessage,
new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade. // Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
@ -129,6 +126,7 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
@Override @Override
public void handleFault() { 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."); log.warn("Sending ReportOfferAvailabilityMessage failed.");
} }
}); });
@ -239,12 +237,8 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// Massage dispatcher // Massage dispatcher
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handleMessage(Message message, Peer sender) { @Override
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message;
nonEmptyStringOf(tradeMessage.tradeId);
if (tradeMessage.tradeId.equals(sellerAsOffererTrade.getId())) {
if (tradeMessage instanceof RequestIsOfferAvailableMessage) { if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
handle((RequestIsOfferAvailableMessage) tradeMessage, sender); handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
} }
@ -264,6 +258,4 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
log.error("Incoming tradeMessage not supported. " + tradeMessage); log.error("Incoming tradeMessage not supported. " + tradeMessage);
} }
} }
}
}
} }

View file

@ -17,7 +17,6 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.p2p.MailboxMessage;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.trade.SellerAsTakerTrade; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtocol, TakerProtocol { public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtocol, TakerProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerAsTakerProtocol.class); private static final Logger log = LoggerFactory.getLogger(SellerAsTakerProtocol.class);
@ -62,11 +59,11 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
public SellerAsTakerProtocol(SellerAsTakerTrade trade) { public SellerAsTakerProtocol(SellerAsTakerTrade trade) {
super(trade.getProcessModel()); super(trade.getProcessModel());
log.debug("New SellerAsTakerProtocol " + this); log.debug("New SellerAsTakerProtocol " + this);
this.sellerAsTakerTrade = trade; this.sellerAsTakerTrade = trade;
messageHandler = this::handleMessage; processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing());
processModel.getMessageService().addMessageHandler(messageHandler);
} }
@ -75,23 +72,21 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade; this.trade = trade;
log.debug("setMailboxMessage " + mailboxMessage); if (message instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) message);
// Find first the actual peer address, as it might have changed in the meantime
if (mailboxMessage instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) mailboxMessage);
} }
else { 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) { if (message instanceof FiatTransferStartedMessage) {
handle((FiatTransferStartedMessage) mailboxMessage); handle((FiatTransferStartedMessage) message);
} }
else if (mailboxMessage instanceof DepositTxPublishedMessage) { else if (message instanceof DepositTxPublishedMessage) {
handle((DepositTxPublishedMessage) mailboxMessage); handle((DepositTxPublishedMessage) message);
} }
}, },
(errorMessage -> { (errorMessage -> {
@ -217,13 +212,8 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
// Massage dispatcher // Massage dispatcher
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handleMessage(Message message, Peer sender) { @Override
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender); protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message;
nonEmptyStringOf(tradeMessage.tradeId);
if (tradeMessage.tradeId.equals(processModel.getId())) {
if (tradeMessage instanceof RequestPayDepositMessage) { if (tradeMessage instanceof RequestPayDepositMessage) {
handle((RequestPayDepositMessage) tradeMessage); handle((RequestPayDepositMessage) tradeMessage);
} }
@ -237,10 +227,7 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
handle((PayoutTxFinalizedMessage) tradeMessage); handle((PayoutTxFinalizedMessage) tradeMessage);
} }
else { else {
log.error("Incoming message not supported. " + tradeMessage); log.error("Incoming message not supported. " + tradeMessage);
} }
} }
}
}
} }

View file

@ -19,51 +19,67 @@ package io.bitsquare.trade.protocol.trade;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.crypto.MessageWithPubKey;
import io.bitsquare.p2p.MessageHandler; import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.p2p.DecryptedMessageHandler;
import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.Peer;
import io.bitsquare.p2p.listener.GetPeerAddressListener; import io.bitsquare.p2p.listener.GetPeerAddressListener;
import io.bitsquare.trade.OffererTrade; import io.bitsquare.trade.OffererTrade;
import io.bitsquare.trade.TakerTrade; import io.bitsquare.trade.TakerTrade;
import io.bitsquare.trade.Trade; 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.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.OffererTradeState;
import io.bitsquare.trade.states.TakerTradeState; import io.bitsquare.trade.states.TakerTradeState;
import org.bitcoinj.utils.Threading; import org.bitcoinj.utils.Threading;
import java.security.PublicKey;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
public abstract class TradeProtocol { public abstract class TradeProtocol {
private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class); private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class);
private static final long TIMEOUT = 10000; private static final long TIMEOUT = 10000;
protected final ProcessModel processModel; protected final ProcessModel processModel;
protected MessageHandler messageHandler; private DecryptedMessageHandler decryptedMessageHandler;
protected Timer timeoutTimer; protected Timer timeoutTimer;
protected Trade trade; protected Trade trade;
public TradeProtocol(ProcessModel processModel) { public TradeProtocol(ProcessModel processModel) {
this.processModel = processModel; this.processModel = processModel;
decryptedMessageHandler = this::handleMessageWithPubKey;
processModel.getMessageService().addDecryptedMessageHandler(decryptedMessageHandler);
} }
public void cleanup() { public void cleanup() {
log.debug("cleanup " + this); log.debug("cleanup " + this);
stopTimeout(); 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 { try {
processModel.getAddressService().findPeerAddress(p2pSigPubKey, new GetPeerAddressListener() { processModel.getAddressService().findPeerAddress(pubKeyRing, new GetPeerAddressListener() {
@Override @Override
public void onResult(Peer peer) { public void onResult(Peer peer) {
trade.setTradingPeer(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) { public void checkPayoutTxTimeLock(Trade trade) {
this.trade = trade; this.trade = trade;

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
@ -26,8 +27,6 @@ import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.Serializable; import java.io.Serializable;
import java.security.PublicKey;
import java.util.List; import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -41,9 +40,6 @@ public class TradingPeer implements Serializable {
// Mutable // Mutable
private String accountId; private String accountId;
private PublicKey p2pSigPubKey;
private PublicKey p2pEncryptPubKey;
private byte[] tradeWalletPubKey;
private FiatAccount fiatAccount; private FiatAccount fiatAccount;
private Transaction preparedDepositTx; private Transaction preparedDepositTx;
private List<TransactionOutput> connectedOutputsForAllInputs; private List<TransactionOutput> connectedOutputsForAllInputs;
@ -54,6 +50,8 @@ public class TradingPeer implements Serializable {
private String contractAsJson; private String contractAsJson;
private String contractSignature; private String contractSignature;
private byte[] signature; private byte[] signature;
private PubKeyRing pubKeyRing;
private byte[] tradeWalletPubKey;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -82,22 +80,6 @@ public class TradingPeer implements Serializable {
this.accountId = 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() { public byte[] getTradeWalletPubKey() {
return tradeWalletPubKey; return tradeWalletPubKey;
} }
@ -185,4 +167,12 @@ public class TradingPeer implements Serializable {
public byte[] getSignature() { public byte[] getSignature() {
return signature; return signature;
} }
public PubKeyRing getPubKeyRing() {
return pubKeyRing;
}
public void setPubKeyRing(PubKeyRing pubKeyRing) {
this.pubKeyRing = pubKeyRing;
}
} }

View file

@ -17,6 +17,8 @@
package io.bitsquare.trade.protocol.trade.messages; package io.bitsquare.trade.protocol.trade.messages;
import io.bitsquare.crypto.PubKeyRing;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import java.io.Serializable; import java.io.Serializable;
@ -29,11 +31,14 @@ public class RequestDepositTxInputsMessage extends TradeMessage implements Seria
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public final Coin tradeAmount; public final Coin tradeAmount;
public final PubKeyRing sellerPubKeyRing;
public final String sellerOfferFeeTxId; public final String sellerOfferFeeTxId;
public final byte[] sellerTradeWalletPubKey; 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); super(tradeId);
this.sellerPubKeyRing = sellerPubKeyRing;
this.sellerOfferFeeTxId = sellerOfferFeeTxId; this.sellerOfferFeeTxId = sellerOfferFeeTxId;
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
this.sellerTradeWalletPubKey = sellerTradeWalletPubKey; this.sellerTradeWalletPubKey = sellerTradeWalletPubKey;

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.trade.messages; package io.bitsquare.trade.protocol.trade.messages;
import io.bitsquare.crypto.PubKeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -24,8 +25,6 @@ import org.bitcoinj.core.TransactionOutput;
import java.io.Serializable; import java.io.Serializable;
import java.security.PublicKey;
import java.util.List; import java.util.List;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@ -38,8 +37,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
public final List<TransactionOutput> buyerConnectedOutputsForAllInputs; public final List<TransactionOutput> buyerConnectedOutputsForAllInputs;
public final List<TransactionOutput> buyerOutputs; public final List<TransactionOutput> buyerOutputs;
public final byte[] buyerTradeWalletPubKey; public final byte[] buyerTradeWalletPubKey;
public final PublicKey buyerP2pSigPublicKey; public final PubKeyRing buyerPubKeyRing;
public final PublicKey buyerP2pEncryptPublicKey;
public final FiatAccount buyerFiatAccount; public final FiatAccount buyerFiatAccount;
public final String buyerAccountId; public final String buyerAccountId;
public final Coin tradeAmount; public final Coin tradeAmount;
@ -49,14 +47,12 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
List<TransactionOutput> buyerConnectedOutputsForAllInputs, List<TransactionOutput> buyerConnectedOutputsForAllInputs,
List<TransactionOutput> buyerOutputs, List<TransactionOutput> buyerOutputs,
byte[] buyerTradeWalletPubKey, byte[] buyerTradeWalletPubKey,
PublicKey buyerP2pSigPublicKey, PubKeyRing buyerPubKeyRing,
PublicKey buyerP2pEncryptPublicKey,
FiatAccount buyerFiatAccount, FiatAccount buyerFiatAccount,
String buyerAccountId) { String buyerAccountId) {
super(tradeId); super(tradeId);
this.tradeAmount = tradeAmount; this.tradeAmount = tradeAmount;
this.buyerP2pSigPublicKey = buyerP2pSigPublicKey; this.buyerPubKeyRing = buyerPubKeyRing;
this.buyerP2pEncryptPublicKey = buyerP2pEncryptPublicKey;
this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs; this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs;
this.buyerOutputs = buyerOutputs; this.buyerOutputs = buyerOutputs;
this.buyerTradeWalletPubKey = buyerTradeWalletPubKey; this.buyerTradeWalletPubKey = buyerTradeWalletPubKey;

View file

@ -24,8 +24,6 @@ import org.bitcoinj.core.TransactionOutput;
import java.io.Serializable; import java.io.Serializable;
import java.security.PublicKey;
import java.util.List; import java.util.List;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@ -37,8 +35,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri
public final FiatAccount sellerFiatAccount; public final FiatAccount sellerFiatAccount;
public final String sellerAccountId; public final String sellerAccountId;
public final PublicKey sellerP2pSigPublicKey;
public final PublicKey sellerP2pEncryptPublicKey;
public final String sellerContractAsJson; public final String sellerContractAsJson;
public final String sellerContractSignature; public final String sellerContractSignature;
public final String sellerPayoutAddressString; public final String sellerPayoutAddressString;
@ -50,8 +46,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri
FiatAccount sellerFiatAccount, FiatAccount sellerFiatAccount,
String sellerAccountId, String sellerAccountId,
byte[] sellerTradeWalletPubKey, byte[] sellerTradeWalletPubKey,
PublicKey sellerP2pSigPublicKey,
PublicKey sellerP2pEncryptPublicKey,
String sellerContractAsJson, String sellerContractAsJson,
String sellerContractSignature, String sellerContractSignature,
String sellerPayoutAddressString, String sellerPayoutAddressString,
@ -61,8 +55,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri
this.sellerFiatAccount = sellerFiatAccount; this.sellerFiatAccount = sellerFiatAccount;
this.sellerAccountId = sellerAccountId; this.sellerAccountId = sellerAccountId;
this.sellerTradeWalletPubKey = sellerTradeWalletPubKey; this.sellerTradeWalletPubKey = sellerTradeWalletPubKey;
this.sellerP2pSigPublicKey = sellerP2pSigPublicKey;
this.sellerP2pEncryptPublicKey = sellerP2pEncryptPublicKey;
this.sellerContractAsJson = sellerContractAsJson; this.sellerContractAsJson = sellerContractAsJson;
this.sellerContractSignature = sellerContractSignature; this.sellerContractSignature = sellerContractSignature;
this.sellerPayoutAddressString = sellerPayoutAddressString; this.sellerPayoutAddressString = sellerPayoutAddressString;

View file

@ -43,8 +43,9 @@ public class ProcessRequestDepositTxInputsMessage extends TradeTask {
checkTradeId(processModel.getId(), message); checkTradeId(processModel.getId(), message);
checkNotNull(message); checkNotNull(message);
trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount))); processModel.tradingPeer.setPubKeyRing(checkNotNull(message.sellerPubKeyRing));
processModel.setTakeOfferFeeTxId(nonEmptyStringOf(message.sellerOfferFeeTxId)); processModel.setTakeOfferFeeTxId(nonEmptyStringOf(message.sellerOfferFeeTxId));
trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount)));
processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey)); processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey));
complete(); complete();

View file

@ -45,9 +45,7 @@ public class ProcessRequestPublishDepositTxMessage extends TradeTask {
processModel.tradingPeer.setFiatAccount(checkNotNull(message.sellerFiatAccount)); processModel.tradingPeer.setFiatAccount(checkNotNull(message.sellerFiatAccount));
processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.sellerAccountId)); processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.sellerAccountId));
processModel.tradingPeer.setP2pSigPubKey(checkNotNull(message.sellerP2pSigPublicKey));
processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey)); processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey));
processModel.tradingPeer.setP2pEncryptPubKey(checkNotNull(message.sellerP2pEncryptPublicKey));
processModel.tradingPeer.setContractAsJson(nonEmptyStringOf(message.sellerContractAsJson)); processModel.tradingPeer.setContractAsJson(nonEmptyStringOf(message.sellerContractAsJson));
processModel.tradingPeer.setContractSignature(nonEmptyStringOf(message.sellerContractSignature)); processModel.tradingPeer.setContractSignature(nonEmptyStringOf(message.sellerContractSignature));
processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddressString)); processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddressString));

View file

@ -39,7 +39,11 @@ public class SendDepositTxPublishedMessage extends TradeTask {
try { try {
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(), trade.getDepositTx()); DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(), trade.getDepositTx());
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() { processModel.getMessageService().sendEncryptedMessage(
trade.getTradingPeer(),
processModel.tradingPeer.getPubKeyRing(),
tradeMessage,
new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("DepositTxPublishedMessage successfully arrived at peer"); log.trace("DepositTxPublishedMessage successfully arrived at peer");

View file

@ -45,10 +45,10 @@ public class SendFiatTransferStartedMessage extends TradeTask {
processModel.getAddressEntry().getAddressString() processModel.getAddressEntry().getAddressString()
); );
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, processModel.getMessageService().sendEncryptedMessage(
processModel.tradingPeer.getP2pSigPubKey(), trade.getTradingPeer(),
processModel.tradingPeer.getP2pEncryptPubKey(), processModel.tradingPeer.getPubKeyRing(),
processModel.getRegistrationKeyPair(), tradeMessage,
new SendMessageListener() { new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {

View file

@ -38,11 +38,10 @@ public class SendPayoutTxFinalizedMessage extends TradeTask {
protected void doRun() { protected void doRun() {
try { try {
PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx()); PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx());
processModel.getMessageService().sendMessage(trade.getTradingPeer(), processModel.getMessageService().sendEncryptedMessage(
trade.getTradingPeer(),
processModel.tradingPeer.getPubKeyRing(),
tradeMessage, tradeMessage,
processModel.tradingPeer.getP2pSigPubKey(),
processModel.tradingPeer.getP2pEncryptPubKey(),
processModel.getRegistrationKeyPair(),
new SendMessageListener() { new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {

View file

@ -43,12 +43,15 @@ public class SendRequestPayDepositMessage extends TradeTask {
processModel.getConnectedOutputsForAllInputs(), processModel.getConnectedOutputsForAllInputs(),
processModel.getOutputs(), processModel.getOutputs(),
processModel.getTradeWalletPubKey(), processModel.getTradeWalletPubKey(),
processModel.getP2pSigPubKey(), processModel.getPubKeyRing(),
processModel.getP2pEncryptPubKey(),
processModel.getFiatAccount(), processModel.getFiatAccount(),
processModel.getAccountId()); processModel.getAccountId());
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() { processModel.getMessageService().sendEncryptedMessage(
trade.getTradingPeer(),
processModel.tradingPeer.getPubKeyRing(),
tradeMessage,
new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer"); log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");

View file

@ -45,10 +45,10 @@ public class VerifyAndSignContract extends TradeTask {
processModel.tradingPeer.getAccountId(), processModel.tradingPeer.getAccountId(),
processModel.getFiatAccount(), processModel.getFiatAccount(),
processModel.tradingPeer.getFiatAccount(), processModel.tradingPeer.getFiatAccount(),
processModel.getP2pSigPubKey(), processModel.getPubKeyRing(),
processModel.tradingPeer.getP2pSigPubKey()); processModel.tradingPeer.getPubKeyRing());
String contractAsJson = Utilities.objectToJson(contract); 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.setContract(contract);
trade.setContractAsJson(contractAsJson); trade.setContractAsJson(contractAsJson);
trade.setBuyerContractSignature(signature); trade.setBuyerContractSignature(signature);

View file

@ -45,10 +45,10 @@ public class CreateAndSignContract extends TradeTask {
processModel.getAccountId(), processModel.getAccountId(),
processModel.tradingPeer.getFiatAccount(), processModel.tradingPeer.getFiatAccount(),
processModel.getFiatAccount(), processModel.getFiatAccount(),
processModel.tradingPeer.getP2pSigPubKey(), processModel.tradingPeer.getPubKeyRing(),
processModel.getP2pSigPubKey()); processModel.getPubKeyRing());
String contractAsJson = Utilities.objectToJson(contract); 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.setContract(contract);
trade.setContractAsJson(contractAsJson); trade.setContractAsJson(contractAsJson);

View file

@ -46,8 +46,7 @@ public class ProcessRequestPayDepositMessage extends TradeTask {
checkArgument(message.buyerConnectedOutputsForAllInputs.size() > 0); checkArgument(message.buyerConnectedOutputsForAllInputs.size() > 0);
processModel.tradingPeer.setOutputs(checkNotNull(message.buyerOutputs)); processModel.tradingPeer.setOutputs(checkNotNull(message.buyerOutputs));
processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.buyerTradeWalletPubKey)); processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.buyerTradeWalletPubKey));
processModel.tradingPeer.setP2pSigPubKey(checkNotNull(message.buyerP2pSigPublicKey)); processModel.tradingPeer.setPubKeyRing(checkNotNull(message.buyerPubKeyRing));
processModel.tradingPeer.setP2pEncryptPubKey(checkNotNull(message.buyerP2pEncryptPublicKey));
processModel.tradingPeer.setFiatAccount(checkNotNull(message.buyerFiatAccount)); processModel.tradingPeer.setFiatAccount(checkNotNull(message.buyerFiatAccount));
processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.buyerAccountId)); processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.buyerAccountId));
trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount))); trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount)));

View file

@ -44,11 +44,16 @@ public class SendRequestDepositTxInputsMessage extends TradeTask {
assert processModel.getTakeOfferFeeTx() != null; assert processModel.getTakeOfferFeeTx() != null;
RequestDepositTxInputsMessage message = new RequestDepositTxInputsMessage( RequestDepositTxInputsMessage message = new RequestDepositTxInputsMessage(
processModel.getId(), processModel.getId(),
processModel.getPubKeyRing(),
processModel.getTakeOfferFeeTx().getHashAsString(), processModel.getTakeOfferFeeTx().getHashAsString(),
trade.getTradeAmount(), trade.getTradeAmount(),
processModel.getTradeWalletPubKey()); processModel.getTradeWalletPubKey());
processModel.getMessageService().sendMessage(trade.getTradingPeer(), message, new SendMessageListener() { processModel.getMessageService().sendEncryptedMessage(
trade.getTradingPeer(),
processModel.tradingPeer.getPubKeyRing(),
message,
new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
log.trace("Sending TakeOfferFeePayedMessage succeeded."); log.trace("Sending TakeOfferFeePayedMessage succeeded.");

View file

@ -48,11 +48,10 @@ public class SendRequestFinalizePayoutTxMessage extends TradeTask {
trade.getLockTime() trade.getLockTime()
); );
processModel.getMessageService().sendMessage(trade.getTradingPeer(), processModel.getMessageService().sendEncryptedMessage(
trade.getTradingPeer(),
processModel.tradingPeer.getPubKeyRing(),
message, message,
processModel.tradingPeer.getP2pSigPubKey(),
processModel.tradingPeer.getP2pEncryptPubKey(),
processModel.getRegistrationKeyPair(),
new SendMessageListener() { new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {

View file

@ -42,8 +42,6 @@ public class SendRequestPublishDepositTxMessage extends TradeTask {
processModel.getFiatAccount(), processModel.getFiatAccount(),
processModel.getAccountId(), processModel.getAccountId(),
processModel.getTradeWalletPubKey(), processModel.getTradeWalletPubKey(),
processModel.getP2pSigPubKey(),
processModel.getP2pEncryptPubKey(),
trade.getContractAsJson(), trade.getContractAsJson(),
trade.getSellerContractSignature(), trade.getSellerContractSignature(),
processModel.getAddressEntry().getAddressString(), processModel.getAddressEntry().getAddressString(),
@ -51,7 +49,11 @@ public class SendRequestPublishDepositTxMessage extends TradeTask {
processModel.getConnectedOutputsForAllInputs() processModel.getConnectedOutputsForAllInputs()
); );
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() { processModel.getMessageService().sendEncryptedMessage(
trade.getTradingPeer(),
processModel.tradingPeer.getPubKeyRing(),
tradeMessage,
new SendMessageListener() {
@Override @Override
public void handleResult() { public void handleResult() {
complete(); complete();

View file

@ -17,18 +17,12 @@
package io.bitsquare.user; package io.bitsquare.user;
import io.bitsquare.crypto.EncryptionService;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import com.google.common.base.Throwables;
import java.io.Serializable; import java.io.Serializable;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 static final Logger log = LoggerFactory.getLogger(User.class);
transient private Storage<User> storage; // Transient immutable fields
transient final private Storage<User> storage;
// Persisted fields // Persisted fields
private KeyPair p2pSigKeyPair;
private KeyPair p2pEncryptKeyPair;
private String accountID; private String accountID;
private List<FiatAccount> fiatAccounts = new ArrayList<>(); private List<FiatAccount> fiatAccounts = new ArrayList<>();
private FiatAccount currentFiatAccount; private FiatAccount currentFiatAccount;
// Observable wrappers // Observable wrappers
final transient private ObservableList<FiatAccount> fiatAccountsObservableList = FXCollections.observableArrayList(fiatAccounts); transient final private ObservableList<FiatAccount> fiatAccountsObservableList = FXCollections.observableArrayList(fiatAccounts);
final transient private ObjectProperty<FiatAccount> currentFiatAccountProperty = new SimpleObjectProperty<>(currentFiatAccount); transient final private ObjectProperty<FiatAccount> currentFiatAccountProperty = new SimpleObjectProperty<>(currentFiatAccount);
@Inject @Inject
public User(Storage<User> storage, EncryptionService encryptionService) { public User(Storage<User> storage) throws NoSuchAlgorithmException {
this.storage = storage; this.storage = storage;
EncryptionService encryptionService1 = encryptionService;
User persisted = storage.initAndGetPersisted(this); User persisted = storage.initAndGetPersisted(this);
if (persisted != null) { if (persisted != null) {
p2pSigKeyPair = persisted.getP2pSigKeyPair();
p2pEncryptKeyPair = persisted.getP2pEncryptKeyPair();
accountID = persisted.getAccountId(); accountID = persisted.getAccountId();
fiatAccounts = new ArrayList<>(persisted.getFiatAccounts()); fiatAccounts = new ArrayList<>(persisted.getFiatAccounts());
@ -86,17 +76,6 @@ public class User implements Serializable {
currentFiatAccount = persisted.getCurrentFiatAccount(); currentFiatAccount = persisted.getCurrentFiatAccount();
currentFiatAccountProperty.set(currentFiatAccount); 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(); storage.queueUpForSave();
// Use that to guarantee update of the serializable field and to make a storage update in case of a change // Use that to guarantee update of the serializable field and to make a storage update in case of a change
fiatAccountsObservableList.addListener((ListChangeListener<FiatAccount>) change -> { fiatAccountsObservableList.addListener((ListChangeListener<FiatAccount>) change -> {
@ -111,6 +90,7 @@ public class User implements Serializable {
// for unit tests // for unit tests
public User() { public User() {
this.storage = null;
} }
@ -196,26 +176,6 @@ public class User implements Serializable {
return getAccountId() != null; 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<FiatAccount> getFiatAccounts() { private List<FiatAccount> getFiatAccounts() {
return fiatAccounts; return fiatAccounts;
} }
@ -231,5 +191,4 @@ public class User implements Serializable {
public ObservableList<FiatAccount> fiatAccountsObservableList() { public ObservableList<FiatAccount> fiatAccountsObservableList() {
return fiatAccountsObservableList; return fiatAccountsObservableList;
} }
} }

View file

@ -26,6 +26,7 @@ import java.awt.*;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInput; import java.io.ObjectInput;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
@ -200,6 +201,15 @@ public class Utilities {
return result; 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) { private static void printElapsedTime(String msg) {
if (!msg.isEmpty()) { if (!msg.isEmpty()) {
msg += " / "; msg += " / ";

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<MailboxMessage> 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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<MailboxMessage> 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<Integer> 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<MailboxMessage> 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<MailboxMessage> 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<MailboxMessage> 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<MailboxMessage> 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<MailboxMessage> 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<MailboxMessage> 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<MailboxMessage> 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;
}
}

View file

@ -19,6 +19,7 @@ package io.bitsquare.app;
import io.bitsquare.btc.UserAgent; import io.bitsquare.btc.UserAgent;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.KeyStorage;
import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainView;
import io.bitsquare.p2p.tomp2p.TomP2PModule; import io.bitsquare.p2p.tomp2p.TomP2PModule;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
@ -59,6 +60,7 @@ public class BitsquareAppEnvironment extends BitsquareEnvironment {
setProperty(WalletService.PREFIX_KEY, appName); setProperty(WalletService.PREFIX_KEY, appName);
setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString()); setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString());
setProperty(KeyStorage.DIR_KEY, Paths.get(appDataDir, "keys").toString());
setProperty(MainView.TITLE_KEY, appName); setProperty(MainView.TITLE_KEY, appName);

View file

@ -22,6 +22,8 @@ import io.bitsquare.arbitration.ArbitratorModule;
import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule; import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule;
import io.bitsquare.btc.BitcoinModule; import io.bitsquare.btc.BitcoinModule;
import io.bitsquare.crypto.CryptoModule; import io.bitsquare.crypto.CryptoModule;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.crypto.KeyStorage;
import io.bitsquare.gui.GuiModule; import io.bitsquare.gui.GuiModule;
import io.bitsquare.offer.OfferModule; import io.bitsquare.offer.OfferModule;
import io.bitsquare.offer.tomp2p.TomP2POfferModule; import io.bitsquare.offer.tomp2p.TomP2POfferModule;
@ -59,6 +61,8 @@ class BitsquareAppModule extends BitsquareModule {
@Override @Override
protected void configure() { protected void configure() {
bind(KeyStorage.class).in(Singleton.class);
bind(KeyRing.class).in(Singleton.class);
bind(User.class).in(Singleton.class); bind(User.class).in(Singleton.class);
bind(Preferences.class).in(Singleton.class); bind(Preferences.class).in(Singleton.class);
bind(AccountSettings.class).in(Singleton.class); bind(AccountSettings.class).in(Singleton.class);
@ -66,6 +70,9 @@ class BitsquareAppModule extends BitsquareModule {
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY)); File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir); 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(Environment.class).toInstance(env);
bind(UpdateProcess.class).in(Singleton.class); bind(UpdateProcess.class).in(Singleton.class);

View file

@ -21,6 +21,7 @@ import io.bitsquare.app.UpdateProcess;
import io.bitsquare.arbitration.ArbitrationRepository; import io.bitsquare.arbitration.ArbitrationRepository;
import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.KeyRing;
import io.bitsquare.fiat.FiatAccount; import io.bitsquare.fiat.FiatAccount;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
@ -85,6 +86,7 @@ class MainViewModel implements ViewModel {
final String bitcoinNetworkAsString; final String bitcoinNetworkAsString;
private final User user; private final User user;
private KeyRing keyRing;
private final WalletService walletService; private final WalletService walletService;
private final ArbitrationRepository arbitrationRepository; private final ArbitrationRepository arbitrationRepository;
private final ClientNode clientNode; private final ClientNode clientNode;
@ -93,10 +95,11 @@ class MainViewModel implements ViewModel {
private final BSFormatter formatter; private final BSFormatter formatter;
@Inject @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, TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
BSFormatter formatter) { BSFormatter formatter) {
this.user = user; this.user = user;
this.keyRing = keyRing;
this.walletService = walletService; this.walletService = walletService;
this.arbitrationRepository = arbitrationRepository; this.arbitrationRepository = arbitrationRepository;
this.clientNode = clientNode; this.clientNode = clientNode;
@ -137,7 +140,7 @@ class MainViewModel implements ViewModel {
// Set executor for all P2PServices // Set executor for all P2PServices
BaseP2PService.setUserThread(Platform::runLater); BaseP2PService.setUserThread(Platform::runLater);
Observable<BootstrapState> bootstrapStateAsObservable = clientNode.bootstrap(user.getP2pSigKeyPair()); Observable<BootstrapState> bootstrapStateAsObservable = clientNode.bootstrap(keyRing.getDhtSignatureKeyPair());
bootstrapStateAsObservable.publish(); bootstrapStateAsObservable.publish();
bootstrapStateAsObservable.subscribe( bootstrapStateAsObservable.subscribe(
state -> Platform.runLater(() -> setBootstrapState(state)), state -> Platform.runLater(() -> setBootstrapState(state)),

View file

@ -22,7 +22,7 @@ import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.InitializableView; import io.bitsquare.gui.common.view.InitializableView;
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol; import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol;
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage; 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.PlaceOfferProtocol;
import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook; import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook;
import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx; import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx;
@ -82,7 +82,7 @@ public class DebugView extends InitializableView {
/*---- Protocol ----*/ /*---- Protocol ----*/
CheckOfferAvailabilityProtocol.class, CheckOfferAvailabilityProtocol.class,
io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class, io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class,
RequestIsOfferAvailable.class, SendRequestIsOfferAvailableMessage.class,
ProcessReportOfferAvailabilityMessage.class, ProcessReportOfferAvailabilityMessage.class,
Boolean.class, /* used as seperator*/ Boolean.class, /* used as seperator*/

View file

@ -210,7 +210,7 @@ class OfferBookDataModel implements Activatable, DataModel {
} }
boolean isMyOffer(Offer offer) { boolean isMyOffer(Offer offer) {
return offer.getP2pSigPubKey() != null && offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()); return tradeManager.isMyOffer(offer);
} }
Coin getAmountAsCoin() { Coin getAmountAsCoin() {

View file

@ -64,8 +64,7 @@ class ClosedTradesDataModel implements Activatable, DataModel {
} }
public Offer.Direction getDirection(Offer offer) { public Offer.Direction getDirection(Offer offer) {
return offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()) ? return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
offer.getDirection() : offer.getMirroredDirection();
} }
private void applyList() { private void applyList() {

View file

@ -75,8 +75,7 @@ class OpenOffersDataModel implements Activatable, DataModel {
} }
public Offer.Direction getDirection(Offer offer) { public Offer.Direction getDirection(Offer offer) {
return offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()) ? return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
offer.getDirection() : offer.getMirroredDirection();
} }
private void applyList() { private void applyList() {

View file

@ -145,7 +145,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
trade = item.getTrade(); trade = item.getTrade();
tradeProperty.set(trade); tradeProperty.set(trade);
isOffererRole = trade.getOffer().getP2pSigPubKey().equals(user.getP2pSigPubKey()); isOffererRole = tradeManager.isMyOffer(trade.getOffer());
if (trade instanceof SellerTrade) if (trade instanceof SellerTrade)
sellerProcessState.bind(trade.processStateProperty()); sellerProcessState.bind(trade.processStateProperty());

View file

@ -402,6 +402,9 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
} }
} }
} }
else {
log.error("Unhandled state " + dataModel.getSellerProcessState().get());
}
} }
private void updateBuyerState() { private void updateBuyerState() {
@ -498,5 +501,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
} }
} }
} }
else {
log.error("Unhandled state " + dataModel.getBuyerProcessState().get());
}
} }
} }

View file

@ -39,7 +39,7 @@ public class TradeWizardItem extends Button {
setMouseTransparent(true); setMouseTransparent(true);
setText(title); setText(title);
setPrefHeight(40); setPrefHeight(40);
setPrefWidth(340); setPrefWidth(360);
setPadding(new Insets(0, 20, 0, 10)); setPadding(new Insets(0, 20, 0, 10));
setAlignment(Pos.CENTER_LEFT); setAlignment(Pos.CENTER_LEFT);
setDisabled(); setDisabled();