rebase of downloadUpdates branch by Simon Tuberlin on the Development HEAD

- rebase conflicts due to package renames
- some bouncy castle pgp stuff changed with new releases
This commit is contained in:
Mike Rosseel 2017-06-30 21:15:30 +02:00
parent edf50e601b
commit 4da9e1848e
10 changed files with 59 additions and 2016 deletions

View file

@ -1,4 +1,4 @@
package io.bitsquare.common.util;
package io.bisq.common.util;
public enum DownloadType {
INST,

View file

@ -1,4 +1,4 @@
package io.bitsquare.common.util;
package io.bisq.common.util;
/**
* A utility that downloads a file from a URL.
@ -8,8 +8,10 @@ package io.bitsquare.common.util;
import javafx.concurrent.Task;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import java.io.*;
import java.net.HttpURLConnection;
@ -153,7 +155,7 @@ public class DownloadUtil extends Task<File> {
// Read keys from file
is = PGPUtil.getDecoderStream(new FileInputStream(pubKeyFile));
PGPPublicKeyRingCollection publicKeyRingCollection = new PGPPublicKeyRingCollection(is);
PGPPublicKeyRingCollection publicKeyRingCollection = new PGPPublicKeyRingCollection(is, new JcaKeyFingerprintCalculator());
is.close();
Iterator<PGPPublicKeyRing> rIt = publicKeyRingCollection.getKeyRings();
@ -171,7 +173,7 @@ public class DownloadUtil extends Task<File> {
// Read signature from file
is = PGPUtil.getDecoderStream(new FileInputStream(sigFile));
PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(is);
PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(is, new JcaKeyFingerprintCalculator());
Object o = pgpObjectFactory.nextObject();
if (o instanceof PGPSignatureList) {
PGPSignatureList signatureList = (PGPSignatureList) o;
@ -185,10 +187,10 @@ public class DownloadUtil extends Task<File> {
System.out.format("KeyID used in signature: %X\n", pgpSignature.getKeyID());
publicKey = pgpPublicKeyRing.getPublicKey(pgpSignature.getKeyID());
System.out.format("The ID of the selected key is %X\n", publicKey.getKeyID());
pgpSignature.initVerify(publicKey, "BC");
pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), publicKey);
// Read file to verify
try {
//try {
byte[] data = new byte[1024];
is = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
while (true) {
@ -203,12 +205,14 @@ public class DownloadUtil extends Task<File> {
result = pgpSignature.verify();
System.out.println("The signature turned out to be " + (result ? "good." : "bad."));
return result;
/*
} catch (SignatureException sigExc) {
try {
is.close();
} catch (IOException ioExc) {}
throw sigExc;
}
*/
}
public String getFileName() {return this.fileName;}

