From 94cca5924975af03b4f4027f0020bbe1ec074606 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 23 Feb 2016 16:16:29 +0000 Subject: [PATCH 1/5] Use Bouncy Castle for encoding public keys. --- .../briarproject/crypto/CryptoComponentImpl.java | 8 ++++---- .../org/briarproject/crypto/Sec1KeyParser.java | 14 +++++++------- .../org/briarproject/crypto/Sec1PublicKey.java | 16 ++-------------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index 825c3720f..bdf74dfaa 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -164,8 +164,8 @@ class CryptoComponentImpl implements CryptoComponent { // Return a wrapper that uses the SEC 1 encoding ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) keyPair.getPublic(); - PublicKey publicKey = new Sec1PublicKey(ecPublicKey, - AGREEMENT_KEY_PAIR_BITS); + PublicKey publicKey = new Sec1PublicKey(ecPublicKey + ); ECPrivateKeyParameters ecPrivateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey, @@ -183,8 +183,8 @@ class CryptoComponentImpl implements CryptoComponent { // Return a wrapper that uses the SEC 1 encoding ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) keyPair.getPublic(); - PublicKey publicKey = new Sec1PublicKey(ecPublicKey, - SIGNATURE_KEY_PAIR_BITS); + PublicKey publicKey = new Sec1PublicKey(ecPublicKey + ); ECPrivateKeyParameters ecPrivateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey, diff --git a/briar-core/src/org/briarproject/crypto/Sec1KeyParser.java b/briar-core/src/org/briarproject/crypto/Sec1KeyParser.java index 99b3d2e72..a3bc7c71e 100644 --- a/briar-core/src/org/briarproject/crypto/Sec1KeyParser.java +++ b/briar-core/src/org/briarproject/crypto/Sec1KeyParser.java @@ -1,11 +1,5 @@ package org.briarproject.crypto; -import static java.util.logging.Level.INFO; - -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.util.logging.Logger; - import org.briarproject.api.crypto.KeyParser; import org.briarproject.api.crypto.PrivateKey; import org.briarproject.api.crypto.PublicKey; @@ -15,6 +9,12 @@ import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.math.ec.ECCurve; import org.spongycastle.math.ec.ECPoint; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.util.logging.Logger; + +import static java.util.logging.Level.INFO; + /** * A key parser that uses the encoding defined in "SEC 1: Elliptic Curve * Cryptography", section 2.3 (Certicom Corporation, May 2009). Point @@ -73,7 +73,7 @@ class Sec1KeyParser implements KeyParser { throw new GeneralSecurityException(); // Construct a public key from the point (x, y) and the params ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params); - PublicKey p = new Sec1PublicKey(k, keyBits); + PublicKey p = new Sec1PublicKey(k); long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info("Parsing public key took " + duration + " ms"); diff --git a/briar-core/src/org/briarproject/crypto/Sec1PublicKey.java b/briar-core/src/org/briarproject/crypto/Sec1PublicKey.java index 95523c79b..f85a34b8d 100644 --- a/briar-core/src/org/briarproject/crypto/Sec1PublicKey.java +++ b/briar-core/src/org/briarproject/crypto/Sec1PublicKey.java @@ -2,7 +2,6 @@ package org.briarproject.crypto; import org.briarproject.api.crypto.PublicKey; import org.spongycastle.crypto.params.ECPublicKeyParameters; -import org.spongycastle.math.ec.ECPoint; /** * An elliptic curve public key that uses the encoding defined in "SEC 1: @@ -12,24 +11,13 @@ import org.spongycastle.math.ec.ECPoint; class Sec1PublicKey implements PublicKey { private final ECPublicKeyParameters key; - private final int bytesPerInt, publicKeyBytes; - Sec1PublicKey(ECPublicKeyParameters key, int keyBits) { + Sec1PublicKey(ECPublicKeyParameters key) { this.key = key; - bytesPerInt = (keyBits + 7) / 8; - publicKeyBytes = 1 + 2 * bytesPerInt; } public byte[] getEncoded() { - byte[] encodedKey = new byte[publicKeyBytes]; - encodedKey[0] = 4; - ECPoint pub = key.getQ().normalize(); - byte[] x = pub.getAffineXCoord().toBigInteger().toByteArray(); - Sec1Utils.convertToFixedLength(x, encodedKey, 1, bytesPerInt); - byte[] y = pub.getAffineYCoord().toBigInteger().toByteArray(); - Sec1Utils.convertToFixedLength(y, encodedKey, 1 + bytesPerInt, - bytesPerInt); - return encodedKey; + return key.getQ().getEncoded(false); } ECPublicKeyParameters getKey() { From 28086cd359b08061b90411eb377ca6e3049cf2b4 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 23 Feb 2016 20:19:03 +0000 Subject: [PATCH 2/5] ECIES encryption for feedback and crash reports. --- .../org/briarproject/crypto/AsciiArmour.java | 27 +++ .../briarproject/crypto/MessageEncrypter.java | 207 ++++++++++++++++++ .../briarproject/crypto/AsciiArmourTest.java | 47 ++++ .../crypto/MessageEncrypterTest.java | 41 ++++ 4 files changed, 322 insertions(+) create mode 100644 briar-core/src/org/briarproject/crypto/AsciiArmour.java create mode 100644 briar-core/src/org/briarproject/crypto/MessageEncrypter.java create mode 100644 briar-tests/src/org/briarproject/crypto/AsciiArmourTest.java create mode 100644 briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java diff --git a/briar-core/src/org/briarproject/crypto/AsciiArmour.java b/briar-core/src/org/briarproject/crypto/AsciiArmour.java new file mode 100644 index 000000000..5e00461d4 --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/AsciiArmour.java @@ -0,0 +1,27 @@ +package org.briarproject.crypto; + +import org.briarproject.api.FormatException; +import org.briarproject.util.StringUtils; + +class AsciiArmour { + + static String wrap(byte[] b, int lineLength) { + String wrapped = StringUtils.toHexString(b); + StringBuilder s = new StringBuilder(); + int length = wrapped.length(); + for (int i = 0; i < length; i += lineLength) { + int end = Math.min(i + lineLength, length); + s.append(wrapped.substring(i, end)); + s.append("\r\n"); + } + return s.toString(); + } + + static byte[] unwrap(String s) throws FormatException { + try { + return StringUtils.fromHexString(s.replaceAll("[^0-9a-fA-F]", "")); + } catch (IllegalArgumentException e) { + throw new FormatException(); + } + } +} diff --git a/briar-core/src/org/briarproject/crypto/MessageEncrypter.java b/briar-core/src/org/briarproject/crypto/MessageEncrypter.java new file mode 100644 index 000000000..a92eed0ab --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/MessageEncrypter.java @@ -0,0 +1,207 @@ +package org.briarproject.crypto; + +import org.briarproject.util.StringUtils; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.BasicAgreement; +import org.spongycastle.crypto.BlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.DerivationFunction; +import org.spongycastle.crypto.KeyEncoder; +import org.spongycastle.crypto.KeyParser; +import org.spongycastle.crypto.Mac; +import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; +import org.spongycastle.crypto.digests.SHA256Digest; +import org.spongycastle.crypto.engines.AESLightEngine; +import org.spongycastle.crypto.engines.IESEngine; +import org.spongycastle.crypto.generators.ECKeyPairGenerator; +import org.spongycastle.crypto.generators.EphemeralKeyPairGenerator; +import org.spongycastle.crypto.generators.KDF2BytesGenerator; +import org.spongycastle.crypto.macs.HMac; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.AsymmetricKeyParameter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECKeyGenerationParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.params.IESWithCipherParameters; +import org.spongycastle.crypto.parsers.ECIESPublicKeyParser; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.security.SecureRandom; +import java.util.Scanner; + +public class MessageEncrypter { + + private static final ECDomainParameters PARAMETERS; + private static final int MAC_KEY_BITS = 256; + private static final int CIPHER_KEY_BITS = 256; + private static final int LINE_LENGTH = 70; + + static { + X9ECParameters x9 = TeleTrusTNamedCurves.getByName("brainpoolp512r1"); + PARAMETERS = new ECDomainParameters(x9.getCurve(), x9.getG(), + x9.getN(), x9.getH()); + } + + private final ECKeyPairGenerator generator; + private final EphemeralKeyPairGenerator ephemeralGenerator; + private final KeyParser parser; + + MessageEncrypter(SecureRandom random) { + generator = new ECKeyPairGenerator(); + generator.init(new ECKeyGenerationParameters(PARAMETERS, random)); + KeyEncoder encoder = new PublicKeyEncoder(); + ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder); + parser = new PublicKeyParser(PARAMETERS); + } + + AsymmetricCipherKeyPair generateKeyPair() { + return generator.generateKeyPair(); + } + + byte[] encrypt(ECPublicKeyParameters pubKey, byte[] plaintext) + throws CryptoException { + IESEngine engine = getEngine(); + engine.init(pubKey, getCipherParameters(), ephemeralGenerator); + return engine.processBlock(plaintext, 0, plaintext.length); + } + + byte[] decrypt(ECPrivateKeyParameters privKey, byte[] ciphertext) + throws CryptoException { + IESEngine engine = getEngine(); + engine.init(privKey, getCipherParameters(), parser); + return engine.processBlock(ciphertext, 0, ciphertext.length); + } + + private IESEngine getEngine() { + BasicAgreement agreement = new ECDHCBasicAgreement(); + DerivationFunction kdf = new KDF2BytesGenerator(new SHA256Digest()); + Mac mac = new HMac(new SHA256Digest()); + BlockCipher cipher = new CBCBlockCipher(new AESLightEngine()); + PaddedBufferedBlockCipher pad = new PaddedBufferedBlockCipher(cipher); + return new IESEngine(agreement, kdf, mac, pad); + } + + private CipherParameters getCipherParameters() { + return new IESWithCipherParameters(null, null, MAC_KEY_BITS, + CIPHER_KEY_BITS); + } + + private static class PublicKeyEncoder implements KeyEncoder { + + @Override + public byte[] getEncoded(AsymmetricKeyParameter key) { + if (!(key instanceof ECPublicKeyParameters)) + throw new IllegalArgumentException(); + return ((ECPublicKeyParameters) key).getQ().getEncoded(false); + } + } + + private static class PublicKeyParser extends ECIESPublicKeyParser { + + private PublicKeyParser(ECDomainParameters ecParams) { + super(ecParams); + } + + @Override + public AsymmetricKeyParameter readKey(InputStream in) + throws IOException { + try { + return super.readKey(in); + } catch (IllegalArgumentException e) { + throw new IOException(e); + } + } + } + + public static void main(String[] args) throws Exception { + if (args.length < 1) { + printUsage(); + return; + } + SecureRandom random = new SecureRandom(); + MessageEncrypter encrypter = new MessageEncrypter(random); + if (args[0].equals("generate")) { + if (args.length != 3) { + printUsage(); + return; + } + // Generate a key pair + AsymmetricCipherKeyPair keyPair = encrypter.generateKeyPair(); + ECPublicKeyParameters publicKey = + (ECPublicKeyParameters) keyPair.getPublic(); + byte[] publicKeyBytes = publicKey.getQ().getEncoded(false); + PrintStream out = new PrintStream(new FileOutputStream(args[1])); + out.print(StringUtils.toHexString(publicKeyBytes)); + out.flush(); + out.close(); + ECPrivateKeyParameters privateKey = + (ECPrivateKeyParameters) keyPair.getPrivate(); + out = new PrintStream(new FileOutputStream(args[2])); + out.print(privateKey.getD().toString(16).toUpperCase()); + out.flush(); + out.close(); + } else if (args[0].equals("encrypt")) { + if (args.length != 2) { + printUsage(); + return; + } + // Encrypt a decrypted message + InputStream in = new FileInputStream(args[1]); + byte[] b = StringUtils.fromHexString(readFully(in).trim()); + in = new ByteArrayInputStream(b); + ECPublicKeyParameters publicKey = + (ECPublicKeyParameters) encrypter.parser.readKey(in); + String message = readFully(System.in); + byte[] plaintext = message.getBytes(Charset.forName("UTF-8")); + byte[] ciphertext = encrypter.encrypt(publicKey, plaintext); + System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH)); + } else if (args[0].equals("decrypt")) { + if (args.length != 2) { + printUsage(); + return; + } + // Decrypt an encrypted message + InputStream in = new FileInputStream(args[1]); + byte[] b = StringUtils.fromHexString(readFully(in).trim()); + BigInteger d = new BigInteger(1, b); + ECPrivateKeyParameters privateKey = new ECPrivateKeyParameters(d, + PARAMETERS); + byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in)); + byte[] plaintext = encrypter.decrypt(privateKey, ciphertext); + System.out.println(new String(plaintext, Charset.forName("UTF-8"))); + } else { + printUsage(); + } + } + + private static void printUsage() { + System.err.println("Usage:"); + System.err.println("MessageEncrypter generate "); + System.err.println("MessageEncrypter encrypt "); + System.err.println("MessageEncrypter decrypt "); + } + + private static String readFully(InputStream in) throws IOException { + StringBuilder stringBuilder = new StringBuilder(); + Scanner scanner = new Scanner(in); + while (scanner.hasNextLine()) { + stringBuilder.append(scanner.nextLine()); + stringBuilder.append(System.lineSeparator()); + } + scanner.close(); + in.close(); + return stringBuilder.toString(); + } +} diff --git a/briar-tests/src/org/briarproject/crypto/AsciiArmourTest.java b/briar-tests/src/org/briarproject/crypto/AsciiArmourTest.java new file mode 100644 index 000000000..e07f3e6c3 --- /dev/null +++ b/briar-tests/src/org/briarproject/crypto/AsciiArmourTest.java @@ -0,0 +1,47 @@ +package org.briarproject.crypto; + +import org.briarproject.BriarTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class AsciiArmourTest extends BriarTestCase { + + @Test + public void testWrapOnSingleLine() { + byte[] b = new byte[8]; + for (int i = 0; i < b.length; i++) b[i] = (byte) i; + String expected = "0001020304050607\r\n"; + assertEquals(expected, AsciiArmour.wrap(b, 70)); + } + + @Test + public void testWrapOnMultipleLines() { + byte[] b = new byte[8]; + for (int i = 0; i < b.length; i++) b[i] = (byte) i; + String expected = "0001020\r\n3040506\r\n07\r\n"; + assertEquals(expected, AsciiArmour.wrap(b, 7)); + } + + @Test + public void testUnwrapOnSingleLine() throws Exception { + String s = "0001020304050607"; + byte[] expected = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + assertArrayEquals(expected, AsciiArmour.unwrap(s)); + } + + @Test + public void testUnwrapOnMultipleLines() throws Exception { + String s = "0001020\r\n3040506\r\n07"; + byte[] expected = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + assertArrayEquals(expected, AsciiArmour.unwrap(s)); + } + + @Test + public void testUnwrapWithJunkCharacters() throws Exception { + String s = "0001??020\rzz\n30z40..506\r\n07;;"; + byte[] expected = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + assertArrayEquals(expected, AsciiArmour.unwrap(s)); + } +} diff --git a/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java b/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java new file mode 100644 index 000000000..29c07984e --- /dev/null +++ b/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java @@ -0,0 +1,41 @@ +package org.briarproject.crypto; + +import org.briarproject.BriarTestCase; +import org.junit.Test; +import org.spongycastle.crypto.AsymmetricCipherKeyPair; +import org.spongycastle.crypto.CryptoException; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; + +import java.security.SecureRandom; + +import static org.junit.Assert.assertArrayEquals; + +public class MessageEncrypterTest extends BriarTestCase { + + private final SecureRandom random = new SecureRandom(); + + @Test + public void testEncryptionAndDecryption() throws Exception { + MessageEncrypter m = new MessageEncrypter(random); + AsymmetricCipherKeyPair kp = m.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters) kp.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters) kp.getPrivate(); + byte[] plaintext = new byte[123]; + random.nextBytes(plaintext); + byte[] ciphertext = m.encrypt(pub, plaintext); + byte[] decrypted = m.decrypt(priv, ciphertext); + assertArrayEquals(plaintext, decrypted); + } + + @Test(expected = CryptoException.class) + public void testDecryptionFailsWithAlteredCiphertext() throws Exception { + MessageEncrypter m = new MessageEncrypter(random); + AsymmetricCipherKeyPair kp = m.generateKeyPair(); + ECPublicKeyParameters pub = (ECPublicKeyParameters) kp.getPublic(); + ECPrivateKeyParameters priv = (ECPrivateKeyParameters) kp.getPrivate(); + byte[] ciphertext = m.encrypt(pub, new byte[123]); + ciphertext[random.nextInt(ciphertext.length)] ^= 0xFF; + m.decrypt(priv, ciphertext); + } +} From d545aaa892dad9c78930d50517d7dc10150f36b1 Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 31 Mar 2016 11:11:55 +0000 Subject: [PATCH 3/5] Encrypt and save crash reports, send them the next time TorPlugin start Will currently fail at runtime; requires a public key and a server onion. --- briar-android/build.gradle | 3 + briar-android/res/values/strings.xml | 4 + .../android/AndroidComponent.java | 2 + .../org/briarproject/android/AppModule.java | 29 ++++ .../android/CrashReportActivity.java | 55 ++++++-- .../android/util/AndroidUtils.java | 8 ++ .../plugins/AndroidPluginsModule.java | 6 +- .../briarproject/plugins/tor/TorPlugin.java | 39 ++++-- .../plugins/tor/TorPluginFactory.java | 13 +- .../api/crypto/CryptoComponent.java | 6 + .../briarproject/api/reporting/DevConfig.java | 10 ++ .../api/reporting/DevReporter.java | 28 ++++ {briar-android => briar-core}/libs/jsocks.jar | Bin .../src/org/briarproject/CoreModule.java | 2 + .../crypto/CryptoComponentImpl.java | 14 ++ .../briarproject/crypto/MessageEncrypter.java | 13 +- .../reporting/DevReporterImpl.java | 124 ++++++++++++++++++ .../reporting/ReportingModule.java | 18 +++ 18 files changed, 342 insertions(+), 32 deletions(-) create mode 100644 briar-api/src/org/briarproject/api/reporting/DevConfig.java create mode 100644 briar-api/src/org/briarproject/api/reporting/DevReporter.java rename {briar-android => briar-core}/libs/jsocks.jar (100%) create mode 100644 briar-core/src/org/briarproject/reporting/DevReporterImpl.java create mode 100644 briar-core/src/org/briarproject/reporting/ReportingModule.java diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 89484f4a1..0b7908081 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -12,6 +12,9 @@ dependencies { compile project(':briar-api') compile project(':briar-core') compile fileTree(dir: 'libs', include: '*.jar') + // This shouldn't be necessary; per section 23.4.4 of the Gradle docs: + // "file dependencies are included in transitive project dependencies within the same build". + compile files('../briar-core/libs/jsocks.jar') compile "com.android.support:support-v4:$supportVersion" compile("com.android.support:appcompat-v7:$supportVersion") { exclude module: 'support-v4' diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index ca1e962f4..ec531c92d 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -4,6 +4,8 @@ Close the navigation drawer Briar Briar Crash Report + Crash report saved. It will be sent the next time you log into Briar. + Could not save crash report to disk. Signed into Briar Touch to show the dashboard. Briar Setup @@ -173,6 +175,8 @@ Are you sure that you want to allow %1$s to trigger destructive panic button actions? Welcome to Briar Add a contact to start communicating securely or press the icon in the upper left corner of the screen for more options. + Send to developers? + Would you like to send this crash report to the developers? It will be stored encrypted on your device until the next time you log into Briar, and then sent securely to the developers. OK Introduce Accept diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java index 5b73d5e53..a3ced844d 100644 --- a/briar-android/src/org/briarproject/android/AndroidComponent.java +++ b/briar-android/src/org/briarproject/android/AndroidComponent.java @@ -37,6 +37,8 @@ import dagger.Component; }) public interface AndroidComponent extends CoreEagerSingletons { + void inject(CrashReportActivity crashReportActivity); + void inject(SplashScreenActivity activity); void inject(SetupActivity activity); diff --git a/briar-android/src/org/briarproject/android/AppModule.java b/briar-android/src/org/briarproject/android/AppModule.java index 066b0d108..e412c9b5b 100644 --- a/briar-android/src/org/briarproject/android/AppModule.java +++ b/briar-android/src/org/briarproject/android/AppModule.java @@ -8,7 +8,9 @@ import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; import org.briarproject.api.event.EventBus; import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.reporting.DevConfig; import org.briarproject.api.ui.UiCallback; +import org.briarproject.util.StringUtils; import java.io.File; @@ -90,6 +92,33 @@ public class AppModule { }; } + @Provides + @Singleton + public DevConfig provideDevConfig() { + return new DevConfig() { + + // TODO fill in + private final byte[] DEV_PUB_KEY = StringUtils.fromHexString(""); + private final String DEV_ONION = ""; + private final int DEV_REPORT_PORT = 8080; + + @Override + public byte[] getDevPublicKey() { + return DEV_PUB_KEY; + } + + @Override + public String getDevOnionAddress() { + return DEV_ONION; + } + + @Override + public int getDevReportPort() { + return DEV_REPORT_PORT; + } + }; + } + @Provides @Singleton ReferenceManager provideReferenceManager() { diff --git a/briar-android/src/org/briarproject/android/CrashReportActivity.java b/briar-android/src/org/briarproject/android/CrashReportActivity.java index 881977c32..8e5fe4c8c 100644 --- a/briar-android/src/org/briarproject/android/CrashReportActivity.java +++ b/briar-android/src/org/briarproject/android/CrashReportActivity.java @@ -2,7 +2,9 @@ package org.briarproject.android; import android.annotation.SuppressLint; import android.app.ActivityManager; +import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; +import android.content.DialogInterface; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -18,12 +20,15 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.LinearLayout; import android.widget.TextView; +import android.widget.Toast; import org.briarproject.R; import org.briarproject.android.util.AndroidUtils; +import org.briarproject.api.reporting.DevReporter; import org.briarproject.util.StringUtils; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -36,12 +41,10 @@ import java.util.Scanner; import java.util.logging.Logger; import java.util.regex.Pattern; +import javax.inject.Inject; + import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; -import static android.content.Intent.ACTION_SEND; -import static android.content.Intent.EXTRA_EMAIL; -import static android.content.Intent.EXTRA_SUBJECT; -import static android.content.Intent.EXTRA_TEXT; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; @@ -59,6 +62,9 @@ public class CrashReportActivity extends AppCompatActivity private LinearLayout status = null; private View progress = null; + @Inject + protected DevReporter reporter; + private volatile String stack = null; private volatile int pid = -1; private volatile BluetoothAdapter bt = null; @@ -68,6 +74,9 @@ public class CrashReportActivity extends AppCompatActivity super.onCreate(state); setContentView(R.layout.activity_crash); + ((BriarApplication) getApplication()).getApplicationComponent() + .inject(this); + status = (LinearLayout) findViewById(R.id.crash_status); progress = findViewById(R.id.progress_wheel); @@ -94,7 +103,20 @@ public class CrashReportActivity extends AppCompatActivity } public void onClick(View view) { - share(); + // TODO Encapsulate the dialog in a re-usable fragment + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.dialog_title_share_crash_report); + builder.setMessage(R.string.dialog_message_share_crash_report); + builder.setNegativeButton(R.string.cancel_button, null); + builder.setPositiveButton(R.string.send, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + saveCrashReport(); + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); } private void refresh() { @@ -324,7 +346,7 @@ public class CrashReportActivity extends AppCompatActivity return Character.toUpperCase(first) + s.substring(1); } - private void share() { + private void saveCrashReport() { StringBuilder s = new StringBuilder(); for (Entry e : getStatusMap().entrySet()) { s.append(e.getKey()); @@ -332,12 +354,19 @@ public class CrashReportActivity extends AppCompatActivity s.append(e.getValue()); s.append("\n\n"); } - String body = s.toString(); - Intent i = new Intent(ACTION_SEND); - i.setType("message/rfc822"); - i.putExtra(EXTRA_EMAIL, new String[] { "contact@briarproject.org" }); - i.putExtra(EXTRA_SUBJECT, "Crash report"); - i.putExtra(EXTRA_TEXT, body); - startActivity(Intent.createChooser(i, "Send to developers")); + final String crashReport = s.toString(); + try { + reporter.encryptCrashReportToFile( + AndroidUtils.getCrashReportDir(this), crashReport); + Toast.makeText(this, R.string.crash_report_saved, Toast.LENGTH_LONG) + .show(); + finish(); + } catch (FileNotFoundException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, "Error while saving encrypted crash report", + e); + Toast.makeText(this, R.string.crash_report_not_saved, + Toast.LENGTH_SHORT).show(); + } } } diff --git a/briar-android/src/org/briarproject/android/util/AndroidUtils.java b/briar-android/src/org/briarproject/android/util/AndroidUtils.java index 005b20eb9..155e50e2b 100644 --- a/briar-android/src/org/briarproject/android/util/AndroidUtils.java +++ b/briar-android/src/org/briarproject/android/util/AndroidUtils.java @@ -17,11 +17,15 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import static android.content.Context.MODE_PRIVATE; + public class AndroidUtils { // Fake Bluetooth address returned by BluetoothAdapter on API 23 and later private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00"; + private static final String STORED_CRASH_REPORTS = "crash-reports"; + @SuppressLint("NewApi") @SuppressWarnings("deprecation") public static Collection getSupportedArchitectures() { @@ -84,4 +88,8 @@ public class AndroidUtils { } } } + + public static File getCrashReportDir(Context ctx) { + return ctx.getDir(STORED_CRASH_REPORTS, MODE_PRIVATE); + } } diff --git a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java index feccd77ff..4ebf3886e 100644 --- a/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java +++ b/briar-android/src/org/briarproject/plugins/AndroidPluginsModule.java @@ -10,6 +10,7 @@ import org.briarproject.api.plugins.BackoffFactory; import org.briarproject.api.plugins.PluginConfig; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; import org.briarproject.api.plugins.simplex.SimplexPluginFactory; +import org.briarproject.api.reporting.DevReporter; import org.briarproject.api.system.LocationUtils; import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory; import org.briarproject.plugins.tcp.AndroidLanTcpPluginFactory; @@ -31,12 +32,13 @@ public class AndroidPluginsModule { public PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor, AndroidExecutor androidExecutor, SecureRandom random, BackoffFactory backoffFactory, Application app, - LocationUtils locationUtils, EventBus eventBus) { + LocationUtils locationUtils, DevReporter reporter, + EventBus eventBus) { Context appContext = app.getApplicationContext(); DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, androidExecutor, appContext, random, backoffFactory); DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, - locationUtils, eventBus); + locationUtils, reporter, eventBus); DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, backoffFactory, appContext); final Collection duplex = diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java index d3dbff793..376a60ef5 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java @@ -13,6 +13,7 @@ import net.freehaven.tor.control.TorControlConnection; import net.sourceforge.jsocks.socks.Socks5Proxy; import net.sourceforge.jsocks.socks.SocksSocket; +import org.briarproject.android.util.AndroidUtils; import org.briarproject.api.TransportId; import org.briarproject.api.contact.ContactId; import org.briarproject.api.crypto.PseudoRandom; @@ -25,6 +26,7 @@ import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.properties.TransportProperties; +import org.briarproject.api.reporting.DevReporter; import org.briarproject.api.settings.Settings; import org.briarproject.api.system.Clock; import org.briarproject.api.system.LocationUtils; @@ -83,6 +85,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, private final Executor ioExecutor; private final Context appContext; private final LocationUtils locationUtils; + private final DevReporter reporter; private final Clock clock; private final DuplexPluginCallback callback; private final String architecture; @@ -104,12 +107,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, private volatile BroadcastReceiver networkStateReceiver = null; TorPlugin(Executor ioExecutor, Context appContext, - LocationUtils locationUtils, Clock clock, + LocationUtils locationUtils, DevReporter reporter, Clock clock, DuplexPluginCallback callback, String architecture, int maxLatency, int maxIdleTime, int pollingInterval) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; + this.reporter = reporter; this.clock = clock; this.callback = callback; this.architecture = architecture; @@ -172,13 +176,14 @@ class TorPlugin implements DuplexPlugin, EventHandler, String torPath = torFile.getAbsolutePath(); String configPath = configFile.getAbsolutePath(); String pid = String.valueOf(android.os.Process.myPid()); - String[] cmd = { torPath, "-f", configPath, OWNER, pid }; - String[] env = { "HOME=" + torDirectory.getAbsolutePath() }; + String[] cmd = {torPath, "-f", configPath, OWNER, pid}; + String[] env = {"HOME=" + torDirectory.getAbsolutePath()}; Process torProcess; try { torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory); } catch (SecurityException e1) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e1.toString(), e1); + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e1.toString(), e1); return false; } // Log the process's standard output until it detaches @@ -225,6 +230,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, if (phase != null && phase.contains("PROGRESS=100")) { LOG.info("Tor has already bootstrapped"); bootstrapped = true; + sendCrashReports(); } } // Register to receive network status events @@ -355,6 +361,16 @@ class TorPlugin implements DuplexPlugin, EventHandler, } } + private void sendCrashReports() { + ioExecutor.execute(new Runnable() { + @Override + public void run() { + reporter.sendCrashReports( + AndroidUtils.getCrashReportDir(appContext), SOCKS_PORT); + } + }); + } + private void bind() { ioExecutor.execute(new Runnable() { public void run() { @@ -420,7 +436,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, obs.startWatching(); // Use the control connection to update the Tor config List config = Arrays.asList( - "HiddenServiceDir " + serviceDirectory.getAbsolutePath(), + "HiddenServiceDir " + + serviceDirectory.getAbsolutePath(), "HiddenServicePort 80 127.0.0.1:" + port); controlConnection.setConf(config); controlConnection.saveConf(); @@ -593,20 +610,24 @@ class TorPlugin implements DuplexPlugin, EventHandler, } } - public void streamStatus(String status, String id, String target) {} + public void streamStatus(String status, String id, String target) { + } public void orConnStatus(String status, String orName) { if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status); } - public void bandwidthUsed(long read, long written) {} + public void bandwidthUsed(long read, long written) { + } - public void newDescriptors(List orList) {} + public void newDescriptors(List orList) { + } public void message(String severity, String msg) { if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg); if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) { bootstrapped = true; + sendCrashReports(); if (isRunning()) callback.transportEnabled(); } } @@ -669,7 +690,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, } else if (blocked) { LOG.info("Disabling network, country is blocked"); enableNetwork(false); - } else if (wifiOnly & !connectedToWifi){ + } else if (wifiOnly & !connectedToWifi) { LOG.info("Disabling network due to wifi setting"); enableNetwork(false); } else { diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java index 836dd48b6..edf0103da 100644 --- a/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java +++ b/briar-android/src/org/briarproject/plugins/tor/TorPluginFactory.java @@ -9,6 +9,7 @@ import org.briarproject.api.event.EventBus; import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginFactory; +import org.briarproject.api.reporting.DevReporter; import org.briarproject.api.system.Clock; import org.briarproject.api.system.LocationUtils; import org.briarproject.system.SystemClock; @@ -28,14 +29,17 @@ public class TorPluginFactory implements DuplexPluginFactory { private final Executor ioExecutor; private final Context appContext; private final LocationUtils locationUtils; + private final DevReporter reporter; private final EventBus eventBus; private final Clock clock; public TorPluginFactory(Executor ioExecutor, Context appContext, - LocationUtils locationUtils, EventBus eventBus) { + LocationUtils locationUtils, DevReporter reporter, + EventBus eventBus) { this.ioExecutor = ioExecutor; this.appContext = appContext; this.locationUtils = locationUtils; + this.reporter = reporter; this.eventBus = eventBus; clock = new SystemClock(); } @@ -68,9 +72,10 @@ public class TorPluginFactory implements DuplexPluginFactory { // Use position-independent executable for SDK >= 16 if (Build.VERSION.SDK_INT >= 16) architecture += "-pie"; - TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils, - clock, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME, - POLLING_INTERVAL); + TorPlugin plugin = + new TorPlugin(ioExecutor, appContext, locationUtils, reporter, + clock, callback, architecture, MAX_LATENCY, + MAX_IDLE_TIME, POLLING_INTERVAL); eventBus.addListener(plugin); return plugin; } diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java index 11631f22f..5f72ae312 100644 --- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java +++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java @@ -3,6 +3,7 @@ package org.briarproject.api.crypto; import org.briarproject.api.TransportId; import org.briarproject.api.transport.TransportKeys; +import java.io.IOException; import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -156,4 +157,9 @@ public interface CryptoComponent { * authenticated (for example, if the password is wrong). */ byte[] decryptWithPassword(byte[] ciphertext, String password); + + /** + * Encrypts the given plaintext to the given public key. + */ + String encryptToKey(byte[] publicKey, byte[] plaintext); } diff --git a/briar-api/src/org/briarproject/api/reporting/DevConfig.java b/briar-api/src/org/briarproject/api/reporting/DevConfig.java new file mode 100644 index 000000000..4152cd927 --- /dev/null +++ b/briar-api/src/org/briarproject/api/reporting/DevConfig.java @@ -0,0 +1,10 @@ +package org.briarproject.api.reporting; + +public interface DevConfig { + + byte[] getDevPublicKey(); + + String getDevOnionAddress(); + + int getDevReportPort(); +} diff --git a/briar-api/src/org/briarproject/api/reporting/DevReporter.java b/briar-api/src/org/briarproject/api/reporting/DevReporter.java new file mode 100644 index 000000000..a2172b0f9 --- /dev/null +++ b/briar-api/src/org/briarproject/api/reporting/DevReporter.java @@ -0,0 +1,28 @@ +package org.briarproject.api.reporting; + +import java.io.File; +import java.io.FileNotFoundException; + +/** + * A task for reporting back to the developers. + */ +public interface DevReporter { + + /** + * Store a crash report encrypted on-disk to be sent later. + * + * @param crashReportDir the directory where crash reports are stored. + * @param crashReport the crash report in the form expected by the server. + * @throws FileNotFoundException if the report could not be written. + */ + void encryptCrashReportToFile(File crashReportDir, String crashReport) + throws FileNotFoundException; + + /** + * Send crash reports previously stored on-disk. + * + * @param crashReportDir the directory where crash reports are stored. + * @param socksPort the SOCKS port of a Tor client. + */ + void sendCrashReports(File crashReportDir, int socksPort); +} diff --git a/briar-android/libs/jsocks.jar b/briar-core/libs/jsocks.jar similarity index 100% rename from briar-android/libs/jsocks.jar rename to briar-core/libs/jsocks.jar diff --git a/briar-core/src/org/briarproject/CoreModule.java b/briar-core/src/org/briarproject/CoreModule.java index 14f5e4a55..62828dfff 100644 --- a/briar-core/src/org/briarproject/CoreModule.java +++ b/briar-core/src/org/briarproject/CoreModule.java @@ -17,6 +17,7 @@ import org.briarproject.messaging.MessagingModule; import org.briarproject.plugins.PluginsModule; import org.briarproject.properties.PropertiesModule; import org.briarproject.reliability.ReliabilityModule; +import org.briarproject.reporting.ReportingModule; import org.briarproject.settings.SettingsModule; import org.briarproject.sync.SyncModule; import org.briarproject.system.SystemModule; @@ -42,6 +43,7 @@ import dagger.Module; PluginsModule.class, PropertiesModule.class, ReliabilityModule.class, + ReportingModule.class, SettingsModule.class, SyncModule.class, SystemModule.class, diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index bdf74dfaa..87a65ef64 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -18,6 +18,7 @@ import org.briarproject.util.ByteUtils; import org.briarproject.util.StringUtils; import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.CryptoException; import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; import org.spongycastle.crypto.digests.SHA256Digest; @@ -28,6 +29,7 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.KeyParameter; +import java.io.IOException; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -438,6 +440,18 @@ class CryptoComponentImpl implements CryptoComponent { } } + public String encryptToKey(byte[] publicKey, byte[] plaintext) { + MessageEncrypter encrypter = new MessageEncrypter(secureRandom); + try { + byte[] ciphertext = encrypter.encrypt(publicKey, plaintext); + return AsciiArmour.wrap(ciphertext, 70); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (CryptoException e) { + throw new RuntimeException(e); + } + } + // Key derivation function based on a pseudo-random function - see // NIST SP 800-108, section 5.1 private byte[] macKdf(SecretKey key, byte[]... inputs) { diff --git a/briar-core/src/org/briarproject/crypto/MessageEncrypter.java b/briar-core/src/org/briarproject/crypto/MessageEncrypter.java index a92eed0ab..5453bcd2f 100644 --- a/briar-core/src/org/briarproject/crypto/MessageEncrypter.java +++ b/briar-core/src/org/briarproject/crypto/MessageEncrypter.java @@ -70,6 +70,14 @@ public class MessageEncrypter { return generator.generateKeyPair(); } + byte[] encrypt(byte[] keyBytes, byte[] plaintext) + throws IOException, CryptoException { + InputStream in = new ByteArrayInputStream(keyBytes); + ECPublicKeyParameters publicKey = + (ECPublicKeyParameters) parser.readKey(in); + return encrypt(publicKey, plaintext); + } + byte[] encrypt(ECPublicKeyParameters pubKey, byte[] plaintext) throws CryptoException { IESEngine engine = getEngine(); @@ -159,10 +167,7 @@ public class MessageEncrypter { } // Encrypt a decrypted message InputStream in = new FileInputStream(args[1]); - byte[] b = StringUtils.fromHexString(readFully(in).trim()); - in = new ByteArrayInputStream(b); - ECPublicKeyParameters publicKey = - (ECPublicKeyParameters) encrypter.parser.readKey(in); + byte[] publicKey = StringUtils.fromHexString(readFully(in).trim()); String message = readFully(System.in); byte[] plaintext = message.getBytes(Charset.forName("UTF-8")); byte[] ciphertext = encrypter.encrypt(publicKey, plaintext); diff --git a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java new file mode 100644 index 000000000..d34bbeeef --- /dev/null +++ b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java @@ -0,0 +1,124 @@ +package org.briarproject.reporting; + +import com.google.common.io.Files; + +import net.sourceforge.jsocks.socks.Socks5Proxy; +import net.sourceforge.jsocks.socks.SocksException; +import net.sourceforge.jsocks.socks.SocksSocket; + +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.reporting.DevConfig; +import org.briarproject.api.reporting.DevReporter; +import org.briarproject.util.StringUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; + +class DevReporterImpl implements DevReporter { + + private static final Logger LOG = + Logger.getLogger(DevReporterImpl.class.getName()); + + private static final int TIMEOUT = 30 * 1000; // 30 seconds + private static final String PREFIX = "briar-"; + private static final String REPORT_EXT = ".report"; + private static final String CRLF = "\r\n"; + + private CryptoComponent crypto; + private DevConfig devConfig; + + public DevReporterImpl(CryptoComponent crypto, DevConfig devConfig) { + this.crypto = crypto; + this.devConfig = devConfig; + } + + private Socket connectToDevelopers(int socksPort, int devPort) + throws UnknownHostException, SocksException, SocketException { + Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", socksPort); + proxy.resolveAddrLocally(false); + Socket s = + new SocksSocket(proxy, devConfig.getDevOnionAddress(), devPort); + s.setSoTimeout(TIMEOUT); + return s; + } + + @Override + public void encryptCrashReportToFile(File crashReportDir, + String crashReport) throws FileNotFoundException { + String encryptedReport = + crypto.encryptToKey(devConfig.getDevPublicKey(), + StringUtils.toUtf8(crashReport)); + + String filename = PREFIX + new Date().getTime() + REPORT_EXT; + File report = new File(crashReportDir, filename); + PrintWriter writer = null; + try { + writer = new PrintWriter( + new OutputStreamWriter(new FileOutputStream(report))); + writer.append(encryptedReport); + writer.flush(); + } finally { + if (writer != null) + writer.close(); + } + } + + @Override + public void sendCrashReports(File crashReportDir, int socksPort) { + File[] reports = crashReportDir.listFiles(); + if (reports == null || reports.length == 0) + return; // No crash reports to send + + LOG.info("Connecting to developers' Hidden Service"); + Socket s; + try { + s = connectToDevelopers(socksPort, + devConfig.getDevReportPort()); + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, "Tor SOCKS proxy failed", e); + return; + } + + LOG.info("Sending crash reports to developers"); + OutputStream output; + PrintWriter writer = null; + try { + output = s.getOutputStream(); + writer = new PrintWriter( + new OutputStreamWriter(output, "UTF-8"), true); + for (File f : reports) { + List encryptedReport = Files.readLines(f, + Charset.forName("UTF-8")); + writer.append(f.getName()).append(CRLF); + for (String line : encryptedReport) { + writer.append(line).append(CRLF); + } + writer.append(CRLF); + f.delete(); + } + writer.flush(); + LOG.info("Crash reports sent"); + } catch (IOException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, "Connection to developers failed", e); + } finally { + if (writer != null) + writer.close(); + } + } +} diff --git a/briar-core/src/org/briarproject/reporting/ReportingModule.java b/briar-core/src/org/briarproject/reporting/ReportingModule.java new file mode 100644 index 000000000..7c88261de --- /dev/null +++ b/briar-core/src/org/briarproject/reporting/ReportingModule.java @@ -0,0 +1,18 @@ +package org.briarproject.reporting; + +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.reporting.DevConfig; +import org.briarproject.api.reporting.DevReporter; + +import dagger.Module; +import dagger.Provides; + +@Module +public class ReportingModule { + + @Provides + DevReporter provideDevReportTask(CryptoComponent crypto, + DevConfig devConfig) { + return new DevReporterImpl(crypto, devConfig); + } +} From 5c2f56549b4f894eb8c5c54d84bfefac0575fa3b Mon Sep 17 00:00:00 2001 From: str4d Date: Thu, 21 Apr 2016 00:32:58 +0000 Subject: [PATCH 4/5] Refactor MessageEncrypter interface to use PublicKey and PrivateKey --- .../org/briarproject/android/AppModule.java | 18 ++++- .../api/crypto/CryptoComponent.java | 4 +- .../briarproject/api/reporting/DevConfig.java | 4 +- .../crypto/CryptoComponentImpl.java | 14 ++-- .../briarproject/crypto/MessageEncrypter.java | 77 +++++++++++-------- .../crypto/MessageEncrypterTest.java | 15 ++-- 6 files changed, 83 insertions(+), 49 deletions(-) diff --git a/briar-android/src/org/briarproject/android/AppModule.java b/briar-android/src/org/briarproject/android/AppModule.java index e412c9b5b..22d59c206 100644 --- a/briar-android/src/org/briarproject/android/AppModule.java +++ b/briar-android/src/org/briarproject/android/AppModule.java @@ -4,6 +4,8 @@ import android.app.Application; import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.api.ReferenceManager; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.PublicKey; import org.briarproject.api.crypto.SecretKey; import org.briarproject.api.db.DatabaseConfig; import org.briarproject.api.event.EventBus; @@ -13,6 +15,7 @@ import org.briarproject.api.ui.UiCallback; import org.briarproject.util.StringUtils; import java.io.File; +import java.security.GeneralSecurityException; import javax.inject.Inject; import javax.inject.Singleton; @@ -94,16 +97,25 @@ public class AppModule { @Provides @Singleton - public DevConfig provideDevConfig() { + public DevConfig provideDevConfig(CryptoComponent crypto) { + final PublicKey pub; + try { + // TODO fill in + pub = crypto.getMessageKeyParser() + .parsePublicKey(StringUtils.fromHexString("")); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + return new DevConfig() { + private final PublicKey DEV_PUB_KEY = pub; // TODO fill in - private final byte[] DEV_PUB_KEY = StringUtils.fromHexString(""); private final String DEV_ONION = ""; private final int DEV_REPORT_PORT = 8080; @Override - public byte[] getDevPublicKey() { + public PublicKey getDevPublicKey() { return DEV_PUB_KEY; } diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java index 5f72ae312..dedcafbff 100644 --- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java +++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java @@ -27,6 +27,8 @@ public interface CryptoComponent { KeyParser getSignatureKeyParser(); + KeyParser getMessageKeyParser(); + /** Generates a random invitation code. */ int generateBTInvitationCode(); @@ -161,5 +163,5 @@ public interface CryptoComponent { /** * Encrypts the given plaintext to the given public key. */ - String encryptToKey(byte[] publicKey, byte[] plaintext); + String encryptToKey(PublicKey publicKey, byte[] plaintext); } diff --git a/briar-api/src/org/briarproject/api/reporting/DevConfig.java b/briar-api/src/org/briarproject/api/reporting/DevConfig.java index 4152cd927..f82e81318 100644 --- a/briar-api/src/org/briarproject/api/reporting/DevConfig.java +++ b/briar-api/src/org/briarproject/api/reporting/DevConfig.java @@ -1,8 +1,10 @@ package org.briarproject.api.reporting; +import org.briarproject.api.crypto.PublicKey; + public interface DevConfig { - byte[] getDevPublicKey(); + PublicKey getDevPublicKey(); String getDevOnionAddress(); diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java index 87a65ef64..2411e5198 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java +++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java @@ -29,7 +29,6 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.KeyParameter; -import java.io.IOException; import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.security.SecureRandom; @@ -95,6 +94,7 @@ class CryptoComponentImpl implements CryptoComponent { private final ECKeyPairGenerator agreementKeyPairGenerator; private final ECKeyPairGenerator signatureKeyPairGenerator; private final KeyParser agreementKeyParser, signatureKeyParser; + private final MessageEncrypter messageEncrypter; @Inject CryptoComponentImpl(SeedProvider seedProvider) { @@ -117,6 +117,7 @@ class CryptoComponentImpl implements CryptoComponent { AGREEMENT_KEY_PAIR_BITS); signatureKeyParser = new Sec1KeyParser(PARAMETERS, SIGNATURE_KEY_PAIR_BITS); + messageEncrypter = new MessageEncrypter(secureRandom); } public SecretKey generateSecretKey() { @@ -198,6 +199,10 @@ class CryptoComponentImpl implements CryptoComponent { return signatureKeyParser; } + public KeyParser getMessageKeyParser() { + return messageEncrypter.getKeyParser(); + } + public int generateBTInvitationCode() { int codeBytes = (CODE_BITS + 7) / 8; byte[] random = new byte[codeBytes]; @@ -440,13 +445,10 @@ class CryptoComponentImpl implements CryptoComponent { } } - public String encryptToKey(byte[] publicKey, byte[] plaintext) { - MessageEncrypter encrypter = new MessageEncrypter(secureRandom); + public String encryptToKey(PublicKey publicKey, byte[] plaintext) { try { - byte[] ciphertext = encrypter.encrypt(publicKey, plaintext); + byte[] ciphertext = messageEncrypter.encrypt(publicKey, plaintext); return AsciiArmour.wrap(ciphertext, 70); - } catch (IOException e) { - throw new RuntimeException(e); } catch (CryptoException e) { throw new RuntimeException(e); } diff --git a/briar-core/src/org/briarproject/crypto/MessageEncrypter.java b/briar-core/src/org/briarproject/crypto/MessageEncrypter.java index 5453bcd2f..bbef555e9 100644 --- a/briar-core/src/org/briarproject/crypto/MessageEncrypter.java +++ b/briar-core/src/org/briarproject/crypto/MessageEncrypter.java @@ -1,5 +1,9 @@ package org.briarproject.crypto; +import org.briarproject.api.crypto.KeyPair; +import org.briarproject.api.crypto.KeyParser; +import org.briarproject.api.crypto.PrivateKey; +import org.briarproject.api.crypto.PublicKey; import org.briarproject.util.StringUtils; import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; import org.spongycastle.asn1.x9.X9ECParameters; @@ -10,7 +14,6 @@ import org.spongycastle.crypto.CipherParameters; import org.spongycastle.crypto.CryptoException; import org.spongycastle.crypto.DerivationFunction; import org.spongycastle.crypto.KeyEncoder; -import org.spongycastle.crypto.KeyParser; import org.spongycastle.crypto.Mac; import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; import org.spongycastle.crypto.digests.SHA256Digest; @@ -30,13 +33,11 @@ import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.IESWithCipherParameters; import org.spongycastle.crypto.parsers.ECIESPublicKeyParser; -import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; -import java.math.BigInteger; import java.nio.charset.Charset; import java.security.SecureRandom; import java.util.Scanner; @@ -44,6 +45,7 @@ import java.util.Scanner; public class MessageEncrypter { private static final ECDomainParameters PARAMETERS; + private static final int MESSAGE_KEY_BITS = 512; private static final int MAC_KEY_BITS = 256; private static final int CIPHER_KEY_BITS = 256; private static final int LINE_LENGTH = 70; @@ -55,40 +57,52 @@ public class MessageEncrypter { } private final ECKeyPairGenerator generator; - private final EphemeralKeyPairGenerator ephemeralGenerator; private final KeyParser parser; + private final EphemeralKeyPairGenerator ephemeralGenerator; + private final PublicKeyParser ephemeralParser; MessageEncrypter(SecureRandom random) { generator = new ECKeyPairGenerator(); generator.init(new ECKeyGenerationParameters(PARAMETERS, random)); + parser = new Sec1KeyParser(PARAMETERS, MESSAGE_KEY_BITS); KeyEncoder encoder = new PublicKeyEncoder(); ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder); - parser = new PublicKeyParser(PARAMETERS); + ephemeralParser = new PublicKeyParser(PARAMETERS); } - AsymmetricCipherKeyPair generateKeyPair() { - return generator.generateKeyPair(); + KeyPair generateKeyPair() { + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + // Return a wrapper that uses the SEC 1 encoding + ECPublicKeyParameters ecPublicKey = + (ECPublicKeyParameters) keyPair.getPublic(); + PublicKey publicKey = new Sec1PublicKey(ecPublicKey); + ECPrivateKeyParameters ecPrivateKey = + (ECPrivateKeyParameters) keyPair.getPrivate(); + PrivateKey privateKey = + new Sec1PrivateKey(ecPrivateKey, MESSAGE_KEY_BITS); + return new KeyPair(publicKey, privateKey); } - byte[] encrypt(byte[] keyBytes, byte[] plaintext) - throws IOException, CryptoException { - InputStream in = new ByteArrayInputStream(keyBytes); - ECPublicKeyParameters publicKey = - (ECPublicKeyParameters) parser.readKey(in); - return encrypt(publicKey, plaintext); + KeyParser getKeyParser() { + return parser; } - byte[] encrypt(ECPublicKeyParameters pubKey, byte[] plaintext) - throws CryptoException { + byte[] encrypt(PublicKey pub, byte[] plaintext) throws CryptoException { + if (!(pub instanceof Sec1PublicKey)) + throw new IllegalArgumentException(); IESEngine engine = getEngine(); - engine.init(pubKey, getCipherParameters(), ephemeralGenerator); + engine.init(((Sec1PublicKey) pub).getKey(), getCipherParameters(), + ephemeralGenerator); return engine.processBlock(plaintext, 0, plaintext.length); } - byte[] decrypt(ECPrivateKeyParameters privKey, byte[] ciphertext) + byte[] decrypt(PrivateKey priv, byte[] ciphertext) throws CryptoException { + if (!(priv instanceof Sec1PrivateKey)) + throw new IllegalArgumentException(); IESEngine engine = getEngine(); - engine.init(privKey, getCipherParameters(), parser); + engine.init(((Sec1PrivateKey) priv).getKey(), getCipherParameters(), + ephemeralParser); return engine.processBlock(ciphertext, 0, ciphertext.length); } @@ -146,18 +160,15 @@ public class MessageEncrypter { return; } // Generate a key pair - AsymmetricCipherKeyPair keyPair = encrypter.generateKeyPair(); - ECPublicKeyParameters publicKey = - (ECPublicKeyParameters) keyPair.getPublic(); - byte[] publicKeyBytes = publicKey.getQ().getEncoded(false); + KeyPair keyPair = encrypter.generateKeyPair(); PrintStream out = new PrintStream(new FileOutputStream(args[1])); - out.print(StringUtils.toHexString(publicKeyBytes)); + out.print( + StringUtils.toHexString(keyPair.getPublic().getEncoded())); out.flush(); out.close(); - ECPrivateKeyParameters privateKey = - (ECPrivateKeyParameters) keyPair.getPrivate(); out = new PrintStream(new FileOutputStream(args[2])); - out.print(privateKey.getD().toString(16).toUpperCase()); + out.print( + StringUtils.toHexString(keyPair.getPrivate().getEncoded())); out.flush(); out.close(); } else if (args[0].equals("encrypt")) { @@ -167,7 +178,9 @@ public class MessageEncrypter { } // Encrypt a decrypted message InputStream in = new FileInputStream(args[1]); - byte[] publicKey = StringUtils.fromHexString(readFully(in).trim()); + byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim()); + PublicKey publicKey = + encrypter.getKeyParser().parsePublicKey(keyBytes); String message = readFully(System.in); byte[] plaintext = message.getBytes(Charset.forName("UTF-8")); byte[] ciphertext = encrypter.encrypt(publicKey, plaintext); @@ -179,10 +192,9 @@ public class MessageEncrypter { } // Decrypt an encrypted message InputStream in = new FileInputStream(args[1]); - byte[] b = StringUtils.fromHexString(readFully(in).trim()); - BigInteger d = new BigInteger(1, b); - ECPrivateKeyParameters privateKey = new ECPrivateKeyParameters(d, - PARAMETERS); + byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim()); + PrivateKey privateKey = + encrypter.getKeyParser().parsePrivateKey(keyBytes); byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in)); byte[] plaintext = encrypter.decrypt(privateKey, ciphertext); System.out.println(new String(plaintext, Charset.forName("UTF-8"))); @@ -193,7 +205,8 @@ public class MessageEncrypter { private static void printUsage() { System.err.println("Usage:"); - System.err.println("MessageEncrypter generate "); + System.err.println( + "MessageEncrypter generate "); System.err.println("MessageEncrypter encrypt "); System.err.println("MessageEncrypter decrypt "); } diff --git a/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java b/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java index 29c07984e..351fa48af 100644 --- a/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java +++ b/briar-tests/src/org/briarproject/crypto/MessageEncrypterTest.java @@ -1,6 +1,9 @@ package org.briarproject.crypto; import org.briarproject.BriarTestCase; +import org.briarproject.api.crypto.KeyPair; +import org.briarproject.api.crypto.PrivateKey; +import org.briarproject.api.crypto.PublicKey; import org.junit.Test; import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.CryptoException; @@ -18,9 +21,9 @@ public class MessageEncrypterTest extends BriarTestCase { @Test public void testEncryptionAndDecryption() throws Exception { MessageEncrypter m = new MessageEncrypter(random); - AsymmetricCipherKeyPair kp = m.generateKeyPair(); - ECPublicKeyParameters pub = (ECPublicKeyParameters) kp.getPublic(); - ECPrivateKeyParameters priv = (ECPrivateKeyParameters) kp.getPrivate(); + KeyPair kp = m.generateKeyPair(); + PublicKey pub = kp.getPublic(); + PrivateKey priv = kp.getPrivate(); byte[] plaintext = new byte[123]; random.nextBytes(plaintext); byte[] ciphertext = m.encrypt(pub, plaintext); @@ -31,9 +34,9 @@ public class MessageEncrypterTest extends BriarTestCase { @Test(expected = CryptoException.class) public void testDecryptionFailsWithAlteredCiphertext() throws Exception { MessageEncrypter m = new MessageEncrypter(random); - AsymmetricCipherKeyPair kp = m.generateKeyPair(); - ECPublicKeyParameters pub = (ECPublicKeyParameters) kp.getPublic(); - ECPrivateKeyParameters priv = (ECPrivateKeyParameters) kp.getPrivate(); + KeyPair kp = m.generateKeyPair(); + PublicKey pub = kp.getPublic(); + PrivateKey priv = kp.getPrivate(); byte[] ciphertext = m.encrypt(pub, new byte[123]); ciphertext[random.nextInt(ciphertext.length)] ^= 0xFF; m.decrypt(priv, ciphertext); From 9b5d3ecb7adc4e48c18eae24c12d7d95fa169a5b Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 21 Apr 2016 16:07:51 +0100 Subject: [PATCH 5/5] Added public key and placeholder onion address. --- .../org/briarproject/android/AppModule.java | 32 ++++++------------- .../briarproject/api/reporting/DevConfig.java | 2 -- .../api/reporting/ReportingConstants.java | 25 +++++++++++++++ .../reporting/DevReporterImpl.java | 21 ++++++------ 4 files changed, 44 insertions(+), 36 deletions(-) create mode 100644 briar-api/src/org/briarproject/api/reporting/ReportingConstants.java diff --git a/briar-android/src/org/briarproject/android/AppModule.java b/briar-android/src/org/briarproject/android/AppModule.java index 22d59c206..0b5b031b9 100644 --- a/briar-android/src/org/briarproject/android/AppModule.java +++ b/briar-android/src/org/briarproject/android/AppModule.java @@ -24,6 +24,8 @@ import dagger.Module; import dagger.Provides; import static android.content.Context.MODE_PRIVATE; +import static org.briarproject.api.reporting.ReportingConstants.DEV_ONION_ADDRESS; +import static org.briarproject.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX; @Module public class AppModule { @@ -97,36 +99,22 @@ public class AppModule { @Provides @Singleton - public DevConfig provideDevConfig(CryptoComponent crypto) { - final PublicKey pub; - try { - // TODO fill in - pub = crypto.getMessageKeyParser() - .parsePublicKey(StringUtils.fromHexString("")); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - + public DevConfig provideDevConfig(final CryptoComponent crypto) { return new DevConfig() { - private final PublicKey DEV_PUB_KEY = pub; - // TODO fill in - private final String DEV_ONION = ""; - private final int DEV_REPORT_PORT = 8080; - @Override public PublicKey getDevPublicKey() { - return DEV_PUB_KEY; + try { + return crypto.getMessageKeyParser().parsePublicKey( + StringUtils.fromHexString(DEV_PUBLIC_KEY_HEX)); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } } @Override public String getDevOnionAddress() { - return DEV_ONION; - } - - @Override - public int getDevReportPort() { - return DEV_REPORT_PORT; + return DEV_ONION_ADDRESS; } }; } diff --git a/briar-api/src/org/briarproject/api/reporting/DevConfig.java b/briar-api/src/org/briarproject/api/reporting/DevConfig.java index f82e81318..b6f4ccbf8 100644 --- a/briar-api/src/org/briarproject/api/reporting/DevConfig.java +++ b/briar-api/src/org/briarproject/api/reporting/DevConfig.java @@ -7,6 +7,4 @@ public interface DevConfig { PublicKey getDevPublicKey(); String getDevOnionAddress(); - - int getDevReportPort(); } diff --git a/briar-api/src/org/briarproject/api/reporting/ReportingConstants.java b/briar-api/src/org/briarproject/api/reporting/ReportingConstants.java new file mode 100644 index 000000000..f1d69a1a1 --- /dev/null +++ b/briar-api/src/org/briarproject/api/reporting/ReportingConstants.java @@ -0,0 +1,25 @@ +package org.briarproject.api.reporting; + +public interface ReportingConstants { + + /** + * Public key for reporting crashes and feedback to the developers. This + * is an ECIES key on the brainpoolp512r1 curve, encoded in SEC 1 format + * without point compression. + */ + String DEV_PUBLIC_KEY_HEX = + "0457AD1619FBD433D5E13D5560697054" + + "6E8FC5F4EF83A8C18718E8BF59BB601F" + + "E20CCB233F06714A1BED370141A04C81" + + "808CF2EE95C7323CDEE5999670BD1174" + + "1F65ED691F355518E1A7E5E54BDDCA4C" + + "B86BD8DB8842BBFD706EBD9708DB8C04" + + "4FF006F215D83A66B3AEBAD674C4C1C4" + + "218121A38FA1FDD4A51E77588D90BD9652"; + + /** + * Hidden service address for reporting crashes and feedback to the + * developers. TODO: Replace this with a real address. + */ + String DEV_ONION_ADDRESS = "aaaaaaaaaaaaaaaa.onion"; +} diff --git a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java index d34bbeeef..192f10bee 100644 --- a/briar-core/src/org/briarproject/reporting/DevReporterImpl.java +++ b/briar-core/src/org/briarproject/reporting/DevReporterImpl.java @@ -22,7 +22,6 @@ import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.nio.charset.Charset; -import java.util.Date; import java.util.List; import java.util.logging.Logger; @@ -33,7 +32,7 @@ class DevReporterImpl implements DevReporter { private static final Logger LOG = Logger.getLogger(DevReporterImpl.class.getName()); - private static final int TIMEOUT = 30 * 1000; // 30 seconds + private static final int SOCKET_TIMEOUT = 30 * 1000; // 30 seconds private static final String PREFIX = "briar-"; private static final String REPORT_EXT = ".report"; private static final String CRLF = "\r\n"; @@ -46,13 +45,12 @@ class DevReporterImpl implements DevReporter { this.devConfig = devConfig; } - private Socket connectToDevelopers(int socksPort, int devPort) + private Socket connectToDevelopers(int socksPort) throws UnknownHostException, SocksException, SocketException { Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", socksPort); proxy.resolveAddrLocally(false); - Socket s = - new SocksSocket(proxy, devConfig.getDevOnionAddress(), devPort); - s.setSoTimeout(TIMEOUT); + Socket s = new SocksSocket(proxy, devConfig.getDevOnionAddress(), 80); + s.setSoTimeout(SOCKET_TIMEOUT); return s; } @@ -63,7 +61,7 @@ class DevReporterImpl implements DevReporter { crypto.encryptToKey(devConfig.getDevPublicKey(), StringUtils.toUtf8(crashReport)); - String filename = PREFIX + new Date().getTime() + REPORT_EXT; + String filename = PREFIX + System.currentTimeMillis() + REPORT_EXT; File report = new File(crashReportDir, filename); PrintWriter writer = null; try { @@ -83,14 +81,13 @@ class DevReporterImpl implements DevReporter { if (reports == null || reports.length == 0) return; // No crash reports to send - LOG.info("Connecting to developers' Hidden Service"); + LOG.info("Connecting to developers"); Socket s; try { - s = connectToDevelopers(socksPort, - devConfig.getDevReportPort()); + s = connectToDevelopers(socksPort); } catch (IOException e) { if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, "Tor SOCKS proxy failed", e); + LOG.log(WARNING, "Could not connect to developers", e); return; } @@ -109,9 +106,9 @@ class DevReporterImpl implements DevReporter { writer.append(line).append(CRLF); } writer.append(CRLF); + writer.flush(); f.delete(); } - writer.flush(); LOG.info("Crash reports sent"); } catch (IOException e) { if (LOG.isLoggable(WARNING))