diff --git a/bramble-android/build.gradle b/bramble-android/build.gradle index e1db103c333431ef793cbb2b70a37fc1345c35e8..d2a4b9eb4c0a4e01d5ba0b0e75ce36584a53f830 100644 --- a/bramble-android/build.gradle +++ b/bramble-android/build.gradle @@ -42,8 +42,10 @@ configurations { dependencies { implementation project(path: ':bramble-core', configuration: 'default') + implementation 'androidx.annotation:annotation:1.5.0' tor "org.briarproject:tor-android:$tor_version" tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version" + tor "org.briarproject:snowflake-android:$snowflake_version" annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java index d8933d10174e48be88231ec851573973ed2d0ad3..2acd6705673a5fb6d8b834bf11accff4d6797114 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/AndroidTorPlugin.java @@ -31,6 +31,8 @@ import java.util.zip.ZipInputStream; import javax.net.SocketFactory; +import androidx.annotation.ChecksSdkIntAtLeast; + import static android.os.Build.VERSION.SDK_INT; import static java.util.Arrays.asList; import static java.util.logging.Level.INFO; @@ -45,13 +47,14 @@ class AndroidTorPlugin extends TorPlugin { private static final String TOR_LIB_NAME = "libtor.so"; private static final String OBFS4_LIB_NAME = "libobfs4proxy.so"; + private static final String SNOWFLAKE_LIB_NAME = "libsnowflake.so"; private static final Logger LOG = getLogger(AndroidTorPlugin.class.getName()); private final Application app; private final AndroidWakeLock wakeLock; - private final File torLib, obfs4Lib; + private final File torLib, obfs4Lib, snowflakeLib; AndroidTorPlugin(Executor ioExecutor, Executor wakefulIoExecutor, @@ -83,6 +86,7 @@ class AndroidTorPlugin extends TorPlugin { String nativeLibDir = app.getApplicationInfo().nativeLibraryDir; torLib = new File(nativeLibDir, TOR_LIB_NAME); obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME); + snowflakeLib = new File(nativeLibDir, SNOWFLAKE_LIB_NAME); } @Override @@ -108,6 +112,12 @@ class AndroidTorPlugin extends TorPlugin { if (!enable) wakeLock.release(); } + @Override + @ChecksSdkIntAtLeast(api = 25) + protected boolean canVerifyLetsEncryptCerts() { + return SDK_INT >= 25; + } + @Override public void stop() { super.stop(); @@ -124,39 +134,43 @@ class AndroidTorPlugin extends TorPlugin { return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile(); } + @Override + protected File getSnowflakeExecutableFile() { + return snowflakeLib.exists() + ? snowflakeLib : super.getSnowflakeExecutableFile(); + } + @Override protected void installTorExecutable() throws IOException { - File extracted = super.getTorExecutableFile(); - if (torLib.exists()) { - // If an older version left behind a Tor binary, delete it - if (extracted.exists()) { - if (extracted.delete()) LOG.info("Deleted Tor binary"); - else LOG.info("Failed to delete Tor binary"); - } - } else if (SDK_INT < 29) { - // The binary wasn't extracted at install time. Try to extract it - extractLibraryFromApk(TOR_LIB_NAME, extracted); - } else { - // No point extracting the binary, we won't be allowed to execute it - throw new FileNotFoundException(torLib.getAbsolutePath()); - } + installExecutable(super.getTorExecutableFile(), torLib, TOR_LIB_NAME); } @Override protected void installObfs4Executable() throws IOException { - File extracted = super.getObfs4ExecutableFile(); - if (obfs4Lib.exists()) { - // If an older version left behind an obfs4 binary, delete it + installExecutable(super.getObfs4ExecutableFile(), obfs4Lib, + OBFS4_LIB_NAME); + } + + @Override + protected void installSnowflakeExecutable() throws IOException { + installExecutable(super.getSnowflakeExecutableFile(), snowflakeLib, + SNOWFLAKE_LIB_NAME); + } + + private void installExecutable(File extracted, File lib, String libName) + throws IOException { + if (lib.exists()) { + // If an older version left behind a binary, delete it if (extracted.exists()) { - if (extracted.delete()) LOG.info("Deleted obfs4 binary"); - else LOG.info("Failed to delete obfs4 binary"); + if (extracted.delete()) LOG.info("Deleted old binary"); + else LOG.info("Failed to delete old binary"); } } else if (SDK_INT < 29) { // The binary wasn't extracted at install time. Try to extract it - extractLibraryFromApk(OBFS4_LIB_NAME, extracted); + extractLibraryFromApk(libName, extracted); } else { // No point extracting the binary, we won't be allowed to execute it - throw new FileNotFoundException(obfs4Lib.getAbsolutePath()); + throw new FileNotFoundException(lib.getAbsolutePath()); } } diff --git a/bramble-android/witness.gradle b/bramble-android/witness.gradle index 046a450992d36217ab81f0355d3ee7948a034828..90146f99f759d9f52da10493145a29eea1010365 100644 --- a/bramble-android/witness.gradle +++ b/bramble-android/witness.gradle @@ -1,5 +1,6 @@ dependencyVerification { verify = [ + 'androidx.annotation:annotation:1.5.0:annotation-1.5.0.jar:261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a', 'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db', 'com.android.tools.analytics-library:protos:30.0.3:protos-30.0.3.jar:f62b89dcd9de719c6a7b7e15fb1dd20e45b57222e675cf633607bd0ed6bca7e7', 'com.android.tools.analytics-library:shared:30.0.3:shared-30.0.3.jar:05aa9ba3cc890354108521fdf99802565aae5dd6ca44a6ac8bb8d594d1c1cd15', @@ -88,6 +89,7 @@ dependencyVerification { 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.briarproject:obfs4proxy-android:0.0.14:obfs4proxy-android-0.0.14.jar:ad9b1ee4757b05867a19e993147bbb018bddd1f26ce3da746d5f037d5991a8c8', + 'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293', 'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', @@ -129,10 +131,12 @@ dependencyVerification { 'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32:kotlin-stdlib-common-1.4.32.jar:e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145', + 'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10:kotlin-stdlib-common-1.7.10.jar:19f102efe9629f8eabc63853ad15c533e47c47f91fca09285c5bde86e59f91d4', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262', 'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656', 'org.jetbrains.kotlin:kotlin-stdlib:1.4.32:kotlin-stdlib-1.4.32.jar:13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba', + 'org.jetbrains.kotlin:kotlin-stdlib:1.7.10:kotlin-stdlib-1.7.10.jar:e771fe74250a943e8f6346713201ff1d8cb95c3a5d1a91a22b65a9e04f6a8901', 'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09', diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java index ff5033bf1d3c404752afb3bc49636a0ebd2ab3ad..7e1c4c83bbb475aff8e96eb8aced66487d5c36ee 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java @@ -12,7 +12,8 @@ public interface CircumventionProvider { DEFAULT_OBFS4, NON_DEFAULT_OBFS4, VANILLA, - MEEK + MEEK, + SNOWFLAKE } /** @@ -41,13 +42,14 @@ public interface CircumventionProvider { * Countries where non-default obfs4 or vanilla bridges are likely to work. * Should be a subset of {@link #BRIDGES}. */ - String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"}; + String[] NON_DEFAULT_BRIDGES = {"BY", "RU"}; /** * Countries where vanilla bridges are blocked via DPI but non-default - * obfs4 bridges and meek may work. Should be a subset of {@link #BRIDGES}. + * obfs4 bridges, meek and snowflake may work. Should be a subset of + * {@link #BRIDGES}. */ - String[] DPI_BRIDGES = {"CN", "IR"}; + String[] DPI_BRIDGES = {"CN", "IR", "TM"}; /** * Returns true if vanilla Tor connections are blocked in the given country. @@ -68,6 +70,6 @@ public interface CircumventionProvider { List<BridgeType> getSuitableBridgeTypes(String countryCode); @IoExecutor - List<String> getBridges(BridgeType type); - + List<String> getBridges(BridgeType type, String countryCode, + boolean letsEncrypt); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java index 461ce5c31e5f4ff1a8b884bfe2dd0355a07dcb18..ac7d77af67438e5803a1fb9516574d9bdfb6e6c9 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java @@ -7,8 +7,10 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Scanner; import java.util.Set; +import java.util.TreeMap; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; @@ -18,6 +20,7 @@ import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA; @Immutable @@ -25,6 +28,8 @@ import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeTy class CircumventionProviderImpl implements CircumventionProvider { private final static String BRIDGE_FILE_NAME = "bridges"; + private final static String SNOWFLAKE_PARAMS_FILE_NAME = "snowflake-params"; + private final static String DEFAULT_COUNTRY_CODE = "ZZ"; private static final Set<String> BLOCKED_IN_COUNTRIES = new HashSet<>(asList(BLOCKED)); @@ -58,7 +63,7 @@ class CircumventionProviderImpl implements CircumventionProvider { } else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) { return asList(NON_DEFAULT_OBFS4, VANILLA); } else if (DPI_COUNTRIES.contains(countryCode)) { - return asList(NON_DEFAULT_OBFS4, MEEK); + return asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE); } else { return asList(DEFAULT_OBFS4, VANILLA); } @@ -66,7 +71,8 @@ class CircumventionProviderImpl implements CircumventionProvider { @Override @IoExecutor - public List<String> getBridges(BridgeType type) { + public List<String> getBridges(BridgeType type, String countryCode, + boolean letsEncrypt) { InputStream is = requireNonNull(getClass().getClassLoader() .getResourceAsStream(BRIDGE_FILE_NAME)); Scanner scanner = new Scanner(is); @@ -79,10 +85,43 @@ class CircumventionProviderImpl implements CircumventionProvider { (type == VANILLA && line.startsWith("v ")) || (type == MEEK && line.startsWith("m "))) { bridges.add(line.substring(2)); + } else if (type == SNOWFLAKE && line.startsWith("s ")) { + String params = getSnowflakeParams(countryCode, letsEncrypt); + bridges.add(line.substring(2) + " " + params); } } scanner.close(); return bridges; } + private String getSnowflakeParams(String countryCode, boolean letsEncrypt) { + Map<String, String> params = loadSnowflakeParams(); + if (countryCode.isEmpty()) countryCode = DEFAULT_COUNTRY_CODE; + // If we have parameters for this country code, return them + String value = params.get(makeKey(countryCode, letsEncrypt)); + if (value != null) return value; + // Return the default parameters + value = params.get(makeKey(DEFAULT_COUNTRY_CODE, letsEncrypt)); + return requireNonNull(value); + } + + private Map<String, String> loadSnowflakeParams() { + InputStream is = requireNonNull(getClass().getClassLoader() + .getResourceAsStream(SNOWFLAKE_PARAMS_FILE_NAME)); + Scanner scanner = new Scanner(is); + Map<String, String> params = new TreeMap<>(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.length() < 5) continue; + String key = line.substring(0, 4); // Country code, space, digit + String value = line.substring(5); + params.put(key, value); + } + scanner.close(); + return params; + } + + private String makeKey(String countryCode, boolean oldAndroid) { + return countryCode + " " + (oldAndroid ? "1" : "0"); + } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 1b51547a9c7317cc4d3a941aacc03cc4dccc721c..792cf43d5303b39138db2fdebe9f28956dbbfeeb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -95,6 +95,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.tryToClose; @@ -212,6 +213,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { return new File(torDirectory, "obfs4proxy"); } + protected File getSnowflakeExecutableFile() { + return new File(torDirectory, "snowflake"); + } + @Override public TransportId getId() { return TorConstants.ID; @@ -338,6 +343,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { geoIpFile.delete(); installTorExecutable(); installObfs4Executable(); + installSnowflakeExecutable(); if (!doneFile.createNewFile()) LOG.warning("Failed to create done file"); } @@ -363,17 +369,29 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (!obfs4File.setExecutable(true, true)) throw new IOException(); } + protected void installSnowflakeExecutable() throws IOException { + if (LOG.isLoggable(INFO)) + LOG.info("Installing snowflake binary for " + architecture); + File snowflakeFile = getSnowflakeExecutableFile(); + extract(getSnowflakeInputStream(), snowflakeFile); + if (!snowflakeFile.setExecutable(true, true)) throw new IOException(); + } + private InputStream getTorInputStream() throws IOException { - InputStream in = resourceProvider - .getResourceInputStream("tor_" + architecture, ".zip"); - ZipInputStream zin = new ZipInputStream(in); - if (zin.getNextEntry() == null) throw new IOException(); - return zin; + return getZipInputStream("tor"); } private InputStream getObfs4InputStream() throws IOException { + return getZipInputStream("obfs4proxy"); + } + + private InputStream getSnowflakeInputStream() throws IOException { + return getZipInputStream("snowflake"); + } + + private InputStream getZipInputStream(String basename) throws IOException { InputStream in = resourceProvider - .getResourceInputStream("obfs4proxy_" + architecture, ".zip"); + .getResourceInputStream(basename + "_" + architecture, ".zip"); ZipInputStream zin = new ZipInputStream(in); if (zin.getNextEntry() == null) throw new IOException(); return zin; @@ -402,6 +420,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { String obfs4Path = getObfs4ExecutableFile().getAbsolutePath(); append(strb, "ClientTransportPlugin obfs4 exec", obfs4Path); append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path); + String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath(); + append(strb, "ClientTransportPlugin snowflake exec", snowflakePath); //noinspection CharsetObjectCanBeUsed return new ByteArrayInputStream( strb.toString().getBytes(Charset.forName("UTF-8"))); @@ -559,7 +579,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } - private void enableBridges(List<BridgeType> bridgeTypes) + private void enableBridges(List<BridgeType> bridgeTypes, String countryCode) throws IOException { if (!state.setBridgeTypes(bridgeTypes)) return; // Unchanged try { @@ -569,8 +589,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } else { Collection<String> conf = new ArrayList<>(); conf.add("UseBridges 1"); + boolean letsEncrypt = canVerifyLetsEncryptCerts(); for (BridgeType bridgeType : bridgeTypes) { - conf.addAll(circumventionProvider.getBridges(bridgeType)); + conf.addAll(circumventionProvider + .getBridges(bridgeType, countryCode, letsEncrypt)); } controlConnection.setConf(conf); } @@ -579,6 +601,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } } + /** + * Returns true if this device can verify Let's Encrypt certificates signed + * with the IdentTrust DST Root X3 certificate, which expired at the end of + * September 2021. + */ + protected boolean canVerifyLetsEncryptCerts() { + return true; + } + @Override public void stop() { ServerSocket ss = state.setStopped(); @@ -944,7 +975,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (network == PREF_TOR_NETWORK_WITH_BRIDGES || (automatic && bridgesWork)) { if (ipv6Only) { - bridgeTypes = singletonList(MEEK); + bridgeTypes = asList(MEEK, SNOWFLAKE); } else { bridgeTypes = circumventionProvider .getSuitableBridgeTypes(country); @@ -968,7 +999,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { try { if (enableNetwork) { - enableBridges(bridgeTypes); + enableBridges(bridgeTypes, country); enableConnectionPadding(enableConnectionPadding); enableIpv6(ipv6Only); } diff --git a/bramble-core/src/main/resources/bridges b/bramble-core/src/main/resources/bridges index 1123d36294d75a943804ebdb844941431d7f9470..458165af7f1b4ab4fd1bfda8f8f3d43eb7ab6502 100644 --- a/bramble-core/src/main/resources/bridges +++ b/bramble-core/src/main/resources/bridges @@ -33,3 +33,4 @@ v Bridge 185.189.195.124:8199 A1F3EE78F9C2343668E68FEB84358A4C742831A5 v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB v Bridge 75.100.92.154:22815 465E990FA8A752DDE861EDF31E42454ACC037AB4 m Bridge meek_lite 192.0.2.2:80 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com +s Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 diff --git a/bramble-core/src/main/resources/snowflake-params b/bramble-core/src/main/resources/snowflake-params new file mode 100644 index 0000000000000000000000000000000000000000..9192ec246dd461879be9725badbd282ba594a7d8 --- /dev/null +++ b/bramble-core/src/main/resources/snowflake-params @@ -0,0 +1,4 @@ +ZZ 1 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 +ZZ 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 +TM 1 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 +TM 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479 diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java index 7dd380d68f6c3965a5e1090323e92853e8f3ce00..310ec6eb0074a770e3685777f3359c0d31d17df9 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/tor/CircumventionProviderTest.java @@ -12,6 +12,7 @@ import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DPI_BRIDGES; @@ -56,7 +57,7 @@ public class CircumventionProviderTest extends BrambleTestCase { provider.getSuitableBridgeTypes(country)); } for (String country : DPI_BRIDGES) { - assertEquals(asList(NON_DEFAULT_OBFS4, MEEK), + assertEquals(asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE), provider.getSuitableBridgeTypes(country)); } assertEquals(asList(DEFAULT_OBFS4, VANILLA), diff --git a/bramble-java/build.gradle b/bramble-java/build.gradle index fb3414d7df74adbfffeedbb4fedf52210cd4aacb..82b11c4a4b278fafbaa37c1bb26ea80dda013b1b 100644 --- a/bramble-java/build.gradle +++ b/bramble-java/build.gradle @@ -21,6 +21,8 @@ dependencies { tor "org.briarproject:tor-windows:$tor_version" tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version" tor "org.briarproject:obfs4proxy-windows:$obfs4proxy_version" + tor "org.briarproject:snowflake-linux:$snowflake_version" + tor "org.briarproject:snowflake-windows:$snowflake_version" annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" diff --git a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java index 79818a3498d258cd47d73833e250de98b2335013..54575469cac2af18290388260c3cf519a54fa817 100644 --- a/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java +++ b/bramble-java/src/test/java/org/briarproject/bramble/plugin/tor/BridgeTest.java @@ -47,6 +47,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; +import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE; import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; @@ -58,6 +59,8 @@ import static org.junit.Assume.assumeTrue; @RunWith(Parameterized.class) public class BridgeTest extends BrambleTestCase { + private static final String[] SNOWFLAKE_COUNTRY_CODES = {"TM", "ZZ"}; + @Parameters public static Iterable<Params> data() { BrambleJavaIntegrationTestComponent component = @@ -69,23 +72,36 @@ public class BridgeTest extends BrambleTestCase { CircumventionProvider provider = component.getCircumventionProvider(); List<Params> states = new ArrayList<>(); for (int i = 0; i < ATTEMPTS_PER_BRIDGE; i++) { - for (String bridge : provider.getBridges(DEFAULT_OBFS4)) { + for (String bridge : + provider.getBridges(DEFAULT_OBFS4, "", true)) { states.add(new Params(bridge, DEFAULT_OBFS4, stats, false)); } - for (String bridge : provider.getBridges(NON_DEFAULT_OBFS4)) { - states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats, false)); + for (String bridge : + provider.getBridges(NON_DEFAULT_OBFS4, "", true)) { + states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats, + false)); } - for (String bridge : provider.getBridges(VANILLA)) { + for (String bridge : provider.getBridges(VANILLA, "", true)) { states.add(new Params(bridge, VANILLA, stats, false)); } - for (String bridge : provider.getBridges(MEEK)) { + for (String bridge : provider.getBridges(MEEK, "", true)) { states.add(new Params(bridge, MEEK, stats, true)); } + for (String countryCode : SNOWFLAKE_COUNTRY_CODES) { + for (String bridge : + provider.getBridges(SNOWFLAKE, countryCode, true)) { + states.add(new Params(bridge, SNOWFLAKE, stats, true)); + } + for (String bridge : + provider.getBridges(SNOWFLAKE, countryCode, false)) { + states.add(new Params(bridge, SNOWFLAKE, stats, true)); + } + } } return states; } - private final static long OBFS4_TIMEOUT = MINUTES.toMillis(2); + private final static long TIMEOUT = MINUTES.toMillis(2); private final static long MEEK_TIMEOUT = MINUTES.toMillis(6); private final static int UNREACHABLE_BRIDGES_ALLOWED = 6; private final static int ATTEMPTS_PER_BRIDGE = 5; @@ -163,7 +179,8 @@ public class BridgeTest extends BrambleTestCase { } @Override - public List<String> getBridges(BridgeType bridgeType) { + public List<String> getBridges(BridgeType bridgeType, + String countryCode, boolean letsEncrypt) { return singletonList(params.bridge); } }; @@ -190,8 +207,7 @@ public class BridgeTest extends BrambleTestCase { try { plugin.start(); long start = clock.currentTimeMillis(); - long timeout = params.bridgeType == MEEK - ? MEEK_TIMEOUT : OBFS4_TIMEOUT; + long timeout = params.bridgeType == MEEK ? MEEK_TIMEOUT : TIMEOUT; while (clock.currentTimeMillis() - start < timeout) { if (plugin.getState() == ACTIVE) return; clock.sleep(500); diff --git a/bramble-java/witness.gradle b/bramble-java/witness.gradle index 97577bb1a8acf9f1adf18dfed6d3120be9827c39..b9cb4a8126c8423fec83e145941cbf673d2c2368 100644 --- a/bramble-java/witness.gradle +++ b/bramble-java/witness.gradle @@ -26,6 +26,8 @@ dependencyVerification { 'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', 'org.briarproject:obfs4proxy-linux:0.0.14:obfs4proxy-linux-0.0.14.jar:6391d323d45a279362236c7c62e21b903d07d4f31f5e0c8d49d009769b720cc6', 'org.briarproject:obfs4proxy-windows:0.0.14:obfs4proxy-windows-0.0.14.jar:801d48525f52583a470a1671026b87992176d4432b299774989387cb87bc8ba3', + 'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3', + 'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a', 'org.briarproject:tor-linux:0.4.5.14:tor-linux-0.4.5.14.jar:1844e54cf6df0c85cec219381a3364c759ae444a6b63f7558b757becb7d41d08', 'org.briarproject:tor-windows:0.4.5.14:tor-windows-0.4.5.14.jar:d337afa1043f0cfa7e6e8c2473d682a5663a2c8052bb97a770450893c78c9b4f', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle index 3893f1f72b2a64dca56b2a3e429497f3707513a2..d2a4e23194ea45d9fb6c41c350c25a2596157996 100644 --- a/briar-android/witness.gradle +++ b/briar-android/witness.gradle @@ -4,7 +4,7 @@ dependencyVerification { 'androidx.activity:activity:1.2.2:activity-1.2.2.aar:e165fb20f006b77894d349572cc3acd2760baa8416ae4d33cb8de6a84dd6730c', 'androidx.activity:activity:1.2.4:activity-1.2.4.aar:ae8e9c7de57e387d2ad90e73f3a5a5dfd502bd4f034c1dccfdb3506d1d2df81a', 'androidx.annotation:annotation-experimental:1.0.0:annotation-experimental-1.0.0.aar:b219d2b568e7e4ba534e09f8c2fd242343df6ccbdfbbe938846f5d740e6b0b11', - 'androidx.annotation:annotation:1.1.0:annotation-1.1.0.jar:d38d63edb30f1467818d50aaf05f8a692dea8b31392a049bfa991b159ad5b692', + 'androidx.annotation:annotation:1.5.0:annotation-1.5.0.jar:261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a', 'androidx.appcompat:appcompat-resources:1.2.0:appcompat-resources-1.2.0.aar:c470297c03ff3de1c3d15dacf0be0cae63abc10b52f021dd07ae28daa3100fe5', 'androidx.appcompat:appcompat:1.2.0:appcompat-1.2.0.aar:3d2131a55a61a777322e2126e0018011efa6339e53b44153eb651b16020cca70', 'androidx.arch.core:core-common:2.1.0:core-common-2.1.0.jar:fe1237bf029d063e7f29fe39aeaf73ef74c8b0a3658486fc29d3c54326653889', @@ -227,13 +227,13 @@ dependencyVerification { 'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32:kotlin-stdlib-common-1.4.32.jar:e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145', - 'org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20:kotlin-stdlib-common-1.6.20.jar:8da40a2520d30dcb1012176fe93d24e82d08a3e346c37e0343b0fb6f64f6be01', + 'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10:kotlin-stdlib-common-1.7.10.jar:19f102efe9629f8eabc63853ad15c533e47c47f91fca09285c5bde86e59f91d4', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.20:kotlin-stdlib-jdk7-1.6.20.jar:aa2fa2e81355c4d98dd97da2169bf401f842261378f5b1cbea1aa11855d67620', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262', 'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656', 'org.jetbrains.kotlin:kotlin-stdlib:1.4.32:kotlin-stdlib-1.4.32.jar:13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba', - 'org.jetbrains.kotlin:kotlin-stdlib:1.6.20:kotlin-stdlib-1.6.20.jar:eeb51c2b67b26233fd81d0bc4f8044ec849718890905763ceffd84a31e2cb799', + 'org.jetbrains.kotlin:kotlin-stdlib:1.7.10:kotlin-stdlib-1.7.10.jar:e771fe74250a943e8f6346713201ff1d8cb95c3a5d1a91a22b65a9e04f6a8901', 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1:kotlinx-coroutines-android-1.4.1.jar:d4cadb673b2101f1ee5fbc147956ac78b1cfd9cc255fb53d3aeb88dff11d99ca', 'org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.4.1:kotlinx-coroutines-core-jvm-1.4.1.jar:6d2f87764b6638f27aff12ed380db4b63c9d46ba55dc32683a650598fa5a3e22', 'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5', diff --git a/build.gradle b/build.gradle index 5d8a71affb3d56d8a2c367999ee9d052cdfbd2fc..f0de0c7809488bbe48c6df2c6fd774ee674e0480 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,7 @@ buildscript { jackson_version = "2.13.0" tor_version = "0.4.5.14" obfs4proxy_version = "0.0.14" + snowflake_version = "2.3.1" junit_version = "4.13.2" jmock_version = '2.12.0' }