View file

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.*;
import io.bisq.common.crypto.LimitedKeyStrengthException;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.input.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
@ -202,6 +203,32 @@ public class Utilities {
}
}
/**
* Creates and starts a Task for background downloading
* @param fileURL URL of file to be downloaded
* @param saveDir Directory to save file to
* @param indicator Progress indicator, can be {@code null}
* @param downloadType enum to identify downloaded files after completion, options are {INST, KEY, SIG, MISC}
* @param index For coordination between key and sig files
* @return The task handling the download
* @throws IOException
*/
public static DownloadUtil downloadFile(String fileURL, String saveDir, @Nullable ProgressIndicator indicator, DownloadType downloadType, byte index) throws IOException {
DownloadUtil task;
if (saveDir != null)
task = new DownloadUtil(fileURL, saveDir, downloadType, index);
else
task = new DownloadUtil(fileURL, downloadType, index); // Tries to use system temp directory
if (indicator != null) {
indicator.progressProperty().unbind();
indicator.progressProperty().bind(task.progressProperty());
}
Thread th = new Thread(task);
th.start();
// TODO: check for problems when creating task
return task;
}
public static void printSystemLoad() {
Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory() / 1024 / 1024;

View file

@ -1,494 +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.common.util;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.*;
import io.bitsquare.io.LookAheadObjectInputStream;
import javafx.concurrent.Task;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.crypto.Cipher;
import java.awt.*;
import java.io.*;
import java.net.URI;
import java.net.URLConnection;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import java.util.Random;
import java.util.concurrent.*;
/**
* General utilities
*/
public class Utilities {
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
private static long lastTimeStamp = System.currentTimeMillis();
public static final String LB = System.getProperty("line.separator");
public static final String LB2 = LB + LB;
public static String objectToJson(Object object) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new AnnotationExclusionStrategy())
/*.excludeFieldsWithModifiers(Modifier.TRANSIENT)*/
/* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)*/
.setPrettyPrinting()
.create();
return gson.toJson(object);
}
public static ListeningExecutorService getListeningExecutorService(String name,
int corePoolSize,
int maximumPoolSize,
long keepAliveTimeInSec) {
return MoreExecutors.listeningDecorator(getThreadPoolExecutor(name, corePoolSize, maximumPoolSize, keepAliveTimeInSec));
}
public static ThreadPoolExecutor getThreadPoolExecutor(String name,
int corePoolSize,
int maximumPoolSize,
long keepAliveTimeInSec) {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(name)
.setDaemon(true)
.build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
executor.allowCoreThreadTimeOut(true);
executor.setRejectedExecutionHandler((r, e) -> {
log.debug("RejectedExecutionHandler called");
});
return executor;
}
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(String name,
int corePoolSize,
int maximumPoolSize,
long keepAliveTimeInSec) {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(name)
.setDaemon(true)
.setPriority(Thread.MIN_PRIORITY)
.build();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
executor.setKeepAliveTime(keepAliveTimeInSec, TimeUnit.SECONDS);
executor.allowCoreThreadTimeOut(true);
executor.setMaximumPoolSize(maximumPoolSize);
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
executor.setRejectedExecutionHandler((r, e) -> {
log.debug("RejectedExecutionHandler called");
});
return executor;
}
public static boolean isUnix() {
return isOSX() || isLinux() || getOSName().contains("freebsd");
}
public static boolean isWindows() {
return getOSName().contains("win");
}
public static boolean isOSX() {
return getOSName().contains("mac") || getOSName().contains("darwin");
}
public static boolean isLinux() {
return getOSName().contains("linux");
}
private static String getOSName() {
return System.getProperty("os.name").toLowerCase(Locale.US);
}
public static String getOSArchitecture() {
String osArch = System.getProperty("os.arch");
if (isWindows()) {
// See: Like always windows needs extra treatment
// https://stackoverflow.com/questions/20856694/how-to-find-the-os-bit-type
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
return arch.endsWith("64")
|| wow64Arch != null && wow64Arch.endsWith("64")
? "64" : "32";
} else if (osArch.contains("arm")) {
// armv8 is 64 bit, armv7l is 32 bit
return osArch.contains("64") || osArch.contains("v8") ? "64" : "32";
} else if (isLinux()) {
return osArch.startsWith("i") ? "32" : "64";
} else {
return osArch.contains("64") ? "64" : osArch;
}
}
public static void printSysInfo() {
log.info("os.name: " + System.getProperty("os.name"));
log.info("os.version: " + System.getProperty("os.version"));
log.info("os.arch: " + System.getProperty("os.arch"));
log.info("sun.arch.data.model: " + getJVMArchitecture());
log.info("JRE: " + System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")");
log.info("JVM: " + System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")");
}
public static String getJVMArchitecture() {
return System.getProperty("sun.arch.data.model");
}
public static boolean isCorrectOSArchitecture() {
boolean result = getOSArchitecture().endsWith(getJVMArchitecture());
if (!result) {
log.warn("System.getProperty(\"os.arch\") " + System.getProperty("os.arch"));
log.warn("System.getenv(\"ProgramFiles(x86)\") " + System.getenv("ProgramFiles(x86)"));
log.warn("System.getenv(\"PROCESSOR_ARCHITECTURE\")" + System.getenv("PROCESSOR_ARCHITECTURE"));
log.warn("System.getenv(\"PROCESSOR_ARCHITEW6432\") " + System.getenv("PROCESSOR_ARCHITEW6432"));
log.warn("System.getProperty(\"sun.arch.data.model\") " + System.getProperty("sun.arch.data.model"));
}
return result;
}
public static void openURI(URI uri) throws IOException {
if (!isLinux()
&& Desktop.isDesktopSupported()
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
Desktop.getDesktop().browse(uri);
} else {
// Maybe Application.HostServices works in those cases?
// HostServices hostServices = getHostServices();
// hostServices.showDocument(uri.toString());
// On Linux Desktop is poorly implemented.
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
if (!DesktopUtil.browse(uri))
throw new IOException("Failed to open URI: " + uri.toString());
}
}
public static void openDirectory(File directory) throws IOException {
if (!isLinux()
&& Desktop.isDesktopSupported()
&& Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
Desktop.getDesktop().open(directory);
} else {
// Maybe Application.HostServices works in those cases?
// HostServices hostServices = getHostServices();
// hostServices.showDocument(uri.toString());
// On Linux Desktop is poorly implemented.
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
if (!DesktopUtil.open(directory))
throw new IOException("Failed to open directory: " + directory.toString());
}
}
/**
* Creates and starts a Task for background downloading
* @param fileURL URL of file to be downloaded
* @param saveDir Directory to save file to
* @param indicator Progress indicator, can be {@code null}
* @param downloadType enum to identify downloaded files after completion, options are {INST, KEY, SIG, MISC}
* @param index For coordination between key and sig files
* @return The task handling the download
* @throws IOException
*/
public static DownloadUtil downloadFile(String fileURL, String saveDir, @Nullable ProgressIndicator indicator, DownloadType downloadType, byte index) throws IOException {
DownloadUtil task;
if (saveDir != null)
task = new DownloadUtil(fileURL, saveDir, downloadType, index);
else
task = new DownloadUtil(fileURL, downloadType, index); // Tries to use system temp directory
if (indicator != null) {
indicator.progressProperty().unbind();
indicator.progressProperty().bind(task.progressProperty());
}
Thread th = new Thread(task);
th.start();
// TODO: check for problems when creating task
return task;
}
public static void printSystemLoad() {
Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory() / 1024 / 1024;
long total = runtime.totalMemory() / 1024 / 1024;
long used = total - free;
log.info("System load (no. threads/used memory (MB)): " + Thread.activeCount() + "/" + used);
}
public static void copyToClipboard(String content) {
try {
if (content != null && content.length() > 0) {
Clipboard clipboard = Clipboard.getSystemClipboard();
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(content);
clipboard.setContent(clipboardContent);
}
} catch (Throwable e) {
log.error("copyToClipboard failed " + e.getMessage());
e.printStackTrace();
}
}
public static byte[] concatByteArrays(byte[]... arrays) {
int totalLength = 0;
for (byte[] array : arrays) {
totalLength += array.length;
}
byte[] result = new byte[totalLength];
int currentIndex = 0;
for (byte[] array : arrays) {
System.arraycopy(array, 0, result, currentIndex, array.length);
currentIndex += array.length;
}
return result;
}
public static <T> T jsonToObject(String jsonString, Class<T> classOfT) {
Gson gson =
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
return gson.fromJson(jsonString, classOfT);
}
/* public static Object deserializeHexStringToObject(String serializedHexString) {
Object result = null;
try {
ByteArrayInputStream byteInputStream =
new ByteArrayInputStream(org.bitcoinj.core.Utils.parseAsHexOrBase58(serializedHexString));
try (ObjectInputStream objectInputStream = new LookAheadObjectInputStream(byteInputStream)) {
result = objectInputStream.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
byteInputStream.close();
}
} catch (IOException i) {
i.printStackTrace();
}
return result;
}
public static String serializeObjectToHexString(Serializable serializable) {
String result = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(serializable);
result = org.bitcoinj.core.Utils.HEX.encode(byteArrayOutputStream.toByteArray());
byteArrayOutputStream.close();
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}*/
public static <T extends Serializable> T deserialize(byte[] data) {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInput in = null;
Object result = null;
try {
in = new LookAheadObjectInputStream(bis, true);
result = in.readObject();
if (!(result instanceof Serializable))
throw new RuntimeException("Object not of type Serializable");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bis.close();
} catch (IOException ex) {
// ignore close exception
}
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
// ignore close exception
}
}
return (T) result;
}
public static byte[] serialize(Serializable object) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = null;
byte[] result = null;
try {
out = new ObjectOutputStream(bos);
out.writeObject(object);
out.flush();
result = bos.toByteArray().clone();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException ignore) {
}
try {
bos.close();
} catch (IOException ignore) {
}
}
return result;
}
private static void printElapsedTime(String msg) {
if (!msg.isEmpty()) {
msg += " / ";
}
long timeStamp = System.currentTimeMillis();
log.debug(msg + "Elapsed: " + String.valueOf(timeStamp - lastTimeStamp));
lastTimeStamp = timeStamp;
}
public static void printElapsedTime() {
printElapsedTime("");
}
public static Object copy(Serializable orig) throws IOException, ClassNotFoundException {
try {
// Write the object out to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(orig);
out.flush();
out.close();
// Make an input stream from the byte array and read
// a copy of the object back in.
ObjectInputStream in = new LookAheadObjectInputStream(new ByteArrayInputStream(bos.toByteArray()), true);
Object obj = in.readObject();
return obj;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
throw e;
}
}
public static String readTextFileFromServer(String url, String userAgent) throws IOException {
URLConnection connection = URI.create(url).toURL().openConnection();
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(10));
connection.addRequestProperty("User-Agent", userAgent);
connection.connect();
try (InputStream inputStream = connection.getInputStream()) {
return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
public static void setThreadName(String name) {
Thread.currentThread().setName(name + "-" + new Random().nextInt(10000));
}
private static class AnnotationExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(JsonExclude.class) != null;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
public static void checkCryptoPolicySetup() throws NoSuchAlgorithmException, LimitedKeyStrengthException {
if (Cipher.getMaxAllowedKeyLength("AES") > 128)
log.debug("Congratulations, you have unlimited key length support!");
else
throw new LimitedKeyStrengthException();
}
public static String toTruncatedString(Object message, int maxLenght) {
return StringUtils.abbreviate(message.toString(), maxLenght).replace("\n", "");
}
public static String toTruncatedString(Object message) {
return toTruncatedString(message, 200);
}
public static String getRandomPrefix(int minLength, int maxLength) {
int length = minLength + new Random().nextInt(maxLength - minLength + 1);
String result;
switch (new Random().nextInt(3)) {
case 0:
result = RandomStringUtils.randomAlphabetic(length);
break;
case 1:
result = RandomStringUtils.randomNumeric(length);
break;
case 2:
default:
result = RandomStringUtils.randomAlphanumeric(length);
}
switch (new Random().nextInt(3)) {
case 0:
result = result.toUpperCase();
break;
case 1:
result = result.toLowerCase();
break;
case 2:
default:
}
return result;
}
public static String getShortId(String id) {
return getShortId(id, "-");
}
public static String getShortId(String id, String sep) {
String[] chunks = id.split(sep);
if (chunks.length > 0)
return chunks[0];
else
return id.substring(0, Math.min(8, id.length()));
}
}

View file

@ -1,152 +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.alert;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.bitsquare.app.AppOptionKeys;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
import io.bitsquare.user.User;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.SignatureException;
import static org.bitcoinj.core.Utils.HEX;
public class AlertManager {
private static final Logger log = LoggerFactory.getLogger(AlertManager.class);
private P2PService p2PService;
private final KeyRing keyRing;
private final User user;
private final ObjectProperty<Alert> alertMessageProperty = new SimpleObjectProperty<>();
// Pub key for developer global alert message
private static final String pubKeyAsHex = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee";
private ECKey alertSigningKey;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, Initialization
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public AlertManager(P2PService p2PService, KeyRing keyRing, User user, @Named(AppOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
this.p2PService = p2PService;
this.keyRing = keyRing;
this.user = user;
if (!ignoreDevMsg) {
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
public void onAdded(ProtectedStorageEntry data) {
if (data.getStoragePayload() instanceof Alert) {
Alert alert = (Alert) data.getStoragePayload();
if (verifySignature(alert))
alertMessageProperty.set(alert);
}
}
@Override
public void onRemoved(ProtectedStorageEntry data) {
if (data.getStoragePayload() instanceof Alert) {
Alert alert = (Alert) data.getStoragePayload();
if (verifySignature(alert))
alertMessageProperty.set(null);
}
}
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public ReadOnlyObjectProperty<Alert> alertMessageProperty() {
return alertMessageProperty;
}
public boolean addAlertMessageIfKeyIsValid(Alert alert, String privKeyString) {
// if there is a previous message we remove that first
if (user.getDevelopersAlert() != null)
removeAlertMessageIfKeyIsValid(privKeyString);
boolean isKeyValid = isKeyValid(privKeyString);
if (isKeyValid) {
signAndAddSignatureToAlertMessage(alert);
user.setDevelopersAlert(alert);
boolean result = p2PService.addData(alert, true);
if (result) {
log.trace("Add alertMessage to network was successful. AlertMessage = " + alert);
}
}
return isKeyValid;
}
public boolean removeAlertMessageIfKeyIsValid(String privKeyString) {
Alert alert = user.getDevelopersAlert();
if (isKeyValid(privKeyString) && alert != null) {
if (p2PService.removeData(alert, true))
log.trace("Remove alertMessage from network was successful. AlertMessage = " + alert);
user.setDevelopersAlert(null);
return true;
} else {
return false;
}
}
private boolean isKeyValid(String privKeyString) {
try {
alertSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString)));
return pubKeyAsHex.equals(Utils.HEX.encode(alertSigningKey.getPubKey()));
} catch (Throwable t) {
return false;
}
}
private void signAndAddSignatureToAlertMessage(Alert alert) {
String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes());
String signatureAsBase64 = alertSigningKey.signMessage(alertMessageAsHex);
alert.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
}
private boolean verifySignature(Alert alert) {
String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes());
try {
ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(alertMessageAsHex, alert.getSignatureAsBase64());
return true;
} catch (SignatureException e) {
log.warn("verifySignature failed");
return false;
}
}
}

View file

@ -1,299 +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.arbitration;
import com.google.inject.Inject;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.p2p.BootstrapListener;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.bitcoinj.core.Utils.HEX;
public class ArbitratorManager {
private static final Logger log = LoggerFactory.getLogger(ArbitratorManager.class);
///////////////////////////////////////////////////////////////////////////////////////////
// Static
///////////////////////////////////////////////////////////////////////////////////////////
private static final long REPUBLISH_MILLIS = Arbitrator.TTL / 2;
private static final long RETRY_REPUBLISH_SEC = 5;
private static final long REPEATED_REPUBLISH_AT_STARTUP_SEC = 60;
// Keys for invited arbitrators in bootstrapping phase (before registration is open to anyone and security payment is implemented)
// For developers we add here 2 test keys so one can setup an arbitrator by adding that test pubKey
// to the publicKeys list and use the test PrivKey for arbitrator registration.
// PrivKey for dev testing: 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a
// Matching pubKey for dev testing: 027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee
private static final List<String> publicKeys = new ArrayList<>(Arrays.asList(
"03697a499d24f497b3c46bf716318231e46c4e6a685a4e122d8e2a2b229fa1f4b8",
"0365c6af94681dbee69de1851f98d4684063bf5c2d64b1c73ed5d90434f375a054",
"031c502a60f9dbdb5ae5e438a79819e4e1f417211dd537ac12c9bc23246534c4bd",
"02c1e5a242387b6d5319ce27246cea6edaaf51c3550591b528d2578a4753c56c2c",
"025c319faf7067d9299590dd6c97fe7e56cd4dac61205ccee1cd1fc390142390a2",
"038f6e24c2bfe5d51d0a290f20a9a657c270b94ef2b9c12cd15ca3725fa798fc55",
"0255256ff7fb615278c4544a9bbd3f5298b903b8a011cd7889be19b6b1c45cbefe",
"024a3a37289f08c910fbd925ebc72b946f33feaeff451a4738ee82037b4cda2e95",
"02a88b75e9f0f8afba1467ab26799dcc38fd7a6468fb2795444b425eb43e2c10bd",
"02349a51512c1c04c67118386f4d27d768c5195a83247c150a4b722d161722ba81",
"03f718a2e0dc672c7cdec0113e72c3322efc70412bb95870750d25c32cd98de17d",
"028ff47ee2c56e66313928975c58fa4f1b19a0f81f3a96c4e9c9c3c6768075509e",
"02b517c0cbc3a49548f448ddf004ed695c5a1c52ec110be1bfd65fa0ca0761c94b",
"03df837a3a0f3d858e82f3356b71d1285327f101f7c10b404abed2abc1c94e7169",
"0203a90fb2ab698e524a5286f317a183a84327b8f8c3f7fa4a98fec9e1cefd6b72",
"023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5",
"027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"
));
///////////////////////////////////////////////////////////////////////////////////////////
// Instance fields
///////////////////////////////////////////////////////////////////////////////////////////
private final KeyRing keyRing;
private final ArbitratorService arbitratorService;
private final User user;
private Preferences preferences;
private final ObservableMap<NodeAddress, Arbitrator> arbitratorsObservableMap = FXCollections.observableHashMap();
private final List<Arbitrator> persistedAcceptedArbitrators;
private Timer republishArbitratorTimer, retryRepublishArbitratorTimer;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public ArbitratorManager(KeyRing keyRing, ArbitratorService arbitratorService, User user, Preferences preferences) {
this.keyRing = keyRing;
this.arbitratorService = arbitratorService;
this.user = user;
this.preferences = preferences;
persistedAcceptedArbitrators = new ArrayList<>(user.getAcceptedArbitrators());
user.clearAcceptedArbitrators();
arbitratorService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
public void onAdded(ProtectedStorageEntry data) {
if (data.getStoragePayload() instanceof Arbitrator)
updateArbitratorMap();
}
@Override
public void onRemoved(ProtectedStorageEntry data) {
if (data.getStoragePayload() instanceof Arbitrator)
updateArbitratorMap();
}
});
}
public void shutDown() {
stopRepublishArbitratorTimer();
stopRetryRepublishArbitratorTimer();
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void onAllServicesInitialized() {
if (user.getRegisteredArbitrator() != null) {
P2PService p2PService = arbitratorService.getP2PService();
if (p2PService.isBootstrapped())
isBootstrapped();
else
p2PService.addP2PServiceListener(new BootstrapListener() {
@Override
public void onBootstrapComplete() {
isBootstrapped();
}
});
}
updateArbitratorMap();
}
private void isBootstrapped() {
if (republishArbitratorTimer == null) {
republishArbitratorTimer = UserThread.runPeriodically(this::republishArbitrator, REPUBLISH_MILLIS, TimeUnit.MILLISECONDS);
UserThread.runAfter(this::republishArbitrator, REPEATED_REPUBLISH_AT_STARTUP_SEC);
republishArbitrator();
}
}
public void updateArbitratorMap() {
Map<NodeAddress, Arbitrator> map = arbitratorService.getArbitrators();
arbitratorsObservableMap.clear();
Map<NodeAddress, Arbitrator> filtered = map.values().stream()
.filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey()))
&& verifySignature(e.getPubKeyRing().getSignaturePubKey(), e.getRegistrationPubKey(), e.getRegistrationSignature()))
.collect(Collectors.toMap(Arbitrator::getArbitratorNodeAddress, Function.identity()));
arbitratorsObservableMap.putAll(filtered);
arbitratorsObservableMap.values().stream()
.filter(arbitrator -> persistedAcceptedArbitrators.contains(arbitrator))
.forEach(user::addAcceptedArbitrator);
if (preferences.getAutoSelectArbitrators()) {
arbitratorsObservableMap.values().stream()
.filter(user::hasMatchingLanguage)
.forEach(user::addAcceptedArbitrator);
} else {
// if we don't have any arbitrator we set all matching
// we use a delay as we might get our matching arbitrator a bit delayed (first we get one we did not selected
// then we get our selected one - we don't want to activate the first in that case)
UserThread.runAfter(() -> {
if (user.getAcceptedArbitrators().isEmpty()) {
arbitratorsObservableMap.values().stream()
.filter(user::hasMatchingLanguage)
.forEach(user::addAcceptedArbitrator);
}
}, 100, TimeUnit.MILLISECONDS);
}
}
public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
user.setRegisteredArbitrator(arbitrator);
arbitratorsObservableMap.put(arbitrator.getArbitratorNodeAddress(), arbitrator);
arbitratorService.addArbitrator(arbitrator,
() -> {
log.debug("Arbitrator successfully saved in P2P network");
resultHandler.handleResult();
if (arbitratorsObservableMap.size() > 0)
UserThread.runAfter(this::updateArbitratorMap, 100, TimeUnit.MILLISECONDS);
},
errorMessageHandler::handleErrorMessage);
}
public void removeArbitrator(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
if (registeredArbitrator != null) {
user.setRegisteredArbitrator(null);
arbitratorsObservableMap.remove(registeredArbitrator.getArbitratorNodeAddress());
arbitratorService.removeArbitrator(registeredArbitrator,
() -> {
log.debug("Arbitrator successfully removed from P2P network");
resultHandler.handleResult();
},
errorMessageHandler::handleErrorMessage);
}
}
public ObservableMap<NodeAddress, Arbitrator> getArbitratorsObservableMap() {
return arbitratorsObservableMap;
}
// A private key is handed over to selected arbitrators for registration.
// An invited arbitrator will sign at registration his storageSignaturePubKey with that private key and attach the signature and pubKey to his data.
// Other users will check the signature with the list of public keys hardcoded in the app.
public String signStorageSignaturePubKey(ECKey key) {
String keyToSignAsHex = Utils.HEX.encode(keyRing.getPubKeyRing().getSignaturePubKey().getEncoded());
return key.signMessage(keyToSignAsHex);
}
@Nullable
public ECKey getRegistrationKey(String privKeyBigIntString) {
try {
return ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyBigIntString)));
} catch (Throwable t) {
return null;
}
}
public boolean isPublicKeyInList(String pubKeyAsHex) {
return publicKeys.contains(pubKeyAsHex);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void republishArbitrator() {
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
if (registeredArbitrator != null) {
addArbitrator(registeredArbitrator,
this::updateArbitratorMap,
errorMessage -> {
if (retryRepublishArbitratorTimer == null)
retryRepublishArbitratorTimer = UserThread.runPeriodically(() -> {
stopRetryRepublishArbitratorTimer();
republishArbitrator();
}, RETRY_REPUBLISH_SEC);
}
);
}
}
private boolean verifySignature(PublicKey storageSignaturePubKey, byte[] registrationPubKey, String signature) {
String keyToSignAsHex = Utils.HEX.encode(storageSignaturePubKey.getEncoded());
try {
ECKey key = ECKey.fromPublicOnly(registrationPubKey);
key.verifyMessage(keyToSignAsHex, signature);
return true;
} catch (SignatureException e) {
log.warn("verifySignature failed");
return false;
}
}
private void stopRetryRepublishArbitratorTimer() {
if (retryRepublishArbitratorTimer != null) {
retryRepublishArbitratorTimer.stop();
retryRepublishArbitratorTimer = null;
}
}
private void stopRepublishArbitratorTimer() {
if (republishArbitratorTimer != null) {
republishArbitratorTimer.stop();
republishArbitratorTimer = null;
}
}
}

View file

@ -83,6 +83,7 @@ import io.bisq.network.p2p.network.CloseConnectionReason;
import io.bisq.network.p2p.network.Connection;
import io.bisq.network.p2p.network.ConnectionListener;
import io.bisq.network.p2p.peers.keepalive.messages.Ping;
import io.bisq.gui.main.overlays.windows.DisplayUpdateDownloadWindow;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
@ -1019,10 +1020,11 @@ public class MainViewModel implements ViewModel {
private void displayAlertIfPresent(Alert alert) {
boolean alreadyDisplayed = alert != null && alert.equals(user.getDisplayedAlert());
user.setDisplayedAlert(alert);
if (alert != null &&
!alreadyDisplayed &&
(!alert.isUpdateInfo() || alert.isNewVersion()))
new DisplayAlertMessageWindow().alertMessage(alert).show();
if (alert != null && !alreadyDisplayed)
if (alert.isUpdateInfo() || alert.isNewVersion())
new DisplayUpdateDownloadWindow().alertMessage(alert).show();
else
new DisplayAlertMessageWindow().alertMessage(alert).show();
}
private void displayPrivateNotification(PrivateNotificationPayload privateNotification) {

View file

@ -15,13 +15,13 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.main.overlays.windows;
package io.bisq.gui.main.overlays.windows;
import io.bitsquare.alert.Alert;
import io.bitsquare.common.util.DownloadType;
import io.bitsquare.common.util.DownloadUtil;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.main.overlays.Overlay;
import io.bisq.core.alert.Alert;
import io.bisq.gui.main.overlays.Overlay;
import io.bisq.common.util.DownloadType;
import io.bisq.common.util.DownloadUtil;
import io.bisq.common.util.Utilities;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
@ -38,9 +38,8 @@ import java.io.File;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.bitsquare.gui.util.FormBuilder.addButton;
import static io.bitsquare.gui.util.FormBuilder.addLabelHyperlinkWithIcon;
import static io.bitsquare.gui.util.FormBuilder.addMultilineLabel;
import static io.bisq.gui.util.FormBuilder.addButton;
import static io.bisq.gui.util.FormBuilder.addMultilineLabel;
// TODO: For future use sigFile and key download calls have to loop through keyIDs and verifySignature needs to be called for all sigs/keys
@ -87,7 +86,7 @@ public class DisplayUpdateDownloadWindow extends Overlay<DisplayUpdateDownloadWi
private void addContent() {
checkNotNull(alert, "alertMessage must not be null");
messageLabel = addMultilineLabel(gridPane, ++rowIndex, alert.message, 10);
messageLabel = addMultilineLabel(gridPane, ++rowIndex, alert.getMessage(), 10);
headLine = "Important update information!";
headLineLabel.setStyle("-fx-text-fill: -fx-accent; -fx-font-weight: bold; -fx-font-size: 22;");
@ -102,14 +101,14 @@ public class DisplayUpdateDownloadWindow extends Overlay<DisplayUpdateDownloadWi
Button downloadButton = addButton(gridPane, ++rowIndex, "Download now");
// TODO How do we get the right URL for the download? (check for other platforms)
String url = "https://github.com/bitsquare/bitsquare/releases/download/" + "v" + alert.version + "/" ;
String url = "https://github.com/bitsquare/bitsquare/releases/download/" + "v" + alert.getVersion() + "/" ;
String fileName;
if (Utilities.isOSX())
fileName = "Bitsquare-" + alert.version + ".dmg";
fileName = "Bitsquare-" + alert.getVersion() + ".dmg";
else if (Utilities.isWindows())
fileName = "Bitsquare-" + Utilities.getOSArchitecture() + "bit-" + alert.version + ".exe";
fileName = "Bitsquare-" + Utilities.getOSArchitecture() + "bit-" + alert.getVersion() + ".exe";
else if (Utilities.isLinux())
fileName = "Bitsquare-" + Utilities.getOSArchitecture() + "bit-" + alert.version + ".deb";
fileName = "Bitsquare-" + Utilities.getOSArchitecture() + "bit-" + alert.getVersion() + ".deb";
else {
fileName = "";
downloadButton.setDisable(true);
@ -300,4 +299,4 @@ enum VerificationResult {
GOOD,
BAD,
EXCEPTION
}
}

File diff suppressed because it is too large Load diff

View file

@ -133,7 +133,7 @@
<artifactId>bcprov-jdk15on</artifactId>
<version>1.56</version>
</dependency>
-->
<!-- We need both bcpg-jdk15on and bcpg-jdk16 -->
<dependency>
<groupId>org.bouncycastle</groupId>