Compare commits

..

2 Commits

Author SHA1 Message Date
Sebastian Kürten
f5728b3d63 Make briar usable as submodule 2021-10-14 09:18:23 +02:00
Sebastian Kürten
be6a9546ea Provide Tor porst dynamically instead of using hard-coded values 59050 and 59051 2021-10-14 09:03:22 +02:00
261 changed files with 1670 additions and 9103 deletions

View File

@@ -32,9 +32,8 @@ test:
extends: .base-test
stage: test
script:
- ./gradlew -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew -Djava.security.egd=file:/dev/urandom assembleOfficialDebug :briar-headless:linuxJars
- ./gradlew -Djava.security.egd=file:/dev/urandom compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources check
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources check
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: always
@@ -62,7 +61,7 @@ android test:
when: on_failure
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: manual
when: on_success
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
changes:
- briar-android/**/*
@@ -85,42 +84,35 @@ test_reproducible:
.optional_tests:
stage: optional_tests
extends: .base-test
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .gradle/wrapper
- .gradle/caches
script:
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
after_script:
# these file change every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
bridge test:
extends: .optional_tests
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: on_success
allow_failure: false
allow_failure: true
- if: '$CI_COMMIT_TAG == null'
when: manual
allow_failure: true
script:
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
mailbox integration test:
extends: .optional_tests
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: on_success
- if: '$CI_COMMIT_TAG == null'
when: manual
allow_failure: true # TODO figure out how not to allow failure while leaving this optional
script:
# start mailbox
- cd /opt && git clone --depth 1 https://code.briarproject.org/briar/briar-mailbox.git briar-mailbox
- cd briar-mailbox
- mkdir -p /root/.local/share # create directory that mailbox (currently) expects to exist
- ./gradlew run --args="--debug --setup-token 54686973206973206120736574757020746f6b656e20666f722042726961722e" &
# run mailbox integration test once mailbox has started
- cd "$CI_PROJECT_DIR"
- bramble-core/src/test/bash/wait-for-mailbox.sh
- OPTIONAL_TESTS=org.briarproject.bramble.mailbox.MailboxIntegrationTest ./gradlew --info bramble-core:test --tests MailboxIntegrationTest
pre_release_tests:
extends: .optional_tests
script:
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
only:
- tags

View File

@@ -1,32 +1,21 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":briar-android:testOfficialDebugUnitTest" />
<option value=":briar-android:testScreenshotDebugUnitTest" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.briar-android" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/briar-android" />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-api" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-headless" run_configuration_type="GradleRunConfiguration" />
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-api" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-headless" run_configuration_type="AndroidJUnit" />
</method>
</configuration>
</component>

View File

@@ -1,23 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-android" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":bramble-android:testDebugUnitTest" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.bramble-android" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,25 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-api" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":bramble-api:animalSnifferMain" />
<option value=":bramble-api:animalSnifferTest" />
<option value=":bramble-api:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.bramble-api" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,25 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-core" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":bramble-core:animalSnifferMain" />
<option value=":bramble-core:animalSnifferTest" />
<option value=":bramble-core:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.bramble-core" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,23 +1,15 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-java" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":bramble-java:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.bramble-java" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,24 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-android" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":briar-android:testOfficialDebugUnitTest" />
<option value=":briar-android:testScreenshotDebugUnitTest" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.briar-android" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,25 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-api" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":briar-api:animalSnifferMain" />
<option value=":briar-api:animalSnifferTest" />
<option value=":briar-api:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in briar-api" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.briar-api" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-api" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,25 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-core" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":briar-core:animalSnifferMain" />
<option value=":briar-core:animalSnifferTest" />
<option value=":briar-core:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.briar-core" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,23 +1,15 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-headless" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":briar-headless:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="All tests in briar-headless" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.briar-headless" />
<option name="PACKAGE_NAME" value="org.briarproject.briar.headless" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/briar-headless" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,28 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="BridgeTest" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="env">
<map>
<entry key="OPTIONAL_TESTS" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
</map>
</option>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="--tests &quot;org.briarproject.bramble.plugin.tor.BridgeTest&quot;" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value=":bramble-java:test" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
<configuration default="false" name="BridgeTest" type="AndroidJUnit" factoryName="Android JUnit" nameIsGenerated="true">
<module name="briar.bramble-java" />
<useClassPathOnly />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.briarproject.bramble.plugin.tor.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="org.briarproject.bramble.plugin.tor" />
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<envs>
<env name="OPTIONAL_TESTS" value="org.briarproject.bramble.plugin.tor.BridgeTest" />
</envs>
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.bramble-core" />
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.H2DatabasePerformanceTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<module name="briar.bramble-core" />
<option name="PACKAGE_NAME" value="org.briarproject.bramble.db" />
<option name="MAIN_CLASS_NAME" value="org.briarproject.bramble.db.HyperSqlDatabasePerformanceTest" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -22,15 +22,6 @@ our site.
[Wiki](https://code.briarproject.org/briar/briar/-/wikis/home)
## Reproducible builds
We provide [docker images](https://code.briarproject.org/briar/briar-reproducer#briar-reproducer)
to ease the task of verifying that the published APK binaries
include nothing but our publicly available source code.
You can either use those images or use them as a blueprint to build your own environment
for reproduction.
## Donate
[![Donate using Liberapay](https://briarproject.org/img/liberapay.svg)](https://liberapay.com/Briar/donate) [![Flattr this](https://briarproject.org/img/flattr-badge-large.png "Flattr this")](https://flattr.com/t/592836/)

View File

@@ -15,8 +15,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
versionCode 10405
versionName "1.4.5"
versionCode 10308
versionName "1.3.8"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -41,19 +41,19 @@ configurations {
}
dependencies {
implementation project(path: ':bramble-core', configuration: 'default')
tor "org.briarproject:tor-android:$tor_version"
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version@zip"
implementation project(path: ':briar:bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.15'
tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip'
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
compileOnly 'javax.annotation:jsr250-api:1.0'
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':briar:bramble-api', configuration: 'testOutput')
testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-imposters:$jmock_version"
testImplementation "org.jmock:jmock-legacy:$jmock_version"
}
def torBinariesDir = 'src/main/res/raw'

View File

@@ -1,8 +1,6 @@
# Keep the H2 classes that are loaded via reflection
-keep class org.h2.Driver { *; }
-keep class org.h2.engine.Engine { *; }
-keep class org.h2.store.fs.** { *; }
# Don't warn about unused dependencies of H2 classes
-keep,includedescriptorclasses class org.briarproject.** { *; }
-keep class org.h2.** { *; }
-dontwarn org.h2.**
-dontnote org.h2.**
@@ -17,4 +15,5 @@
-dontwarn sun.misc.Unsafe
-dontnote com.google.common.**
-dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl
# UPnP library isn't used
-dontwarn org.bitlet.weupnp.**

View File

@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.plugin.TorPorts;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.File;
@@ -59,6 +60,7 @@ class AndroidTorPlugin extends TorPlugin {
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
TorPorts torPorts,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
@@ -70,14 +72,12 @@ class AndroidTorPlugin extends TorPlugin {
String architecture,
long maxLatency,
int maxIdleTime,
File torDirectory,
int torSocksPort,
int torControlPort) {
File torDirectory) {
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
torSocketFactory, torPorts, clock, resourceProvider,
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture, maxLatency,
maxIdleTime, torDirectory, torSocksPort, torControlPort);
maxIdleTime, torDirectory);
this.app = app;
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
import android.app.Application;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager;
@@ -12,9 +11,7 @@ import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorControlPort;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TorSocksPort;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
@@ -23,6 +20,7 @@ import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.plugin.TorPorts;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.File;
@@ -52,6 +50,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final LocationUtils locationUtils;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final TorPorts torPorts;
private final BackoffFactory backoffFactory;
private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider;
@@ -59,9 +58,6 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final AndroidWakeLockManager wakeLockManager;
private final Clock clock;
private final File torDirectory;
private int torSocksPort;
private int torControlPort;
private final CryptoComponent crypto;
@Inject
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
@@ -71,16 +67,14 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
TorPorts torPorts,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
AndroidWakeLockManager wakeLockManager,
Clock clock,
@TorDirectory File torDirectory,
@TorSocksPort int torSocksPort,
@TorControlPort int torControlPort,
CryptoComponent crypto) {
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.app = app;
@@ -88,6 +82,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
this.locationUtils = locationUtils;
this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory;
this.torPorts = torPorts;
this.backoffFactory = backoffFactory;
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
@@ -95,9 +90,6 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
this.wakeLockManager = wakeLockManager;
this.clock = clock;
this.torDirectory = torDirectory;
this.torSocksPort = torSocksPort;
this.torControlPort = torControlPort;
this.crypto = crypto;
}
@Override
@@ -139,15 +131,13 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto =
new TorRendezvousCryptoImpl(crypto);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
wakefulIoExecutor, app, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
torSocketFactory, torPorts, clock, resourceProvider,
circumventionProvider, batteryManager, wakeLockManager,
backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
torControlPort);
MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -44,7 +44,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
private AndroidAccountManager accountManager;
public AndroidAccountManagerTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
context.setImposteriser(ClassImposteriser.INSTANCE);
app = context.mock(Application.class);
applicationInfo = new ApplicationInfo();
applicationInfo.dataDir = testDir.getAbsolutePath();

View File

@@ -1,50 +1,50 @@
dependencyVerification {
verify = [
'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',
'com.android.tools.analytics-library:tracker:30.0.3:tracker-30.0.3.jar:5d0ef35bf6733e96210b5085a2a202152921bf834d345959dce1ca3369b528df',
'com.android.tools.analytics-library:protos:27.1.3:protos-27.1.3.jar:0d9e6cff60b318baac250b6f5bb076a8161103338bf2749cdf1db8a5a13a1f12',
'com.android.tools.analytics-library:shared:27.1.3:shared-27.1.3.jar:10d2a51d8f89ff4ac849888e5a9c60b10e879c30d78545ec1da4d3df7bd56ae4',
'com.android.tools.analytics-library:tracker:27.1.3:tracker-27.1.3.jar:589b355a2ba796cbc0a2b2295737de6661f078262e5f87cd6f540b8d011e5ebb',
'com.android.tools.build:aapt2-proto:4.1.0-alpha01-6193524:aapt2-proto-4.1.0-alpha01-6193524.jar:17e75523e1e92dd4f222c7368ee41df9e964a508232f591e265d0c499baf9dca',
'com.android.tools.build:apksig:7.0.3:apksig-7.0.3.jar:012337a2803c9a30dfc41dcbc6450686ee9e5f582549f7f126479f743a343ec9',
'com.android.tools.build:apkzlib:7.0.3:apkzlib-7.0.3.jar:b31e53174c92db83c5cc6e7dac6734ea4e907a72e452c2bf1818dfd082c59397',
'com.android.tools.build:builder-model:7.0.3:builder-model-7.0.3.jar:483f99d7494a5bed027e1e8d29111384cf535d4842f0be5a79805bd44bb68d4e',
'com.android.tools.build:builder-test-api:7.0.3:builder-test-api-7.0.3.jar:f6de4bc2cef545e8367bf82d7c733829c7be3b0b3b8b09fd8c58f2150e59ab46',
'com.android.tools.build:builder:7.0.3:builder-7.0.3.jar:c6952da0094b094c2ba0fe84c675622097c5d9b9f9beb53485b860320540cf1d',
'com.android.tools.build:manifest-merger:30.0.3:manifest-merger-30.0.3.jar:72b346ba6318b4b6260e6e49df4bea5da2e12329ab6c2beb2269c49a9f51f178',
'com.android.tools.ddms:ddmlib:30.0.3:ddmlib-30.0.3.jar:7a914a68ab93393657297234e2f37b22410ae9a433cba692ce8c727c9607e3bb',
'com.android.tools.external.com-intellij:intellij-core:30.0.3:intellij-core-30.0.3.jar:1ebe858d3f58eeaa8c06507f8ac0f1c7051e6c61f35a70f3c3967d5734d3abc5',
'com.android.tools.external.com-intellij:kotlin-compiler:30.0.3:kotlin-compiler-30.0.3.jar:ed00e441f427cb4e0d418287b9da30b12b7f735f9af32e6b5d3dc960b6a742fc',
'com.android.tools.external.org-jetbrains:uast:30.0.3:uast-30.0.3.jar:a77801bee6ff509910e459525c9c34d7f04b066ade123547f16f1917548eadea',
'com.android.tools.layoutlib:layoutlib-api:30.0.3:layoutlib-api-30.0.3.jar:4caa87e9ca2e11315f650d576cd59fec1793373bc3fca3f6d53c029e7534e7c4',
'com.android.tools.lint:lint-api:30.0.3:lint-api-30.0.3.jar:bcecbd2f752a6560096a9029a47d1de6bd788a51bab505c5ebfba6a18524b983',
'com.android.tools.lint:lint-checks:30.0.3:lint-checks-30.0.3.jar:25a7cd42dc3ad502337f131fb8b7e873c53301db0a67b1c64dd4ae7a8eb66cec',
'com.android.tools.lint:lint-gradle:30.0.3:lint-gradle-30.0.3.jar:94544d6147a809bf2fd3440e51f28a4e42e547d74aab53eefd74938cdad42c26',
'com.android.tools.lint:lint-model:30.0.3:lint-model-30.0.3.jar:0b940a7f575c2ff5cbd038260f41dde686a93c672213881ead3ce8af3513b396',
'com.android.tools.lint:lint:30.0.3:lint-30.0.3.jar:ee4f11001e0c7e3b776e0d67399ad354b19b0f168822ec2b7db47c0910ed227d',
'com.android.tools:annotations:30.0.3:annotations-30.0.3.jar:5c1944982fda8555855c4f5422fabf0dc8e2306e1f5460e9ad82dae71316bc31',
'com.android.tools:common:30.0.3:common-30.0.3.jar:8751efaaf2c2ddd1f0a37526c794347def6a3057ca9fc510307c13a6cf0d036f',
'com.android.tools:dvlib:30.0.3:dvlib-30.0.3.jar:5affafcec390041e5afd64cb924153f5e474db47ee8ccc2f555b495083141233',
'com.android.tools:repository:30.0.3:repository-30.0.3.jar:0a40c6f16c506903ce2c609affd8228aceda73a69d93dfa42d4f02b8491449f6',
'com.android.tools:sdk-common:30.0.3:sdk-common-30.0.3.jar:b45570a380360236ffee0f6bb593d66b673bad3834dfe0d6c9871fa7188ee0eb',
'com.android.tools:sdklib:30.0.3:sdklib-30.0.3.jar:7088f20a414fab170a21e457825e14ebe099f753558e02c8acc12c67eb412162',
'com.android:signflinger:7.0.3:signflinger-7.0.3.jar:903a4536db3e96b4e1e1dc1e400eb0b91bf7866d9b39cd7ec94d75dde158f152',
'com.android:zipflinger:7.0.3:zipflinger-7.0.3.jar:fd209c960a3eff7a339e6fcba07d5e9ef4604d1633c69ab2df987460d9804140',
'com.beust:jcommander:1.78:jcommander-1.78.jar:7891debb84b5f83e9bd57593ebece3399abbe0fd938cf306b3534c57913b9615',
'com.github.javaparser:javaparser-core:3.17.0:javaparser-core-3.17.0.jar:23f5c982e1c7771423d37d52c774e8d2e80fd7ea7305ebe448797a96f67e6fca',
'com.android.tools.build:apksig:4.1.3:apksig-4.1.3.jar:a851980c678ff7a6785388b9a9e8cc094788ce3c4a985ad2b19c2028fd3c631a',
'com.android.tools.build:apkzlib:4.1.3:apkzlib-4.1.3.jar:475903065e7e83a8c1ba78d267c97a54dc5a04d768b535093850423d7b11f2c8',
'com.android.tools.build:builder-model:4.1.3:builder-model-4.1.3.jar:2624a1436c3ab39dd91d3ecf9409a594b0f89ea5cab255f2e9ff11f5ee03d274',
'com.android.tools.build:builder-test-api:4.1.3:builder-test-api-4.1.3.jar:3d2af66726b06b53b8d6d497efcee39ff9f77eb2f8d2cce38b31502383a40d2c',
'com.android.tools.build:builder:4.1.3:builder-4.1.3.jar:a40426cd6d68f6a722ef4950058c075e4547025e8c2fd78e732ad89f15176f84',
'com.android.tools.build:gradle-api:4.1.3:gradle-api-4.1.3.jar:11b1fb9de658bdcf9290b1c1517060d0c4d93f2b27975934989ca4ac890bc077',
'com.android.tools.build:manifest-merger:27.1.3:manifest-merger-27.1.3.jar:ce8d4009b1f1584777a7ffa1da3b0551dc316bc8e08112e442c352af70f46f2d',
'com.android.tools.ddms:ddmlib:27.1.3:ddmlib-27.1.3.jar:8f76e8236d2b9eebf26378746dad025c4c7c056a02e133dae4ddef47b283c710',
'com.android.tools.external.com-intellij:intellij-core:27.1.3:intellij-core-27.1.3.jar:652814fa099b4746fb6f10e19718e476952e8b5bac24e17d914f90650ad21808',
'com.android.tools.external.com-intellij:kotlin-compiler:27.1.3:kotlin-compiler-27.1.3.jar:8d7a78d5efd213c5e467e42bd205582aad73ffc77ee5dc18eb1361c9af72f125',
'com.android.tools.external.org-jetbrains:uast:27.1.3:uast-27.1.3.jar:aea53944a1ac6a05f12297b55290e8cbecfe54c4166260cfba4405823bfe1c78',
'com.android.tools.layoutlib:layoutlib-api:27.1.3:layoutlib-api-27.1.3.jar:23875ce0a8429f33a4e86cc358f658faa0ba9c576f5f05760e544b453d67d04b',
'com.android.tools.lint:lint-api:27.1.3:lint-api-27.1.3.jar:97666be32bcadacd944416ea334a9575ef8f4ad0c8f333151491ff4a7df43e1c',
'com.android.tools.lint:lint-checks:27.1.3:lint-checks-27.1.3.jar:b2d71ae84a31490fe9ff26c706163fe245b2aea98e3eb747214c1085df444708',
'com.android.tools.lint:lint-gradle-api:27.1.3:lint-gradle-api-27.1.3.jar:e54131c287a2954e6ed78a3351e5e10e35a1da2f09ac443bf44b705c71b63a4d',
'com.android.tools.lint:lint-gradle:27.1.3:lint-gradle-27.1.3.jar:6a79e48943649d63665db7b17dbaff7af93e94ab9b15072f1a4d90486294ee9f',
'com.android.tools.lint:lint-model:27.1.3:lint-model-27.1.3.jar:acb9e792db7000e38e3c3ca21a9b14f2de6549d7a3fc92a97ffba3d06345e5bf',
'com.android.tools.lint:lint:27.1.3:lint-27.1.3.jar:5a2e69d0901a3a476a5b2d5001de755868113145f5f6aa557750cfad5389a44b',
'com.android.tools:annotations:27.1.3:annotations-27.1.3.jar:904dd771883496d5dfc86619ab2555968ea4e8a29d7a5f4f7cae6fbf5429f8f5',
'com.android.tools:common:27.1.3:common-27.1.3.jar:17ab4728e3ea50f047dd5937f0faf35f2c5416962ed74891057087ddc328bf96',
'com.android.tools:dvlib:27.1.3:dvlib-27.1.3.jar:cead1c0c356cbe43e6855b0330fe09ef4bec2c72e22bdb4c6e7cf7e6b1dfbc37',
'com.android.tools:repository:27.1.3:repository-27.1.3.jar:99de1a178855b56b8cd91a56296f1e0a9399c445e6acc51f1d2927947cc472cb',
'com.android.tools:sdk-common:27.1.3:sdk-common-27.1.3.jar:b591e2aa0f1be600795f5c9e2bf81cba9b052bee452fc86c3362b5dd9e427a14',
'com.android.tools:sdklib:27.1.3:sdklib-27.1.3.jar:ad6c08a45fe2904d05656bdddf9f623fa5c1d16bbd7b8d6a270a0734136ae02e',
'com.android:signflinger:4.1.3:signflinger-4.1.3.jar:f3103b55ccdc8dd9ee2517eb26af93b904d41303726594372d0df59d51156e5c',
'com.android:zipflinger:4.1.3:zipflinger-4.1.3.jar:48569896c0497268308a8014c66eb0f2bace2b9e2fc9390f3012823fb86387d5',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.6:gson-2.8.6.jar:c8fb4839054d280b3033f800d1f5a97de2f028eb8ba2eb458ad287e536f3f25f',
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
'com.google.dagger:dagger-producers:2.33:dagger-producers-2.33.jar:5897f0b6eef799c2adfe3ccacc58c0fb374d58acb063c3ebe5366c38a8bce5c8',
'com.google.dagger:dagger-spi:2.33:dagger-spi-2.33.jar:e2dcab2221b8afb9556ef0a1c83b0bd5f42552e254322a257330f754cdbbb9d4',
'com.google.dagger:dagger:2.33:dagger-2.33.jar:d8798c5b8cf6b125234e33af5c6293bb9f2208ce29b57924c35b8c0be7b6bdcb',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
'com.google.errorprone:error_prone_annotations:2.3.4:error_prone_annotations-2.3.4.jar:baf7d6ea97ce606c53e11b6854ba5f2ce7ef5c24dddf0afa18d1260bd25b002c',
'com.google.errorprone:error_prone_annotations:2.3.2:error_prone_annotations-2.3.2.jar:357cd6cfb067c969226c442451502aee13800a24e950fdfde77bcdb4565a668d',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26',
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.guava:guava:30.1-jre:guava-30.1-jre.jar:e6dd072f9d3fe02a4600688380bd422bdac184caf6fe2418cfdd0934f09432aa',
'com.google.guava:guava:28.1-jre:guava-28.1-jre.jar:30beb8b8527bd07c6e747e77f1a92122c2f29d57ce347461a4a55eb26e382da4',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
@@ -54,108 +54,63 @@ dependencyVerification {
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
'com.sun.istack:istack-commons-runtime:3.0.8:istack-commons-runtime-3.0.8.jar:4ffabb06be454a05e4398e20c77fa2b6308d4b88dfbef7ca30a76b5b7d5505ef',
'com.sun.xml.fastinfoset:FastInfoset:1.2.16:FastInfoset-1.2.16.jar:056f3a1e144409f21ed16afc26805f58e9a21f3fce1543c42d400719d250c511',
'com.thoughtworks.qdox:qdox:1.12.1:qdox-1.12.1.jar:21fba22f830e9268f07cf4ab2d99e8181abbdcb0cb91ee0228eb3cb918dcdd1d',
'com.sun.istack:istack-commons-runtime:3.0.7:istack-commons-runtime-3.0.7.jar:6443e10ba2e259fb821d9b6becf10db5316285fc30c53cec9d7b19a3877e7fdf',
'com.sun.xml.fastinfoset:FastInfoset:1.2.15:FastInfoset-1.2.15.jar:785861db11ca1bd0d1956682b974ad73eb19cd3e01a4b3fa82d62eca97210aec',
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
'commons-io:commons-io:2.4:commons-io-2.4.jar:cc6a41dc3eaacc9e440a6bd0d2890b20d36b4ee408fe2d67122f328bb6e01581',
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
'info.picocli:picocli:4.5.2:picocli-4.5.2.jar:b4395e9a67932616efd2245d984bf5fcd453c2c5049558c3ce959ac2af4d3fac',
'it.unimi.dsi:fastutil:8.4.0:fastutil-8.4.0.jar:2ad2824a4a0a0eb836b52ee2fc84ba2134f44bce7bfa54015ae3f31c710a3071',
'jakarta.activation:jakarta.activation-api:1.2.1:jakarta.activation-api-1.2.1.jar:8b0a0f52fa8b05c5431921a063ed866efaa41dadf2e3a7ee3e1961f2b0d9645b',
'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2:jakarta.xml.bind-api-2.3.2.jar:69156304079bdeed9fc0ae3b39389f19b3cc4ba4443bc80508995394ead742ea',
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
'javax.activation:javax.activation-api:1.2.0:javax.activation-api-1.2.0.jar:43fdef0b5b6ceb31b0424b208b930c74ab58fac2ceeb7b3f6fd3aeb8b5ca4393',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'jline:jline:2.14.6:jline-2.14.6.jar:97d1acaac82409be42e622d7a54d3ae9d08517e8aefdea3d2ba9791150c2f02d',
'junit:junit:4.13.1:junit-4.13.1.jar:c30719db974d6452793fe191b3638a5777005485bae145924044530ffa5f6122',
'javax.xml.bind:jaxb-api:2.3.1:jaxb-api-2.3.1.jar:88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06',
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'net.java.dev.jna:jna-platform:5.6.0:jna-platform-5.6.0.jar:9ecea8bf2b1b39963939d18b70464eef60c508fed8820f9dcaba0c35518eabf7',
'net.java.dev.jna:jna:5.6.0:jna-5.6.0.jar:5557e235a8aa2f9766d5dc609d67948f2a8832c2d796cea9ef1d6cbe0b3b7eaf',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.apache.ant:ant-antlr:1.10.9:ant-antlr-1.10.9.jar:7623dc9d0f20ea713290c6bf1a23f4c059447aef7ff9f5b2be75960f3f028d2e',
'org.apache.ant:ant-junit:1.10.9:ant-junit-1.10.9.jar:960bdc8827954d62206ba42d0a68a7ee4476175ba47bb113e17e77cce7394630',
'org.apache.ant:ant-launcher:1.10.9:ant-launcher-1.10.9.jar:fcce891f57f3be72149ff96ac2a80574165b3e0839866b95d24528f3027d50c1',
'org.apache.ant:ant:1.10.9:ant-1.10.9.jar:0715478af585ea80a18985613ebecdc7922122d45b2c3c970ff9b352cddb75fc',
'org.apache.commons:commons-compress:1.20:commons-compress-1.20.jar:0aeb625c948c697ea7b205156e112363b59ed5e2551212cd4e460bdb72c7c06e',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
'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.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82',
'org.briarproject:tor-android:0.3.5.17:tor-android-0.3.5.17.jar:1888afc10a26b93d00a010ea27bf0b1b162a6d524688b08b98d70d14dc363b54',
'org.briarproject:tor-android:0.3.5.15:tor-android-0.3.5.15.jar:560c5070166300b396cb2f28d82d9f639ee1fb5479096a3cef67da56d39937ad',
'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',
'org.checkerframework:checker-qual:3.5.0:checker-qual-3.5.0.jar:729990b3f18a95606fc2573836b6958bcdb44cb52bfbd1b7aa9c339cff35a5a4',
'org.codehaus.groovy:groovy-ant:3.0.7:groovy-ant-3.0.7.jar:6ed2ba82813d128f7050c24142e87b3dc2ad8b504786280eb03e81f0cf6a5793',
'org.codehaus.groovy:groovy-astbuilder:3.0.7:groovy-astbuilder-3.0.7.jar:b290451eb1583666e906c41f7d14747b4cc96363c99c478b244634fd5dfc9013',
'org.codehaus.groovy:groovy-cli-picocli:3.0.7:groovy-cli-picocli-3.0.7.jar:71b4bd11fb30a9c7b5618e22122c9c5141958fb27f4dcf0068b6f715088f6916',
'org.codehaus.groovy:groovy-console:3.0.7:groovy-console-3.0.7.jar:0541b358b6b8e5363215026736168fccfec1d91bac678d066fa77349eeeaa5dd',
'org.codehaus.groovy:groovy-datetime:3.0.7:groovy-datetime-3.0.7.jar:b9823d14b1a4f94236ae2f8a471701aab17e093e1b33402b91550b5c8dd88f04',
'org.codehaus.groovy:groovy-docgenerator:3.0.7:groovy-docgenerator-3.0.7.jar:bf53f7a11c9eb1e278e1b8ed2714c741bcf781235c803ad3ba1555f2614573f3',
'org.codehaus.groovy:groovy-groovydoc:3.0.7:groovy-groovydoc-3.0.7.jar:86b24dfc23c005066ab83927cdb54177f06c9531773f2e2d2ecc9a131f7c2677',
'org.codehaus.groovy:groovy-groovysh:3.0.7:groovy-groovysh-3.0.7.jar:5c40e78cbc09726aedd1c75fab112d245d665d6294870f9119e6cd3013ed14ab',
'org.codehaus.groovy:groovy-jmx:3.0.7:groovy-jmx-3.0.7.jar:0a89f3007884eb156751937d93382038b83d39c7c2f0ab156ebf251a7251f2ab',
'org.codehaus.groovy:groovy-json:3.0.7:groovy-json-3.0.7.jar:df1f0ee475e3fc93a6a0d17548294e160cca5de6d9d36817a7be1fbe650de03b',
'org.codehaus.groovy:groovy-jsr223:3.0.7:groovy-jsr223-3.0.7.jar:1dbd969595332416193baa660fbb45743d19696eaa25fe98e591a2739e13517e',
'org.codehaus.groovy:groovy-macro:3.0.7:groovy-macro-3.0.7.jar:c6cc06df526b39e2c359e2435f0071594c5a1c7babafaa6c184fdd8fa931531f',
'org.codehaus.groovy:groovy-nio:3.0.7:groovy-nio-3.0.7.jar:db54c577882b294cd8c975ec5451596441baf54781319c61627dca0e0c2361ef',
'org.codehaus.groovy:groovy-servlet:3.0.7:groovy-servlet-3.0.7.jar:5b6a909bf501c209adfb6205b9e740649609074455fd979bf9da4853e6ff9a39',
'org.codehaus.groovy:groovy-sql:3.0.7:groovy-sql-3.0.7.jar:252bb6c74e1a9f41756ad4fbd3b0d2eddc93bb61109961dd1952a37bf2d57a64',
'org.codehaus.groovy:groovy-swing:3.0.7:groovy-swing-3.0.7.jar:bd942032d9328d54c6679c49a41f6caa0d4a0039ebe598493b8a647730d98cff',
'org.codehaus.groovy:groovy-templates:3.0.7:groovy-templates-3.0.7.jar:f119e07f650ef186ae5a4b944f9e30915b14311bad47c94a6b32de8d4f69bc80',
'org.codehaus.groovy:groovy-test-junit5:3.0.7:groovy-test-junit5-3.0.7.jar:c16eeea07b8e396891e266d7ba9388b24ac804237ffdd9a792b0d08969bad014',
'org.codehaus.groovy:groovy-test:3.0.7:groovy-test-3.0.7.jar:f71afd7c25d43017f89ea47e6de6daec971d159047dae083c1513a8422d44b90',
'org.codehaus.groovy:groovy-testng:3.0.7:groovy-testng-3.0.7.jar:713d5f2231bbb5712aefd362151b9ffd884aeb7ef2e773315cc54259cbdd063d',
'org.codehaus.groovy:groovy-xml:3.0.7:groovy-xml-3.0.7.jar:8a62e7c9ddece3e82676c4bef2f2c100f459602cd1fb6a14e94187bf863e97ff',
'org.codehaus.groovy:groovy:3.0.7:groovy-3.0.7.jar:51d1777e8dd1f00e60ea56e00d8a354ff5aab1f00fc8464ae8d39d71867e401f',
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
'org.glassfish.jaxb:jaxb-runtime:2.3.2:jaxb-runtime-2.3.2.jar:e6e0a1e89fb6ff786279e6a0082d5cef52dc2ebe67053d041800737652b4fd1b',
'org.glassfish.jaxb:txw2:2.3.2:txw2-2.3.2.jar:4a6a9f483388d461b81aa9a28c685b8b74c0597993bf1884b04eddbca95f48fe',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d',
'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f',
'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.jacoco:org.jacoco.agent:0.8.3:org.jacoco.agent-0.8.3.jar:522deb254ee16a04cc8341cc8f335f5cb7232982994d961b9cf3a0454709209f',
'org.jacoco:org.jacoco.ant:0.8.3:org.jacoco.ant-0.8.3.jar:735844e1ae15f9b875b42a27ac5cb61cc26e106d9e839e5d1c6756709b424ce0',
'org.jacoco:org.jacoco.core:0.8.3:org.jacoco.core-0.8.3.jar:0818437bc060a0c7cc798148f22b713702aae2771aba104444407697d578f1ea',
'org.jacoco:org.jacoco.report:0.8.3:org.jacoco.report-0.8.3.jar:aae08fa4ff043c807b8876cdb2d8705eb8449a55efce461baa6c09da245088c1',
'org.jetbrains.intellij.deps:trove4j:1.0.20181211:trove4j-1.0.20181211.jar:affb7c85a3c87bdcf69ff1dbb84de11f63dc931293934bc08cd7ab18de083601',
'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b',
'org.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a',
'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-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-jdk7:1.3.72:kotlin-stdlib-jdk7-1.3.72.jar:40566c0c08d414b9413ba556ff7f8a0b04b98b9f0f424d122dd2088510efccc4',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72:kotlin-stdlib-jdk8-1.3.72.jar:133da70cfc07b56094282eac5c59bccd59f167ee2ead22e5282876d8bc10bf95',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b',
'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.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b',
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd',
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
'org.junit.jupiter:junit-jupiter-api:5.7.0:junit-jupiter-api-5.7.0.jar:b03f78e0daeed2d77a0af9bcd662b4cdb9693f7ee72e01a539b508b84c63d182',
'org.junit.jupiter:junit-jupiter-engine:5.7.0:junit-jupiter-engine-5.7.0.jar:dfa26af94644ac2612dde6625852fcb550a0d21caa243257de54cba738ba87af',
'org.junit.platform:junit-platform-commons:1.7.0:junit-platform-commons-1.7.0.jar:5330ee87cc7586e6e25175a34e9251624ff12ff525269d3415d0b4ca519b6fea',
'org.junit.platform:junit-platform-engine:1.7.0:junit-platform-engine-1.7.0.jar:75f21a20dc594afdc875736725b408cec6d0344874d29f34b2dd3075500236f2',
'org.junit.platform:junit-platform-launcher:1.7.0:junit-platform-launcher-1.7.0.jar:fbdc748fde4c4279fe1d3c607447cb3b7ccd45d7338fc574f8a894ddf2d16818',
'org.jvnet.staxex:stax-ex:1.8.1:stax-ex-1.8.1.jar:20522549056e9e50aa35ef0b445a2e47a53d06be0b0a9467d704e2483ffb049a',
'org.jvnet.staxex:stax-ex:1.8:stax-ex-1.8.jar:95b05d9590af4154c6513b9c5dc1fb2e55b539972ba0a9ef28e9a0c01d83ad77',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.opentest4j:opentest4j:1.2.0:opentest4j-1.2.0.jar:58812de60898d976fb81ef3b62da05c6604c18fd4a249f5044282479fc286af2',
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
'org.testng:testng:7.3.0:testng-7.3.0.jar:63727488f9717d57f0d0a0fee5a1fc10a2be9cfcff2ec3a7187656d663c0774e',
'xerces:xercesImpl:2.12.0:xercesImpl-2.12.0.jar:b50d3a4ca502faa4d1c838acb8aa9480446953421f7327e338c5dda3da5e76d0',
'xml-apis:xml-apis:1.4.01:xml-apis-1.4.01.jar:a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad',
]
}

View File

@@ -9,11 +9,11 @@ apply from: 'witness.gradle'
dependencies {
implementation "com.google.dagger:dagger:$dagger_version"
implementation 'com.google.code.findbugs:jsr305:3.0.2'
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-legacy:$jmock_version"
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
}

View File

@@ -11,11 +11,7 @@ public interface FeatureFlags {
boolean shouldEnableDisappearingMessages();
boolean shouldEnableMailbox();
boolean shouldEnableTransferData();
boolean shouldEnablePrivateGroupsInCore();
boolean shouldEnableForumsInCore();
boolean shouldEnableBlogsInCore();
boolean shouldEnableShareAppViaOfflineHotspot();
}

View File

@@ -1,35 +0,0 @@
package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.lang.ref.WeakReference;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Provider;
/**
* A {@link Provider} that keeps a {@link WeakReference} to the last provided
* instance and provides the same instance again until the instance is garbage
* collected.
*/
@NotNullByDefault
public abstract class WeakSingletonProvider<T> implements Provider<T> {
private final Object lock = new Object();
@GuardedBy("lock")
private WeakReference<T> ref = new WeakReference<>(null);
@Override
public T get() {
synchronized (lock) {
T instance = ref.get();
if (instance == null) {
instance = createInstance();
ref = new WeakReference<>(instance);
}
return instance;
}
}
public abstract T createInstance();
}

View File

@@ -128,7 +128,7 @@ public interface ClientHelper {
* group.
*/
ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException;
throws DbException, FormatException;
/**
* Stores the given contact ID in the group metadata of the given contact

View File

@@ -107,32 +107,6 @@ public interface ContactManager {
*/
String getHandshakeLink() throws DbException;
/**
* Returns the handshake link that needs to be sent to a contact we want
* to add.
*/
String getHandshakeLink(Transaction txn) throws DbException;
/**
* Creates a {@link PendingContact} from the given handshake link and
* alias, adds it to the database and returns it.
*
* @param link The handshake link received from the pending contact
* @param alias The alias the user has given this pending contact
* @throws UnsupportedVersionException If the link uses a format version
* that is not supported
* @throws FormatException If the link is invalid
* @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid
* @throws ContactExistsException If a contact with the same handshake
* public key already exists
* @throws PendingContactExistsException If a pending contact with the same
* handshake public key already exists
*/
PendingContact addPendingContact(Transaction txn, String link, String alias)
throws DbException, FormatException, GeneralSecurityException,
ContactExistsException, PendingContactExistsException;
/**
* Creates a {@link PendingContact} from the given handshake link and
* alias, adds it to the database and returns it.
@@ -166,24 +140,11 @@ public interface ContactManager {
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
throws DbException;
/**
* Returns a list of {@link PendingContact PendingContacts} and their
* {@link PendingContactState states}.
*/
Collection<Pair<PendingContact, PendingContactState>> getPendingContacts(Transaction txn)
throws DbException;
/**
* Removes a {@link PendingContact}.
*/
void removePendingContact(PendingContactId p) throws DbException;
/**
* Removes a {@link PendingContact}.
*/
void removePendingContact(Transaction txn, PendingContactId p)
throws DbException;
/**
* Returns the contact with the given ID.
*/

View File

@@ -170,11 +170,4 @@ public interface CryptoComponent {
* length. The line terminator is CRLF.
*/
String asciiArmour(byte[] b, int lineLength);
/**
* Encode the onion/hidden service address given its public key. As
* specified here: https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt?id=29245fd5#n2135
*/
String encodeOnionAddress(byte[] publicKey);
}

View File

@@ -471,14 +471,6 @@ public interface DatabaseComponent extends TransactionManager {
Map<MessageId, Integer> getUnackedMessagesToSend(Transaction txn,
ContactId c) throws DbException;
/**
* Reset the transmission count, expiry time and ETA of all messages that
* are eligible to be sent to the given contact. This includes messages that
* have already been sent and are not yet due for retransmission.
*/
void resetUnackedMessagesToSend(Transaction txn, ContactId c)
throws DbException;
/**
* Returns the total length, including headers, of all messages that are
* eligible to be sent to the given contact. This may include messages

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class InvalidMailboxIdException extends Exception {
}

View File

@@ -1,24 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe
@NotNullByDefault
public class MailboxAuthToken extends MailboxId {
public MailboxAuthToken(byte[] id) {
super(id);
}
/**
* Creates a {@link MailboxAuthToken} from the given string.
*
* @throws InvalidMailboxIdException if token is not valid.
*/
public static MailboxAuthToken fromString(@Nullable String token)
throws InvalidMailboxIdException {
return new MailboxAuthToken(bytesFromString(token));
}
}

View File

@@ -1,24 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe
@NotNullByDefault
public class MailboxFileId extends MailboxId {
public MailboxFileId(byte[] id) {
super(id);
}
/**
* Creates a {@link MailboxFileId} from the given string.
*
* @throws IllegalArgumentException if token is not valid.
*/
public static MailboxFileId fromString(@Nullable String token)
throws InvalidMailboxIdException {
return new MailboxFileId(bytesFromString(token));
}
}

View File

@@ -1,24 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe
@NotNullByDefault
public class MailboxFolderId extends MailboxId {
public MailboxFolderId(byte[] id) {
super(id);
}
/**
* Creates a {@link MailboxFolderId} from the given string.
*
* @throws IllegalArgumentException if token is not valid.
*/
public static MailboxFolderId fromString(@Nullable String token)
throws InvalidMailboxIdException {
return new MailboxFolderId(bytesFromString(token));
}
}

View File

@@ -1,49 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import com.fasterxml.jackson.annotation.JsonValue;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Locale;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.toHexString;
@ThreadSafe
@NotNullByDefault
public abstract class MailboxId extends UniqueId {
MailboxId(byte[] id) {
super(id);
}
/**
* Returns valid {@link MailboxId} bytes from the given string.
*
* @throws InvalidMailboxIdException if token is not valid.
*/
static byte[] bytesFromString(@Nullable String token)
throws InvalidMailboxIdException {
if (token == null || token.length() != 64) {
throw new InvalidMailboxIdException();
}
try {
return fromHexString(token);
} catch (IllegalArgumentException e) {
throw new InvalidMailboxIdException();
}
}
/**
* Returns the string representation expected by the mailbox API.
* Also used for serialization.
*/
@Override
@JsonValue
public String toString() {
return toHexString(getBytes()).toLowerCase(Locale.US);
}
}

View File

@@ -1,35 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import javax.annotation.Nullable;
public interface MailboxManager {
/**
* @return true if a mailbox is already paired.
*/
boolean isPaired(Transaction txn) throws DbException;
/**
* @return the current status of the mailbox.
*/
MailboxStatus getMailboxStatus(Transaction txn) throws DbException;
/**
* Returns the currently running pairing task,
* or null if no pairing task is running.
*/
@Nullable
MailboxPairingTask getCurrentPairingTask();
/**
* Starts and returns a pairing task. If a pairing task is already running,
* it will be returned and the argument will be ignored.
*
* @param qrCodePayload The ISO-8859-1 encoded bytes of the mailbox QR code.
*/
MailboxPairingTask startPairingTask(String qrCodePayload);
}

View File

@@ -1,59 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import javax.annotation.Nullable;
public abstract class MailboxPairingState {
/**
* The QR code payload that was scanned by the user.
* This is null if the code should not be re-used anymore in this state.
*/
@Nullable
public final String qrCodePayload;
MailboxPairingState(@Nullable String qrCodePayload) {
this.qrCodePayload = qrCodePayload;
}
public static class QrCodeReceived extends MailboxPairingState {
public QrCodeReceived(String qrCodePayload) {
super(qrCodePayload);
}
}
public static class Pairing extends MailboxPairingState {
public Pairing(String qrCodePayload) {
super(qrCodePayload);
}
}
public static class Paired extends MailboxPairingState {
public Paired() {
super(null);
}
}
public static class InvalidQrCode extends MailboxPairingState {
public InvalidQrCode() {
super(null);
}
}
public static class MailboxAlreadyPaired extends MailboxPairingState {
public MailboxAlreadyPaired() {
super(null);
}
}
public static class ConnectionError extends MailboxPairingState {
public ConnectionError(String qrCodePayload) {
super(qrCodePayload);
}
}
public static class UnexpectedError extends MailboxPairingState {
public UnexpectedError(String qrCodePayload) {
super(qrCodePayload);
}
}
}

View File

@@ -1,21 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.Consumer;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface MailboxPairingTask extends Runnable {
/**
* Adds an observer to the task. The observer will be notified on the
* event thread of the current state of the task and any subsequent state
* changes.
*/
void addObserver(Consumer<MailboxPairingState> observer);
/**
* Removes an observer from the task.
*/
void removeObserver(Consumer<MailboxPairingState> observer);
}

View File

@@ -1,33 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class MailboxProperties {
private final String baseUrl;
private final MailboxAuthToken authToken;
private final boolean owner;
public MailboxProperties(String baseUrl, MailboxAuthToken authToken,
boolean owner) {
this.baseUrl = baseUrl;
this.authToken = authToken;
this.owner = owner;
}
public String getBaseUrl() {
return baseUrl;
}
public MailboxAuthToken getAuthToken() {
return authToken;
}
public boolean isOwner() {
return owner;
}
}

View File

@@ -1,33 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
public interface MailboxSettingsManager {
@Nullable
MailboxProperties getOwnMailboxProperties(Transaction txn)
throws DbException;
void setOwnMailboxProperties(Transaction txn, MailboxProperties p)
throws DbException;
MailboxStatus getOwnMailboxStatus(Transaction txn) throws DbException;
void recordSuccessfulConnection(Transaction txn, long now)
throws DbException;
void recordFailedConnectionAttempt(Transaction txn, long now)
throws DbException;
void setPendingUpload(Transaction txn, ContactId id,
@Nullable String filename) throws DbException;
@Nullable
String getPendingUpload(Transaction txn, ContactId id) throws DbException;
}

View File

@@ -1,59 +0,0 @@
package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class MailboxStatus {
private final long lastAttempt, lastSuccess;
private final int attemptsSinceSuccess;
public MailboxStatus(long lastAttempt, long lastSuccess,
int attemptsSinceSuccess) {
this.lastAttempt = lastAttempt;
this.lastSuccess = lastSuccess;
this.attemptsSinceSuccess = attemptsSinceSuccess;
}
/**
* Returns the time of the last attempt to connect to the mailbox, in
* milliseconds since the Unix epoch, or -1 if no attempt has been made.
* <p>
* If an attempt is in progress and has not yet succeeded or failed then
* this method returns the time of the previous attempt, or -1 if the
* current attempt is the first.
*/
public long getTimeOfLastAttempt() {
return lastAttempt;
}
/**
* Returns the time of the last successful attempt to connect to the
* mailbox, in milliseconds since the Unix epoch, or -1 if no attempt has
* succeeded.
* <p>
* If the last attempt was successful then this method returns the same
* value as {@link #getTimeOfLastAttempt()}. If an attempt is in progress
* and has not yet succeeded or failed then this method returns the time
* of the previous successful connection, or -1 if no attempt has
* succeeded.
*/
public long getTimeOfLastSuccess() {
return lastSuccess;
}
/**
* Returns the number of attempts to connect to the mailbox that have
* failed since the last attempt succeeded, or the number of attempts that
* have been made, if no attempt has ever succeeded.
* <p>
* If an attempt is in progress and has not yet succeeded or failed then
* it is not included in this count.
*/
public int getAttemptsSinceSuccess() {
return attemptsSinceSuccess;
}
}

View File

@@ -1,15 +1,15 @@
package org.briarproject.bramble.api.plugin;
import static java.util.concurrent.TimeUnit.DAYS;
public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor");
// Transport properties
String PROP_ONION_V2 = "onion";
String PROP_ONION_V3 = "onion3";
int DEFAULT_SOCKS_PORT = 59050;
int DEFAULT_CONTROL_PORT = 59051;
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
@@ -18,7 +18,14 @@ public interface TorConstants {
String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
String HS_PRIVATE_KEY_V2 = "onionPrivKey";
String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
String HS_V3_CREATED = "onionPrivKey3Created";
/**
* How long to publish a v3 hidden service before retiring the v2 service.
*/
long V3_MIGRATION_PERIOD_MS = DAYS.toMillis(180);
// Values for PREF_TOR_NETWORK
int PREF_TOR_NETWORK_AUTOMATIC = 0;

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.plugin;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the control port for the Tor plugin.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface TorControlPort {
}

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.plugin;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the socks port for the Tor plugin.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface TorSocksPort {
}

View File

@@ -22,11 +22,4 @@ public interface SettingsManager {
* namespace.
*/
void mergeSettings(Settings s, String namespace) throws DbException;
/**
* Merges the given settings with any existing settings in the given
* namespace.
*/
void mergeSettings(Transaction txn, Settings s, String namespace)
throws DbException;
}

View File

@@ -113,25 +113,9 @@ public interface KeyManager {
/**
* Looks up the given tag and returns a {@link StreamContext} for reading
* from the corresponding stream, or null if an error occurs or the tag was
* unexpected. Marks the tag as recognised and updates the reordering
* window.
* unexpected.
*/
@Nullable
StreamContext getStreamContext(TransportId t, byte[] tag)
throws DbException;
/**
* Looks up the given tag and returns a {@link StreamContext} for reading
* from the corresponding stream, or null if an error occurs or the tag was
* unexpected. Only returns the StreamContext; does not mark the tag as
* recognised.
*/
@Nullable
StreamContext getStreamContextOnly(TransportId t, byte[] tag)
throws DbException;
/**
* Marks the tag as recognised and updates the reordering window.
*/
void markTagAsRecognised(TransportId t, byte[] tag) throws DbException;
}

View File

@@ -1,17 +1,12 @@
package org.briarproject.bramble.test;
import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser;
import org.junit.After;
public abstract class BrambleMockTestCase extends BrambleTestCase {
protected final Mockery context = new Mockery();
public BrambleMockTestCase() {
context.setThreadingPolicy(new Synchroniser());
}
@After
public void checkExpectations() {
context.assertIsSatisfied();

View File

@@ -25,11 +25,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.util.IoUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -49,7 +45,6 @@ import static org.briarproject.bramble.api.properties.TransportPropertyConstants
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
public class TestUtils {
@@ -214,24 +209,6 @@ public class TestUtils {
getAgreementPublicKey(), verified);
}
public static void writeBytes(File file, byte[] bytes)
throws IOException {
FileOutputStream outputStream = new FileOutputStream(file);
//noinspection TryFinallyCanBeTryWithResources
try {
outputStream.write(bytes);
} finally {
outputStream.close();
}
}
public static byte[] readBytes(File file) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
FileInputStream inputStream = new FileInputStream(file);
copyAndClose(inputStream, outputStream);
return outputStream.toByteArray();
}
public static double getMedian(Collection<? extends Number> samples) {
int size = samples.size();
if (size == 0) throw new IllegalArgumentException();

View File

@@ -9,29 +9,23 @@ apply from: 'witness.gradle'
apply from: '../dagger.gradle'
dependencies {
implementation project(path: ':bramble-api', configuration: 'default')
implementation 'org.bouncycastle:bcprov-jdk15to18:1.70'
//noinspection GradleDependency
implementation project(path: ':briar:bramble-api', configuration: 'default')
implementation 'org.bouncycastle:bcprov-jdk15on:1.69'
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
implementation 'org.bitlet:weupnp:0.1.4'
implementation 'net.i2p.crypto:eddsa:0.2.0'
implementation 'org.whispersystems:curve25519-java:0.5.0'
implementation 'org.briarproject:jtorctl:0.3'
//noinspection GradleDependency
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':briar:bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
testImplementation 'net.jodah:concurrentunit:0.4.2'
testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-imposters:$jmock_version"
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
testImplementation "org.jmock:jmock-legacy:$jmock_version"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"

View File

@@ -14,7 +14,6 @@ import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.io.IoModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.mailbox.MailboxModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.record.RecordModule;
@@ -44,7 +43,6 @@ import dagger.Module;
IoModule.class,
KeyAgreementModule.class,
LifecycleModule.class,
MailboxModule.class,
PluginModule.class,
PropertiesModule.class,
RecordModule.class,

View File

@@ -121,40 +121,28 @@ class ContactManagerImpl implements ContactManager, EventListener {
@Override
public String getHandshakeLink() throws DbException {
return db.transactionWithResult(true, this::getHandshakeLink);
}
@Override
public String getHandshakeLink(Transaction txn) throws DbException {
KeyPair keyPair = identityManager.getHandshakeKeys(txn);
KeyPair keyPair = db.transactionWithResult(true,
identityManager::getHandshakeKeys);
return pendingContactFactory.createHandshakeLink(keyPair.getPublic());
}
@Override
public PendingContact addPendingContact(Transaction txn, String link,
String alias)
throws DbException, FormatException, GeneralSecurityException {
PendingContact p =
pendingContactFactory.createPendingContact(link, alias);
AuthorId local = identityManager.getLocalAuthor(txn).getId();
db.addPendingContact(txn, p, local);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
ourKeyPair);
return p;
}
@Override
public PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException, GeneralSecurityException {
PendingContact p =
pendingContactFactory.createPendingContact(link, alias);
Transaction txn = db.startTransaction(false);
try {
PendingContact p = addPendingContact(txn, link, alias);
AuthorId local = identityManager.getLocalAuthor(txn).getId();
db.addPendingContact(txn, p, local);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
ourKeyPair);
db.commitTransaction(txn);
return p;
} finally {
db.endTransaction(txn);
}
return p;
}
@Override
@@ -166,14 +154,8 @@ class ContactManagerImpl implements ContactManager, EventListener {
@Override
public Collection<Pair<PendingContact, PendingContactState>> getPendingContacts()
throws DbException {
return db.transactionWithResult(true, this::getPendingContacts);
}
@Override
public Collection<Pair<PendingContact, PendingContactState>> getPendingContacts(
Transaction txn)
throws DbException {
Collection<PendingContact> pendingContacts = db.getPendingContacts(txn);
Collection<PendingContact> pendingContacts =
db.transactionWithResult(true, db::getPendingContacts);
List<Pair<PendingContact, PendingContactState>> pairs =
new ArrayList<>(pendingContacts.size());
for (PendingContact p : pendingContacts) {
@@ -186,13 +168,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
@Override
public void removePendingContact(PendingContactId p) throws DbException {
db.transaction(false, txn -> removePendingContact(txn, p));
}
@Override
public void removePendingContact(Transaction txn, PendingContactId p)
throws DbException {
db.removePendingContact(txn, p);
db.transaction(false, txn -> db.removePendingContact(txn, p));
states.remove(p);
}

View File

@@ -7,7 +7,6 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.Blake2bDigest;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent;
@@ -22,13 +21,11 @@ import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.Base32;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.whispersystems.curve25519.Curve25519;
import org.whispersystems.curve25519.Curve25519KeyPair;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
@@ -61,8 +58,6 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final byte PBKDF_FORMAT_SCRYPT = 0;
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
private static final byte ONION_HS_PROTOCOL_VERSION = 3;
private static final int ONION_CHECKSUM_BYTES = 2;
private final SecureRandom secureRandom;
private final PasswordBasedKdf passwordBasedKdf;
@@ -447,21 +442,4 @@ class CryptoComponentImpl implements CryptoComponent {
public String asciiArmour(byte[] b, int lineLength) {
return AsciiArmour.wrap(b, lineLength);
}
@Override
public String encodeOnionAddress(byte[] publicKey) {
Digest digest = new SHA3Digest(256);
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
digest.update(label, 0, label.length);
digest.update(publicKey, 0, publicKey.length);
digest.update(ONION_HS_PROTOCOL_VERSION);
byte[] checksum = new byte[digest.getDigestSize()];
digest.doFinal(checksum, 0);
byte[] address = new byte[publicKey.length + ONION_CHECKSUM_BYTES + 1];
arraycopy(publicKey, 0, address, 0, publicKey.length);
arraycopy(checksum, 0, address, publicKey.length, ONION_CHECKSUM_BYTES);
address[address.length - 1] = ONION_HS_PROTOCOL_VERSION;
return Base32.encode(address).toLowerCase();
}
}

View File

@@ -757,13 +757,6 @@ interface Database<T> {
*/
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
/**
* Resets the transmission count, expiry time and ETA of all messages that
* are eligible to be sent to the given contact. This includes messages that
* have already been sent and are not yet due for retransmission.
*/
void resetUnackedMessagesToSend(T txn, ContactId c) throws DbException;
/**
* Sets the cleanup timer duration for the given message. This does not
* start the message's cleanup timer.
@@ -852,8 +845,8 @@ interface Database<T> {
* of the given message with respect to the given contact, using the latency
* of the transport over which it was sent.
*/
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m,
long maxLatency) throws DbException;
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, long maxLatency)
throws DbException;
/**
* Stores the given transport keys, deleting any keys they have replaced.

View File

@@ -750,15 +750,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getUnackedMessagesToSend(txn, c);
}
@Override
public void resetUnackedMessagesToSend(Transaction transaction, ContactId c)
throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.resetUnackedMessagesToSend(txn, c);
}
@Override
public long getUnackedMessageBytesToSend(Transaction transaction,
ContactId c) throws DbException {

View File

@@ -429,11 +429,8 @@ abstract class JdbcDatabase implements Database<Connection> {
compactAndClose();
logDuration(LOG, "Compacting database", start);
// Allow the next transaction to reopen the DB
connectionsLock.lock();
try {
synchronized (connectionsLock) {
closed = false;
} finally {
connectionsLock.unlock();
}
txn = startTransaction();
try {
@@ -3293,29 +3290,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void resetUnackedMessagesToSend(Connection txn, ContactId c)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE statuses SET expiry = 0, txCount = 0, eta = 0"
+ " WHERE contactId = ? AND state = ?"
+ " AND groupShared = TRUE AND messageShared = TRUE"
+ " AND deleted = FALSE AND seen = FALSE";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, DELIVERED.getValue());
int affected = ps.executeUpdate();
if (affected < 0) {
throw new DbStateException();
}
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public void setCleanupTimerDuration(Connection txn, MessageId m,
long duration) throws DbException {

View File

@@ -1,49 +1,18 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.WeakSingletonProvider;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import javax.annotation.Nonnull;
import javax.inject.Singleton;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
import okhttp3.Dns;
import okhttp3.OkHttpClient;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@Module
public class IoModule {
private static final int CONNECT_TIMEOUT = 60_000; // Milliseconds
@Provides
@Singleton
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
return timeoutMonitor;
}
// Share an HTTP client instance between requests where possible, while
// allowing the client to be garbage-collected between requests. The
// provider keeps a weak reference to the last client instance and reuses
// the instance until it gets garbage-collected. See
// https://medium.com/@leandromazzuquini/if-you-are-using-okhttp-you-should-know-this-61d68e065a2b
@Provides
@Singleton
WeakSingletonProvider<OkHttpClient> provideOkHttpClientProvider(
SocketFactory torSocketFactory, Dns noDnsLookups) {
return new WeakSingletonProvider<OkHttpClient>() {
@Override
@Nonnull
public OkHttpClient createInstance() {
return new OkHttpClient.Builder()
.socketFactory(torSocketFactory)
.dns(noDnsLookups) // Don't make local DNS lookups
.connectTimeout(CONNECT_TIMEOUT, MILLISECONDS)
.build();
}
};
}
}

View File

@@ -1,177 +0,0 @@
package org.briarproject.bramble.mailbox;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxFileId;
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
interface MailboxApi {
/**
* Sets up the mailbox with the setup token.
*
* @param properties MailboxProperties with the setup token
* @return the owner token
* @throws ApiException for 401 response.
*/
MailboxAuthToken setup(MailboxProperties properties)
throws IOException, ApiException;
/**
* Checks the status of the mailbox.
*
* @return true if the status is OK, false otherwise.
* @throws ApiException for 401 response.
*/
boolean checkStatus(MailboxProperties properties)
throws IOException, ApiException;
/**
* Unpairs Briar and the mailbox (owner only).
* Resets mailbox state to that after first install
* (e.g. removes all stored files as well).
*/
void wipeMailbox(MailboxProperties properties)
throws IOException, ApiException;
/**
* Adds a new contact to the mailbox.
*
* @throws TolerableFailureException if response code is 409
* (contact was already added).
*/
void addContact(MailboxProperties properties, MailboxContact contact)
throws IOException, ApiException, TolerableFailureException;
/**
* Deletes a contact from the mailbox.
* This should get called after a contact was removed from Briar.
*
* @throws TolerableFailureException if response code is 404
* (contact probably was already deleted).
*/
void deleteContact(MailboxProperties properties, ContactId contactId)
throws IOException, ApiException, TolerableFailureException;
/**
* Gets a list of {@link ContactId}s from the mailbox.
* These are the contacts that the mailbox already knows about.
*/
Collection<ContactId> getContacts(MailboxProperties properties)
throws IOException, ApiException;
/**
* Used by contacts to send files to the owner
* and by the owner to send files to contacts.
* <p>
* The owner can add files to the contacts' inboxes
* and the contacts can add files to their own outbox.
*/
void addFile(MailboxProperties properties, MailboxFolderId folderId,
File file) throws IOException, ApiException;
/**
* Used by owner and contacts to list their files to retrieve.
* <p>
* Returns 200 OK with the list of files in JSON.
*/
List<MailboxFile> getFiles(MailboxProperties properties,
MailboxFolderId folderId) throws IOException, ApiException;
/**
* Used by owner and contacts to retrieve a file.
* <p>
* Returns 200 OK if successful with the files' raw bytes
* in the response body.
*
* @param file the empty file the response bytes will be written into.
*/
void getFile(MailboxProperties properties, MailboxFolderId folderId,
MailboxFileId fileId, File file) throws IOException, ApiException;
/**
* Used by owner and contacts to delete files.
* <p>
* Returns 200 OK (no exception) if deletion was successful.
*
* @throws TolerableFailureException on 404 response,
* because file was most likely deleted already.
*/
void deleteFile(MailboxProperties properties, MailboxFolderId folderId,
MailboxFileId fileId)
throws IOException, ApiException, TolerableFailureException;
/**
* Lists all contact outboxes that have files available
* for the owner to download.
*
* @return a list of folder names
* to be used with {@link #getFiles(MailboxProperties, MailboxFolderId)}.
* @throws IllegalArgumentException if used by non-owner.
*/
List<MailboxFolderId> getFolders(MailboxProperties properties)
throws IOException, ApiException;
@Immutable
@JsonSerialize
class MailboxContact {
public final int contactId;
public final MailboxAuthToken token;
public final MailboxFolderId inboxId, outboxId;
MailboxContact(ContactId contactId,
MailboxAuthToken token,
MailboxFolderId inboxId,
MailboxFolderId outboxId) {
this.contactId = contactId.getInt();
this.token = token;
this.inboxId = inboxId;
this.outboxId = outboxId;
}
}
@JsonSerialize
class MailboxFile implements Comparable<MailboxFile> {
public final MailboxFileId name;
public final long time;
public MailboxFile(MailboxFileId name, long time) {
this.name = name;
this.time = time;
}
@Override
public int compareTo(@Nonnull MailboxApi.MailboxFile mailboxFile) {
//noinspection UseCompareMethod
return time < mailboxFile.time ? -1 :
(time == mailboxFile.time ? 0 : 1);
}
}
@Immutable
class ApiException extends Exception {
}
@Immutable
class MailboxAlreadyPairedException extends ApiException {
}
/**
* A failure that does not need to be retried,
* e.g. when adding a contact that already exists.
*/
@Immutable
class TolerableFailureException extends Exception {
}
}

View File

@@ -1,301 +0,0 @@
package org.briarproject.bramble.mailbox;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.briarproject.bramble.api.WeakSingletonProvider;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxFileId;
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
import org.briarproject.bramble.api.mailbox.MailboxId;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES;
import static java.util.Objects.requireNonNull;
import static okhttp3.internal.Util.EMPTY_REQUEST;
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
@NotNullByDefault
class MailboxApiImpl implements MailboxApi {
private final WeakSingletonProvider<OkHttpClient> httpClientProvider;
private final JsonMapper mapper = JsonMapper.builder()
.enable(BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES)
.build();
private static final MediaType JSON =
requireNonNull(MediaType.parse("application/json; charset=utf-8"));
private static final MediaType FILE =
requireNonNull(MediaType.parse("application/octet-stream"));
@Inject
MailboxApiImpl(WeakSingletonProvider<OkHttpClient> httpClientProvider) {
this.httpClientProvider = httpClientProvider;
}
@Override
public MailboxAuthToken setup(MailboxProperties properties)
throws IOException, ApiException {
if (!properties.isOwner()) throw new IllegalArgumentException();
Request request = getRequestBuilder(properties.getAuthToken())
.url(properties.getBaseUrl() + "/setup")
.put(EMPTY_REQUEST)
.build();
OkHttpClient client = httpClientProvider.get();
Response response = client.newCall(request).execute();
if (response.code() == 401) throw new MailboxAlreadyPairedException();
if (!response.isSuccessful()) throw new ApiException();
ResponseBody body = response.body();
if (body == null) throw new ApiException();
try {
JsonNode node = mapper.readTree(body.string());
JsonNode tokenNode = node.get("token");
if (tokenNode == null) {
throw new ApiException();
}
String ownerToken = tokenNode.textValue();
return MailboxAuthToken.fromString(ownerToken);
} catch (JacksonException | InvalidMailboxIdException e) {
throw new ApiException();
}
}
@Override
public boolean checkStatus(MailboxProperties properties)
throws IOException, ApiException {
if (!properties.isOwner()) throw new IllegalArgumentException();
Response response = sendGetRequest(properties, "/status");
if (response.code() == 401) throw new ApiException();
return response.isSuccessful();
}
@Override
public void wipeMailbox(MailboxProperties properties)
throws IOException, ApiException {
if (!properties.isOwner()) throw new IllegalArgumentException();
Request request = getRequestBuilder(properties.getAuthToken())
.url(properties.getBaseUrl() + "/")
.delete()
.build();
OkHttpClient client = httpClientProvider.get();
Response response = client.newCall(request).execute();
if (response.code() != 204) throw new ApiException();
}
/* Contact Management API (owner only) */
@Override
public void addContact(MailboxProperties properties, MailboxContact contact)
throws IOException, ApiException, TolerableFailureException {
if (!properties.isOwner()) throw new IllegalArgumentException();
byte[] bodyBytes = mapper.writeValueAsBytes(contact);
RequestBody body = RequestBody.create(JSON, bodyBytes);
Response response = sendPostRequest(properties, "/contacts", body);
if (response.code() == 409) throw new TolerableFailureException();
if (!response.isSuccessful()) throw new ApiException();
}
@Override
public void deleteContact(MailboxProperties properties, ContactId contactId)
throws IOException, ApiException, TolerableFailureException {
if (!properties.isOwner()) throw new IllegalArgumentException();
String url = properties.getBaseUrl() + "/contacts/" +
contactId.getInt();
Request request = getRequestBuilder(properties.getAuthToken())
.delete()
.url(url)
.build();
OkHttpClient client = httpClientProvider.get();
Response response = client.newCall(request).execute();
if (response.code() == 404) throw new TolerableFailureException();
if (response.code() != 200) throw new ApiException();
}
@Override
public Collection<ContactId> getContacts(MailboxProperties properties)
throws IOException, ApiException {
if (!properties.isOwner()) throw new IllegalArgumentException();
Response response = sendGetRequest(properties, "/contacts");
if (response.code() != 200) throw new ApiException();
ResponseBody body = response.body();
if (body == null) throw new ApiException();
try {
JsonNode node = mapper.readTree(body.string());
ArrayNode contactsNode = getArray(node, "contacts");
List<ContactId> list = new ArrayList<>();
for (JsonNode contactNode : contactsNode) {
if (!contactNode.isNumber()) throw new ApiException();
int id = contactNode.intValue();
if (id < 1) throw new ApiException();
list.add(new ContactId(id));
}
return list;
} catch (JacksonException e) {
throw new ApiException();
}
}
/* File Management (owner and contacts) */
@Override
public void addFile(MailboxProperties properties, MailboxFolderId folderId,
File file) throws IOException, ApiException {
String path = "/files/" + folderId;
RequestBody body = RequestBody.create(FILE, file);
Response response = sendPostRequest(properties, path, body);
if (response.code() != 200) throw new ApiException();
}
@Override
public List<MailboxFile> getFiles(MailboxProperties properties,
MailboxFolderId folderId) throws IOException, ApiException {
String path = "/files/" + folderId;
Response response = sendGetRequest(properties, path);
if (response.code() != 200) throw new ApiException();
ResponseBody body = response.body();
if (body == null) throw new ApiException();
try {
JsonNode node = mapper.readTree(body.string());
ArrayNode filesNode = getArray(node, "files");
List<MailboxFile> list = new ArrayList<>();
for (JsonNode fileNode : filesNode) {
if (!fileNode.isObject()) throw new ApiException();
ObjectNode objectNode = (ObjectNode) fileNode;
JsonNode nameNode = objectNode.get("name");
JsonNode timeNode = objectNode.get("time");
if (nameNode == null || !nameNode.isTextual()) {
throw new ApiException();
}
if (timeNode == null || !timeNode.isNumber()) {
throw new ApiException();
}
String name = nameNode.asText();
long time = timeNode.asLong();
if (time < 1) throw new ApiException();
list.add(new MailboxFile(MailboxFileId.fromString(name), time));
}
Collections.sort(list);
return list;
} catch (JacksonException | InvalidMailboxIdException e) {
throw new ApiException();
}
}
@Override
public void getFile(MailboxProperties properties, MailboxFolderId folderId,
MailboxFileId fileId, File file) throws IOException, ApiException {
String path = "/files/" + folderId + "/" + fileId;
Response response = sendGetRequest(properties, path);
if (response.code() != 200) throw new ApiException();
ResponseBody body = response.body();
if (body == null) throw new ApiException();
FileOutputStream outputStream = new FileOutputStream(file);
copyAndClose(body.byteStream(), outputStream);
}
@Override
public void deleteFile(MailboxProperties properties,
MailboxFolderId folderId, MailboxFileId fileId)
throws IOException, ApiException, TolerableFailureException {
String path = "/files/" + folderId + "/" + fileId;
Request request = getRequestBuilder(properties.getAuthToken())
.delete()
.url(properties.getBaseUrl() + path)
.build();
OkHttpClient client = httpClientProvider.get();
Response response = client.newCall(request).execute();
if (response.code() == 404) throw new TolerableFailureException();
if (response.code() != 200) throw new ApiException();
}
@Override
public List<MailboxFolderId> getFolders(MailboxProperties properties)
throws IOException, ApiException {
if (!properties.isOwner()) throw new IllegalArgumentException();
Response response = sendGetRequest(properties, "/folders");
if (response.code() != 200) throw new ApiException();
ResponseBody body = response.body();
if (body == null) throw new ApiException();
try {
JsonNode node = mapper.readTree(body.string());
ArrayNode filesNode = getArray(node, "folders");
List<MailboxFolderId> list = new ArrayList<>();
for (JsonNode fileNode : filesNode) {
if (!fileNode.isObject()) throw new ApiException();
ObjectNode objectNode = (ObjectNode) fileNode;
JsonNode idNode = objectNode.get("id");
if (idNode == null || !idNode.isTextual()) {
throw new ApiException();
}
String id = idNode.asText();
list.add(MailboxFolderId.fromString(id));
}
return list;
} catch (JacksonException | InvalidMailboxIdException e) {
throw new ApiException();
}
}
/* Helper Functions */
private Response sendGetRequest(MailboxProperties properties, String path)
throws IOException {
Request request = getRequestBuilder(properties.getAuthToken())
.url(properties.getBaseUrl() + path)
.build();
OkHttpClient client = httpClientProvider.get();
return client.newCall(request).execute();
}
private Response sendPostRequest(MailboxProperties properties, String path,
RequestBody body) throws IOException {
Request request = getRequestBuilder(properties.getAuthToken())
.url(properties.getBaseUrl() + path)
.post(body)
.build();
OkHttpClient client = httpClientProvider.get();
return client.newCall(request).execute();
}
private Request.Builder getRequestBuilder(MailboxId token) {
return new Request.Builder()
.addHeader("Authorization", "Bearer " + token);
}
/* JSON helpers */
private ArrayNode getArray(JsonNode node, String name) throws ApiException {
JsonNode arrayNode = node.get(name);
if (arrayNode == null || !arrayNode.isArray()) {
throw new ApiException();
}
return (ArrayNode) arrayNode;
}
}

View File

@@ -1,78 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.mailbox.MailboxManager;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.mailbox.MailboxStatus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class MailboxManagerImpl implements MailboxManager {
private final Executor ioExecutor;
private final MailboxSettingsManager mailboxSettingsManager;
private final MailboxPairingTaskFactory pairingTaskFactory;
private final Object lock = new Object();
@Nullable
@GuardedBy("lock")
private MailboxPairingTask pairingTask = null;
@Inject
MailboxManagerImpl(
@IoExecutor Executor ioExecutor,
MailboxSettingsManager mailboxSettingsManager,
MailboxPairingTaskFactory pairingTaskFactory) {
this.ioExecutor = ioExecutor;
this.mailboxSettingsManager = mailboxSettingsManager;
this.pairingTaskFactory = pairingTaskFactory;
}
@Override
public boolean isPaired(Transaction txn) throws DbException {
return mailboxSettingsManager.getOwnMailboxProperties(txn) != null;
}
@Override
public MailboxStatus getMailboxStatus(Transaction txn) throws DbException {
return mailboxSettingsManager.getOwnMailboxStatus(txn);
}
@Nullable
@Override
public MailboxPairingTask getCurrentPairingTask() {
synchronized (lock) {
return pairingTask;
}
}
@Override
public MailboxPairingTask startPairingTask(String payload) {
MailboxPairingTask created;
synchronized (lock) {
if (pairingTask != null) return pairingTask;
created = pairingTaskFactory.createPairingTask(payload);
pairingTask = created;
}
ioExecutor.execute(() -> {
created.run();
synchronized (lock) {
// remove task after it finished
pairingTask = null;
}
});
return created;
}
}

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.mailbox.MailboxManager;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class MailboxModule {
@Provides
@Singleton
MailboxManager providesMailboxManager(MailboxManagerImpl mailboxManager) {
return mailboxManager;
}
@Provides
MailboxPairingTaskFactory provideMailboxPairingTaskFactory(
MailboxPairingTaskFactoryImpl mailboxPairingTaskFactory) {
return mailboxPairingTaskFactory;
}
@Provides
MailboxSettingsManager provideMailboxSettingsManager(
MailboxSettingsManagerImpl mailboxSettingsManager) {
return mailboxSettingsManager;
}
@Provides
MailboxApi providesMailboxApi(MailboxApiImpl mailboxApi) {
return mailboxApi;
}
}

View File

@@ -1,12 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
interface MailboxPairingTaskFactory {
MailboxPairingTask createPairingTask(String qrCodePayload);
}

View File

@@ -1,48 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
private final Executor eventExecutor;
private final TransactionManager db;
private final CryptoComponent crypto;
private final Clock clock;
private final MailboxApi api;
private final MailboxSettingsManager mailboxSettingsManager;
@Inject
MailboxPairingTaskFactoryImpl(
@EventExecutor Executor eventExecutor,
TransactionManager db,
CryptoComponent crypto,
Clock clock,
MailboxApi api,
MailboxSettingsManager mailboxSettingsManager) {
this.eventExecutor = eventExecutor;
this.db = db;
this.crypto = crypto;
this.clock = clock;
this.api = api;
this.mailboxSettingsManager = mailboxSettingsManager;
}
@Override
public MailboxPairingTask createPairingTask(String qrCodePayload) {
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
crypto, clock, api, mailboxSettingsManager);
}
}

View File

@@ -1,172 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Consumer;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@NotNullByDefault
class MailboxPairingTaskImpl implements MailboxPairingTask {
private final static Logger LOG =
getLogger(MailboxPairingTaskImpl.class.getName());
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private static final int VERSION_REQUIRED = 32;
private final String payload;
private final Executor eventExecutor;
private final TransactionManager db;
private final CryptoComponent crypto;
private final Clock clock;
private final MailboxApi api;
private final MailboxSettingsManager mailboxSettingsManager;
private final Object lock = new Object();
@GuardedBy("lock")
private final List<Consumer<MailboxPairingState>> observers =
new ArrayList<>();
@GuardedBy("lock")
private MailboxPairingState state;
MailboxPairingTaskImpl(
String payload,
@EventExecutor Executor eventExecutor,
TransactionManager db,
CryptoComponent crypto,
Clock clock,
MailboxApi api,
MailboxSettingsManager mailboxSettingsManager) {
this.payload = payload;
this.eventExecutor = eventExecutor;
this.db = db;
this.crypto = crypto;
this.clock = clock;
this.api = api;
this.mailboxSettingsManager = mailboxSettingsManager;
state = new MailboxPairingState.QrCodeReceived(payload);
}
@Override
public void addObserver(Consumer<MailboxPairingState> o) {
MailboxPairingState state;
synchronized (lock) {
observers.add(o);
state = this.state;
eventExecutor.execute(() -> o.accept(state));
}
}
@Override
public void removeObserver(Consumer<MailboxPairingState> o) {
synchronized (lock) {
observers.remove(o);
}
}
@Override
public void run() {
try {
pairMailbox();
} catch (FormatException e) {
onMailboxError(e, new MailboxPairingState.InvalidQrCode());
} catch (MailboxAlreadyPairedException e) {
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired());
} catch (IOException e) {
onMailboxError(e, new MailboxPairingState.ConnectionError(payload));
} catch (ApiException | DbException e) {
onMailboxError(e, new MailboxPairingState.UnexpectedError(payload));
}
}
private void pairMailbox() throws IOException, ApiException, DbException {
MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
setState(new MailboxPairingState.Pairing(payload));
MailboxAuthToken ownerToken = api.setup(mailboxProperties);
MailboxProperties ownerProperties = new MailboxProperties(
mailboxProperties.getBaseUrl(), ownerToken, true);
long time = clock.currentTimeMillis();
db.transaction(false, txn -> {
mailboxSettingsManager
.setOwnMailboxProperties(txn, ownerProperties);
mailboxSettingsManager.recordSuccessfulConnection(txn, time);
});
setState(new MailboxPairingState.Paired());
}
private void onMailboxError(Exception e, MailboxPairingState state) {
logException(LOG, WARNING, e);
setState(state);
}
private void setState(MailboxPairingState state) {
synchronized (lock) {
this.state = state;
notifyObservers();
}
}
@GuardedBy("lock")
private void notifyObservers() {
List<Consumer<MailboxPairingState>> observers =
new ArrayList<>(this.observers);
MailboxPairingState state = this.state;
eventExecutor.execute(() -> {
for (Consumer<MailboxPairingState> o : observers) o.accept(state);
});
}
private MailboxProperties decodeQrCodePayload(String payload)
throws FormatException {
byte[] bytes = payload.getBytes(ISO_8859_1);
if (bytes.length != 65) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("QR code length is not 65: " + bytes.length);
}
throw new FormatException();
}
int version = bytes[0] & 0xFF;
if (version != VERSION_REQUIRED) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("QR code has not version " + VERSION_REQUIRED +
": " + version);
}
throw new FormatException();
}
LOG.info("QR code is valid");
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
String onionAddress = crypto.encodeOnionAddress(onionPubKey);
String baseUrl = "http://" + onionAddress + ".onion";
byte[] tokenBytes = Arrays.copyOfRange(bytes, 33, 65);
MailboxAuthToken setupToken = new MailboxAuthToken(tokenBytes);
return new MailboxProperties(baseUrl, setupToken, true);
}
}

View File

@@ -1,116 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.mailbox.MailboxStatus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@Immutable
@NotNullByDefault
class MailboxSettingsManagerImpl implements MailboxSettingsManager {
// Package access for testing
static final String SETTINGS_NAMESPACE = "mailbox";
static final String SETTINGS_KEY_ONION = "onion";
static final String SETTINGS_KEY_TOKEN = "token";
static final String SETTINGS_KEY_LAST_ATTEMPT = "lastAttempt";
static final String SETTINGS_KEY_LAST_SUCCESS = "lastSuccess";
static final String SETTINGS_KEY_ATTEMPTS = "attempts";
static final String SETTINGS_UPLOADS_NAMESPACE = "mailbox-uploads";
private final SettingsManager settingsManager;
@Inject
MailboxSettingsManagerImpl(SettingsManager settingsManager) {
this.settingsManager = settingsManager;
}
@Override
public MailboxProperties getOwnMailboxProperties(Transaction txn)
throws DbException {
Settings s = settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
String onion = s.get(SETTINGS_KEY_ONION);
String token = s.get(SETTINGS_KEY_TOKEN);
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return null;
try {
MailboxAuthToken tokenId = MailboxAuthToken.fromString(token);
return new MailboxProperties(onion, tokenId, true);
} catch (InvalidMailboxIdException e) {
throw new DbException(e);
}
}
@Override
public void setOwnMailboxProperties(Transaction txn, MailboxProperties p)
throws DbException {
Settings s = new Settings();
s.put(SETTINGS_KEY_ONION, p.getBaseUrl());
s.put(SETTINGS_KEY_TOKEN, p.getAuthToken().toString());
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
}
@Override
public MailboxStatus getOwnMailboxStatus(Transaction txn)
throws DbException {
Settings s = settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
long lastAttempt = s.getLong(SETTINGS_KEY_LAST_ATTEMPT, -1);
long lastSuccess = s.getLong(SETTINGS_KEY_LAST_SUCCESS, -1);
int attempts = s.getInt(SETTINGS_KEY_ATTEMPTS, 0);
return new MailboxStatus(lastAttempt, lastSuccess, attempts);
}
@Override
public void recordSuccessfulConnection(Transaction txn, long now)
throws DbException {
Settings s = new Settings();
s.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
s.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
s.putInt(SETTINGS_KEY_ATTEMPTS, 0);
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
}
@Override
public void recordFailedConnectionAttempt(Transaction txn, long now)
throws DbException {
Settings oldSettings =
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
int attempts = oldSettings.getInt(SETTINGS_KEY_ATTEMPTS, 0);
Settings newSettings = new Settings();
newSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
newSettings.putInt(SETTINGS_KEY_ATTEMPTS, attempts + 1);
settingsManager.mergeSettings(txn, newSettings, SETTINGS_NAMESPACE);
}
@Override
public void setPendingUpload(Transaction txn, ContactId id,
@Nullable String filename) throws DbException {
Settings s = new Settings();
String value = filename == null ? "" : filename;
s.put(String.valueOf(id.getInt()), value);
settingsManager.mergeSettings(txn, s, SETTINGS_UPLOADS_NAMESPACE);
}
@Nullable
@Override
public String getPendingUpload(Transaction txn, ContactId id)
throws DbException {
Settings s =
settingsManager.getSettings(txn, SETTINGS_UPLOADS_NAMESPACE);
String filename = s.get(String.valueOf(id.getInt()));
if (isNullOrEmpty(filename)) return null;
return filename;
}
}

View File

@@ -42,4 +42,10 @@ public class PluginModule {
if (config.shouldPoll()) eventBus.addListener(poller);
return poller;
}
@Provides
@Singleton
TorPorts provideTorPorts() {
return new TorPortsImpl();
}
}

View File

@@ -0,0 +1,12 @@
package org.briarproject.bramble.plugin;
/**
* Interface used for injecting the tor ports.
*/
public interface TorPorts {
int getSocksPort();
int getControlPort();
}

View File

@@ -0,0 +1,28 @@
package org.briarproject.bramble.plugin;
public class TorPortsImpl implements TorPorts {
private static int currentPort = 59050;
private static int nextPort() {
return currentPort++;
}
private int socksPort;
private int controlPort;
public TorPortsImpl() {
socksPort = nextPort();
controlPort = nextPort();
}
@Override
public int getSocksPort() {
return socksPort;
}
@Override
public int getControlPort() {
return controlPort;
}
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.file.RemovableDriveTask;
@Deprecated // We can simply remove tasks when they finish
@NotNullByDefault
interface RemovableDriveTaskRegistry {

View File

@@ -1,71 +1,40 @@
package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.List;
@NotNullByDefault
public interface CircumventionProvider {
// TODO: Create a module for this so it doesn't have to be public
enum BridgeType {
DEFAULT_OBFS4,
NON_DEFAULT_OBFS4,
MEEK
}
public interface CircumventionProvider {
/**
* Countries where Tor is blocked, i.e. vanilla Tor connection won't work.
* <p>
*
* See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
* and https://trac.torproject.org/projects/tor/wiki/doc/OONI/censorshipwiki
*/
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE", "RU"};
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"};
/**
* Countries where obfs4 or meek bridge connections are likely to work.
* Should be a subset of {@link #BLOCKED} and the union of
* {@link #DEFAULT_OBFS4_BRIDGES}, {@link #NON_DEFAULT_OBFS4_BRIDGES} and
* {@link #MEEK_BRIDGES}.
* Should be a subset of {@link #BLOCKED}.
*/
String[] BRIDGES = {"CN", "IR", "EG", "BY", "TR", "SY", "VE", "RU"};
/**
* Countries where default obfs4 bridges are likely to work.
* Should be a subset of {@link #BRIDGES}.
*/
String[] DEFAULT_OBFS4_BRIDGES = {"EG", "BY", "TR", "SY", "VE"};
/**
* Countries where non-default obfs4 bridges are likely to work.
* Should be a subset of {@link #BRIDGES}.
*/
String[] NON_DEFAULT_OBFS4_BRIDGES = {"RU"};
String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" };
/**
* Countries where obfs4 bridges won't work and meek is needed.
* Should be a subset of {@link #BRIDGES}.
*/
String[] MEEK_BRIDGES = {"CN", "IR"};
String[] NEEDS_MEEK = {"CN", "IR"};
/**
* Returns true if vanilla Tor connections are blocked in the given country.
*/
boolean isTorProbablyBlocked(String countryCode);
/**
* Returns true if bridge connections of some type work in the given
* country.
*/
boolean doBridgesWork(String countryCode);
/**
* Returns the best type of bridge connection for the given country, or
* {@link #DEFAULT_OBFS4_BRIDGES} if no bridge type is known to work.
*/
BridgeType getBestBridgeType(String countryCode);
boolean needsMeek(String countryCode);
@IoExecutor
List<String> getBridges(BridgeType type);
List<String> getBridges(boolean meek);
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.InputStream;
import java.util.ArrayList;
@@ -10,31 +9,24 @@ import java.util.List;
import java.util.Scanner;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.Arrays.asList;
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;
@Immutable
@NotNullByDefault
class CircumventionProviderImpl implements CircumventionProvider {
private final static String BRIDGE_FILE_NAME = "bridges";
private static final Set<String> BLOCKED_IN_COUNTRIES =
new HashSet<>(asList(BLOCKED));
private static final Set<String> BRIDGE_COUNTRIES =
private static final Set<String> BRIDGES_WORK_IN_COUNTRIES =
new HashSet<>(asList(BRIDGES));
private static final Set<String> DEFAULT_OBFS4_BRIDGE_COUNTRIES =
new HashSet<>(asList(DEFAULT_OBFS4_BRIDGES));
private static final Set<String> NON_DEFAULT_OBFS4_BRIDGE_COUNTRIES =
new HashSet<>(asList(NON_DEFAULT_OBFS4_BRIDGES));
private static final Set<String> MEEK_COUNTRIES =
new HashSet<>(asList(MEEK_BRIDGES));
private static final Set<String> BRIDGES_NEED_MEEK =
new HashSet<>(asList(NEEDS_MEEK));
@Nullable
private volatile List<String> bridges = null;
@Inject
CircumventionProviderImpl() {
@@ -47,42 +39,33 @@ class CircumventionProviderImpl implements CircumventionProvider {
@Override
public boolean doBridgesWork(String countryCode) {
return BRIDGE_COUNTRIES.contains(countryCode);
return BRIDGES_WORK_IN_COUNTRIES.contains(countryCode);
}
@Override
public BridgeType getBestBridgeType(String countryCode) {
if (DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) {
return DEFAULT_OBFS4;
} else if (NON_DEFAULT_OBFS4_BRIDGE_COUNTRIES.contains(countryCode)) {
return NON_DEFAULT_OBFS4;
} else if (MEEK_COUNTRIES.contains(countryCode)) {
return MEEK;
} else {
return DEFAULT_OBFS4;
}
public boolean needsMeek(String countryCode) {
return BRIDGES_NEED_MEEK.contains(countryCode);
}
@Override
@IoExecutor
public List<String> getBridges(BridgeType type) {
InputStream is = requireNonNull(getClass().getClassLoader()
.getResourceAsStream(BRIDGE_FILE_NAME));
public List<String> getBridges(boolean useMeek) {
List<String> bridges = this.bridges;
if (bridges != null) return new ArrayList<>(bridges);
InputStream is = getClass().getClassLoader()
.getResourceAsStream(BRIDGE_FILE_NAME);
Scanner scanner = new Scanner(is);
List<String> bridges = new ArrayList<>();
bridges = new ArrayList<>();
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
boolean isDefaultObfs4 = line.startsWith("d ");
boolean isNonDefaultObfs4 = line.startsWith("n ");
boolean isMeek = line.startsWith("m ");
if ((type == DEFAULT_OBFS4 && isDefaultObfs4) ||
(type == NON_DEFAULT_OBFS4 && isNonDefaultObfs4) ||
(type == MEEK && isMeek)) {
bridges.add(line.substring(2));
}
boolean isMeekBridge = line.startsWith("Bridge meek");
if (useMeek && !isMeekBridge || !useMeek && isMeekBridge) continue;
if (!line.startsWith("#")) bridges.add(line);
}
scanner.close();
this.bridges = bridges;
return bridges;
}

View File

@@ -33,7 +33,7 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
import org.briarproject.bramble.plugin.TorPorts;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
@@ -46,7 +46,6 @@ import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -71,6 +70,7 @@ import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
@@ -80,7 +80,9 @@ import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_PLUG
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_V3_CREATED;
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
@@ -89,11 +91,12 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
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.api.plugin.TorConstants.V3_MIGRATION_PERIOD_MS;
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;
@@ -113,6 +116,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final String OWNER = "__OwningControllerProcess";
private static final int COOKIE_TIMEOUT_MS = 3000;
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}");
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
private final Executor ioExecutor, wakefulIoExecutor;
@@ -120,6 +124,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final NetworkManager networkManager;
private final LocationUtils locationUtils;
private final SocketFactory torSocketFactory;
private final TorPorts torPorts;
private final Clock clock;
private final BatteryManager batteryManager;
private final Backoff backoff;
@@ -129,11 +134,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final CircumventionProvider circumventionProvider;
private final ResourceProvider resourceProvider;
private final long maxLatency;
private final int maxIdleTime;
private final int socketTimeout;
private final int maxIdleTime, socketTimeout;
private final File torDirectory, geoIpFile, configFile;
private final int torSocksPort;
private final int torControlPort;
private final File doneFile, cookieFile;
private final AtomicBoolean used = new AtomicBoolean(false);
@@ -152,6 +154,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
TorPorts torPorts,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
@@ -162,14 +165,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
String architecture,
long maxLatency,
int maxIdleTime,
File torDirectory,
int torSocksPort,
int torControlPort) {
File torDirectory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager;
this.locationUtils = locationUtils;
this.torSocketFactory = torSocketFactory;
this.torPorts = torPorts;
this.clock = clock;
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
@@ -184,8 +186,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2;
this.torDirectory = torDirectory;
this.torSocksPort = torSocksPort;
this.torControlPort = torControlPort;
geoIpFile = new File(torDirectory, "geoip");
configFile = new File(torDirectory, "torrc");
doneFile = new File(torDirectory, "done");
@@ -281,7 +281,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) listFiles(torDirectory);
throw new PluginException();
}
//noinspection BusyWait
Thread.sleep(COOKIE_POLLING_INTERVAL_MS);
}
LOG.info("Auth cookie created");
@@ -292,7 +291,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
try {
// Open a control connection and authenticate using the cookie file
controlSocket = new Socket("127.0.0.1", torControlPort);
controlSocket = new Socket("127.0.0.1", torPorts.getControlPort());
controlConnection = new TorControlConnection(controlSocket);
controlConnection.authenticate(read(cookieFile));
// Tell Tor to exit when the control connection is closed
@@ -395,6 +394,18 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return zin;
}
private InputStream getTorrc() {
StringBuilder strb = new StringBuilder();
append(strb, "ControlPort", torPorts.getControlPort());
append(strb, "CookieAuthentication", 1);
append(strb, "DisableNetwork", 1);
append(strb, "RunAsDaemon", 1);
append(strb, "SafeSocks", 1);
append(strb, "SocksPort", torPorts.getSocksPort());
return new ByteArrayInputStream(strb.toString().getBytes());
}
private static void append(StringBuilder strb, String name, int value) {
strb.append(name);
strb.append(" ");
@@ -403,16 +414,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
private InputStream getConfigInputStream() {
StringBuilder strb = new StringBuilder();
append(strb, "ControlPort", torControlPort);
append(strb, "CookieAuthentication", 1);
append(strb, "DisableNetwork", 1);
append(strb, "RunAsDaemon", 1);
append(strb, "SafeSocks", 1);
append(strb, "SocksPort", torSocksPort);
//noinspection CharsetObjectCanBeUsed
return new ByteArrayInputStream(
strb.toString().getBytes(Charset.forName("UTF-8")));
ClassLoader cl = getClass().getClassLoader();
return requireNonNull(cl.getResourceAsStream("torrc"));
}
private void listFiles(File f) {
@@ -477,10 +480,54 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private void publishHiddenService(String port) {
if (!state.isTorRunning()) return;
// TODO: Remove support for v2 hidden services after a reasonable
// migration period (migration started 2020-06-30)
String privKey2 = settings.get(HS_PRIVATE_KEY_V2);
String privKey3 = settings.get(HS_PRIVATE_KEY_V3);
String v3Created = settings.get(HS_V3_CREATED);
// Publish a v2 hidden service if we've already created one, and
// either we've never published a v3 hidden service or we're still
// in the migration period since first publishing it
if (!isNullOrEmpty(privKey2)) {
long now = clock.currentTimeMillis();
long then = v3Created == null ? now : Long.parseLong(v3Created);
if (now - then >= V3_MIGRATION_PERIOD_MS) retireV2HiddenService();
else publishV2HiddenService(port, privKey2);
}
publishV3HiddenService(port, privKey3);
}
private void publishV2HiddenService(String port, String privKey) {
LOG.info("Creating v2 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
Map<String, String> response;
try {
response = controlConnection.addOnion(privKey, portLines);
} catch (IOException e) {
logException(LOG, WARNING, e);
return;
}
if (!response.containsKey(HS_ADDRESS)) {
LOG.warning("Tor did not return a hidden service address");
return;
}
String onion2 = response.get(HS_ADDRESS);
if (LOG.isLoggable(INFO)) {
LOG.info("V2 hidden service " + scrubOnion(onion2));
}
// The hostname has already been published and the private key stored
}
private void retireV2HiddenService() {
LOG.info("Retiring v2 hidden service");
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V2, "");
callback.mergeLocalProperties(p);
Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V2, "");
callback.mergeSettings(s);
}
private void publishV3HiddenService(String port, @Nullable String privKey) {
LOG.info("Creating v3 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
@@ -517,6 +564,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// Save the hidden service's private key for next time
Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY));
s.put(HS_V3_CREATED, String.valueOf(clock.currentTimeMillis()));
callback.mergeSettings(s);
}
}
@@ -544,20 +592,20 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
controlConnection.setConf("DisableNetwork", enable ? "0" : "1");
}
private void enableBridges(boolean enable, BridgeType bridgeType)
private void enableBridges(boolean enable, boolean needsMeek)
throws IOException {
if (enable) {
Collection<String> conf = new ArrayList<>();
conf.add("UseBridges 1");
File obfs4File = getObfs4ExecutableFile();
if (bridgeType == MEEK) {
if (needsMeek) {
conf.add("ClientTransportPlugin meek_lite exec " +
obfs4File.getAbsolutePath());
} else {
conf.add("ClientTransportPlugin obfs4 exec " +
obfs4File.getAbsolutePath());
}
conf.addAll(circumventionProvider.getBridges(bridgeType));
conf.addAll(circumventionProvider.getBridges(needsMeek));
controlConnection.setConf(conf);
} else {
controlConnection.setConf("UseBridges", "0");
@@ -623,30 +671,49 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public DuplexTransportConnection createConnection(TransportProperties p) {
if (getState() != ACTIVE) return null;
// TODO: Remove support for v2 hidden services after a reasonable
// migration period (migration started 2020-06-30)
String bestOnion = null, version = null;
String onion2 = p.get(PROP_ONION_V2);
String onion3 = p.get(PROP_ONION_V3);
if (onion3 != null && !ONION_V3.matcher(onion3).matches()) {
// Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO)) {
LOG.info("Invalid v3 hostname: " + onion3);
if (!isNullOrEmpty(onion2)) {
if (ONION_V2.matcher(onion2).matches()) {
bestOnion = onion2;
version = "v2";
} else {
// Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO))
LOG.info("Invalid v2 hostname: " + onion2);
}
onion3 = null;
}
if (onion3 == null) return null;
if (!isNullOrEmpty(onion3)) {
if (ONION_V3.matcher(onion3).matches()) {
bestOnion = onion3;
version = "v3";
} else {
// Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO))
LOG.info("Invalid v3 hostname: " + onion3);
}
}
if (bestOnion == null) return null;
Socket s = null;
try {
if (LOG.isLoggable(INFO)) {
LOG.info("Connecting to v3 " + scrubOnion(onion3));
LOG.info("Connecting to " + version + " "
+ scrubOnion(bestOnion));
}
s = torSocketFactory.createSocket(onion3 + ".onion", 80);
s = torSocketFactory.createSocket(bestOnion + ".onion", 80);
s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) {
LOG.info("Connected to v3 " + scrubOnion(onion3));
LOG.info("Connected to " + version + " "
+ scrubOnion(bestOnion));
}
return new TorTransportConnection(this, s);
} catch (IOException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to v3 "
+ scrubOnion(onion3) + ": " + e.toString());
LOG.info("Could not connect to " + version + " "
+ scrubOnion(bestOnion) + ": " + e.toString());
}
tryToClose(s, LOG, WARNING);
return null;
@@ -846,9 +913,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
int reasonsDisabled = 0;
boolean enableNetwork = false, enableBridges = false;
boolean enableConnectionPadding = false;
BridgeType bridgeType =
circumventionProvider.getBestBridgeType(country);
boolean useMeek = false, enableConnectionPadding = false;
if (!online) {
LOG.info("Disabling network, device is offline");
@@ -877,10 +942,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
enableNetwork = true;
if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
(automatic && bridgesWork)) {
if (ipv6Only) bridgeType = MEEK;
enableBridges = true;
if (LOG.isLoggable(INFO)) {
LOG.info("Using bridge type " + bridgeType);
if (ipv6Only ||
circumventionProvider.needsMeek(country)) {
LOG.info("Using meek bridges");
enableBridges = true;
useMeek = true;
} else {
LOG.info("Using obfs4 bridges");
enableBridges = true;
}
} else {
LOG.info("Not using bridges");
@@ -898,7 +967,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
try {
if (enableNetwork) {
enableBridges(enableBridges, bridgeType);
enableBridges(enableBridges, useMeek);
enableConnectionPadding(enableConnectionPadding);
useIpv6(ipv6Only);
}
@@ -938,17 +1007,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Nullable
private ServerSocket serverSocket = null;
private synchronized void setStarted() {
synchronized void setStarted() {
started = true;
callback.pluginStateChanged(getState());
}
private synchronized boolean isTorRunning() {
synchronized boolean isTorRunning() {
return started && !stopped;
}
@Nullable
private synchronized ServerSocket setStopped() {
synchronized ServerSocket setStopped() {
stopped = true;
ServerSocket ss = serverSocket;
serverSocket = null;
@@ -956,44 +1025,44 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return ss;
}
private synchronized void setBootstrapped() {
synchronized void setBootstrapped() {
bootstrapped = true;
callback.pluginStateChanged(getState());
}
private synchronized boolean getAndSetCircuitBuilt() {
synchronized boolean getAndSetCircuitBuilt() {
boolean firstCircuit = !circuitBuilt;
circuitBuilt = true;
callback.pluginStateChanged(getState());
return firstCircuit;
}
private synchronized void enableNetwork(boolean enable) {
synchronized void enableNetwork(boolean enable) {
networkInitialised = true;
networkEnabled = enable;
if (!enable) circuitBuilt = false;
callback.pluginStateChanged(getState());
}
private synchronized void setReasonsDisabled(int reasonsDisabled) {
synchronized void setReasonsDisabled(int reasonsDisabled) {
settingsChecked = true;
this.reasonsDisabled = reasonsDisabled;
callback.pluginStateChanged(getState());
}
// Doesn't affect getState()
private synchronized boolean setServerSocket(ServerSocket ss) {
synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false;
serverSocket = ss;
return true;
}
// Doesn't affect getState()
private synchronized void clearServerSocket(ServerSocket ss) {
synchronized void clearServerSocket(ServerSocket ss) {
if (serverSocket == ss) serverSocket = null;
}
private synchronized State getState() {
synchronized State getState() {
if (!started || stopped || !settingsChecked) {
return STARTING_STOPPING;
}
@@ -1003,7 +1072,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return bootstrapped && circuitBuilt ? ACTIVE : ENABLING;
}
private synchronized int getReasonsDisabled() {
synchronized int getReasonsDisabled() {
return getState() == DISABLED ? reasonsDisabled : 0;
}
}

View File

@@ -4,26 +4,39 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.bouncycastle.util.encoders.Base64;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.util.Base32;
import java.nio.charset.Charset;
class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
import static java.lang.System.arraycopy;
public class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
private static final EdDSANamedCurveSpec CURVE_SPEC =
EdDSANamedCurveTable.getByName("Ed25519");
private final CryptoComponent crypto;
TorRendezvousCryptoImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
private static final byte HS_PROTOCOL_VERSION = 3;
private static final int CHECKSUM_BYTES = 2;
@Override
public String getOnionAddress(byte[] seed) {
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
return crypto.encodeOnionAddress(spec.getA().toByteArray());
byte[] publicKey = spec.getA().toByteArray();
Digest digest = new SHA3Digest(256);
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
digest.update(label, 0, label.length);
digest.update(publicKey, 0, publicKey.length);
digest.update(HS_PROTOCOL_VERSION);
byte[] checksum = new byte[digest.getDigestSize()];
digest.doFinal(checksum, 0);
byte[] address = new byte[publicKey.length + CHECKSUM_BYTES + 1];
arraycopy(publicKey, 0, address, 0, publicKey.length);
arraycopy(checksum, 0, address, publicKey.length, CHECKSUM_BYTES);
address[address.length - 1] = HS_PROTOCOL_VERSION;
return Base32.encode(address).toLowerCase();
}
@Override

View File

@@ -37,10 +37,4 @@ class SettingsManagerImpl implements SettingsManager {
public void mergeSettings(Settings s, String namespace) throws DbException {
db.transaction(false, txn -> db.mergeSettings(txn, s, namespace));
}
@Override
public void mergeSettings(Transaction txn, Settings s, String namespace)
throws DbException {
db.mergeSettings(txn, s, namespace);
}
}

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.socks;
import org.briarproject.bramble.api.plugin.TorSocksPort;
import org.briarproject.bramble.plugin.TorPorts;
import java.net.InetSocketAddress;
@@ -16,9 +16,9 @@ import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_SOCKET_TIME
public class SocksModule {
@Provides
SocketFactory provideTorSocketFactory(@TorSocksPort int torSocksPort) {
SocketFactory provideTorSocketFactory(TorPorts torPorts) {
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
torSocksPort);
torPorts.getSocksPort());
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT,
EXTRA_SOCKET_TIMEOUT);
}

View File

@@ -215,23 +215,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
m.getStreamContext(txn, tag)));
}
@Override
public StreamContext getStreamContextOnly(TransportId t, byte[] tag)
throws DbException {
return withManager(t, m ->
db.transactionWithNullableResult(false, txn ->
m.getStreamContextOnly(txn, tag)));
}
@Override
public void markTagAsRecognised(TransportId t, byte[] tag)
throws DbException {
withManager(t, m -> {
db.transaction(false, txn -> m.markTagAsRecognised(txn, tag));
return null;
});
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) {

View File

@@ -48,9 +48,4 @@ interface TransportKeyManager {
StreamContext getStreamContext(Transaction txn, byte[] tag)
throws DbException;
@Nullable
StreamContext getStreamContextOnly(Transaction txn, byte[] tag);
void markTagAsRecognised(Transaction txn, byte[] tag) throws DbException;
}

View File

@@ -393,82 +393,56 @@ class TransportKeyManagerImpl implements TransportKeyManager {
throws DbException {
lock.lock();
try {
StreamContext ctx = streamContextFromTag(tag);
if (ctx == null) return null;
markTagAsRecognised(txn, tag);
// Look up the incoming keys for the tag
TagContext tagCtx = inContexts.remove(new Bytes(tag));
if (tagCtx == null) return null;
MutableIncomingKeys inKeys = tagCtx.inKeys;
// Create a stream context
StreamContext ctx = new StreamContext(tagCtx.contactId,
tagCtx.pendingContactId, transportId,
inKeys.getTagKey(), inKeys.getHeaderKey(),
tagCtx.streamNumber, tagCtx.handshakeMode);
// Update the reordering window
ReorderingWindow window = inKeys.getWindow();
Change change = window.setSeen(tagCtx.streamNumber);
// Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
tagCtx.contactId, tagCtx.pendingContactId, inKeys,
streamNumber, tagCtx.handshakeMode);
inContexts.put(new Bytes(addTag), tagCtx1);
}
// Remove tags for any stream numbers removed from the window
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
inContexts.remove(new Bytes(removeTag));
}
// Write the window back to the DB
db.setReorderingWindow(txn, tagCtx.keySetId, transportId,
inKeys.getTimePeriod(), window.getBase(),
window.getBitmap());
// If the outgoing keys are inactive, activate them
MutableTransportKeySet ks = keys.get(tagCtx.keySetId);
MutableOutgoingKeys outKeys =
ks.getKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) {
LOG.info("Activating outgoing keys");
outKeys.activate();
considerReplacingOutgoingKeys(ks);
db.setTransportKeysActive(txn, transportId, tagCtx.keySetId);
}
return ctx;
} finally {
lock.unlock();
}
}
@Override
public StreamContext getStreamContextOnly(Transaction txn, byte[] tag) {
lock.lock();
try {
return streamContextFromTag(tag);
} finally {
lock.unlock();
}
}
@GuardedBy("lock")
@Nullable
private StreamContext streamContextFromTag(byte[] tag) {
// Look up the incoming keys for the tag
TagContext tagCtx = inContexts.get(new Bytes(tag));
if (tagCtx == null) return null;
MutableIncomingKeys inKeys = tagCtx.inKeys;
// Create a stream context
return new StreamContext(tagCtx.contactId,
tagCtx.pendingContactId, transportId,
inKeys.getTagKey(), inKeys.getHeaderKey(),
tagCtx.streamNumber, tagCtx.handshakeMode);
}
@Override
public void markTagAsRecognised(Transaction txn, byte[] tag)
throws DbException {
TagContext tagCtx = inContexts.remove(new Bytes(tag));
if (tagCtx == null) return;
MutableIncomingKeys inKeys = tagCtx.inKeys;
// Update the reordering window
ReorderingWindow window = inKeys.getWindow();
Change change = window.setSeen(tagCtx.streamNumber);
// Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
tagCtx.contactId, tagCtx.pendingContactId, inKeys,
streamNumber, tagCtx.handshakeMode);
inContexts.put(new Bytes(addTag), tagCtx1);
}
// Remove tags for any stream numbers removed from the window
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
inContexts.remove(new Bytes(removeTag));
}
// Write the window back to the DB
db.setReorderingWindow(txn, tagCtx.keySetId, transportId,
inKeys.getTimePeriod(), window.getBase(),
window.getBitmap());
// If the outgoing keys are inactive, activate them
MutableTransportKeySet ks = keys.get(tagCtx.keySetId);
MutableOutgoingKeys outKeys =
ks.getKeys().getCurrentOutgoingKeys();
if (!outKeys.isActive()) {
LOG.info("Activating outgoing keys");
outKeys.activate();
considerReplacingOutgoingKeys(ks);
db.setTransportKeysActive(txn, transportId, tagCtx.keySetId);
}
}
@DatabaseExecutor
@Wakeful
private void updateKeys(Transaction txn) throws DbException {

View File

@@ -1,20 +1,10 @@
d Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
d Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
d Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0
d Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
d Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
d Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
d Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
d Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
d Bridge obfs4 185.100.85.3:443 5B403DFE34F4872EB027059CECAE30B0C864B3A2 cert=bWUdFUe8io9U6JkSLoGAvSAUDcB779/shovCYmYAQb/pW/iEAMZtO/lCd94OokOF909TPA iat-mode=2
n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cert=t8tA9q2AeGlmp/dO6oW9bkY5RqqmvqjArCEM9wjJoDnk6XtnaejkF0JTA7VamdyOzcvuBg iat-mode=0
n Bridge obfs4 23.88.49.56:443 1CDA1660823AE2565D7F50DE8EB99DFDDE96074B cert=4bwNXedHutVD0ZqCm6ph90Vik9dRY4n9qnBHiLiqQOSsIvui4iHwuMFQK6oqiK8tyhVcDw iat-mode=0
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0
n Bridge obfs4 185.161.70.200:9003 7A81D0CD19870DFA3FD13C5DE232D8ADD026DC40 cert=aIcWxyZS3JcWsowlD9hcw9fttA44Bq/W2laFjVWlhuXqrIlAAwrXvq1O9lm9XrkV8GG/ZA iat-mode=0
n Bridge obfs4 172.105.22.69:80 CBD17B33192A879433AB37C9E142541BD3459ABD cert=rk5YmpKypLsjlS4tjkYaZNBweYMa5tWQRhZ8Q2WRleNOgrhSceKo59BA8kp6kVfaMPXnSw iat-mode=0
n Bridge obfs4 46.128.93.192:7346 5D28B8E1D117B8720D56A8513CF32509DCA1D84F cert=ED6tZP50eF0vno09F5gFvoWTMdcWFEX2FtwXOUYRevjzKg30/y701f61Vycnh6HO9gkaMw iat-mode=0
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0
Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=ssH+9rP8dG2NLDN2XuFw63hIO/9MNNinLmxQDpVa+7kTOa9/m+tGWT1SmSYpQ9uTBGa6Hw iat-mode=0
Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3c0SwxpOisbohNxEc4tb875RVW8eOu1opRTVXJhafaKA/PNNtI7ElQIVOVZg1AdL5bxGCw iat-mode=0
Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com

View File

@@ -0,0 +1,6 @@
ControlPort 59051
CookieAuthentication 1
DisableNetwork 1
RunAsDaemon 1
SafeSocks 1
SocksPort 59050

View File

@@ -1,21 +0,0 @@
#!/bin/bash
set -e
URL="http://127.0.0.1:8000/status"
attempt_counter=0
max_attempts=200 # 10min - CI for mailbox currently takes ~5min
echo "Waiting for mailbox to come online at $URL"
until [[ "$(curl -s -o /dev/null -w '%{http_code}' $URL)" == "401" ]]; do
if [ ${attempt_counter} -eq ${max_attempts} ]; then
echo "Timed out waiting for mailbox"
exit 1
fi
printf '.'
attempt_counter=$((attempt_counter + 1))
sleep 3
done
echo "Mailbox started"

View File

@@ -13,7 +13,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.test.ValidatorTestCase;
import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
@@ -38,7 +38,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
private final Metadata meta = new Metadata();
public BdfMessageValidatorTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
context.setImposteriser(ClassImposteriser.INSTANCE);
}
@Test(expected = InvalidMessageException.class)

View File

@@ -24,10 +24,11 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -52,8 +53,9 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class ClientHelperImplTest extends BrambleMockTestCase {
public class ClientHelperImplTest extends BrambleTestCase {
private final Mockery context = new Mockery();
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final MessageFactory messageFactory =
context.mock(MessageFactory.class);
@@ -98,6 +100,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
clientHelper.addLocalMessage(message, dictionary, shared);
context.assertIsSatisfied();
}
@Test
@@ -109,6 +112,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
clientHelper.createMessage(groupId, timestamp, list);
context.assertIsSatisfied();
}
@Test
@@ -123,6 +127,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
clientHelper.getMessageAsList(messageId);
context.assertIsSatisfied();
}
@Test
@@ -139,6 +144,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
assertEquals(dictionary,
clientHelper.getGroupMetadataAsDictionary(groupId));
context.assertIsSatisfied();
}
@Test
@@ -155,6 +161,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
assertEquals(dictionary,
clientHelper.getMessageMetadataAsDictionary(messageId));
context.assertIsSatisfied();
}
@Test
@@ -172,6 +179,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
assertEquals(map, clientHelper.getMessageMetadataAsDictionary(groupId));
context.assertIsSatisfied();
}
@Test
@@ -196,6 +204,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
assertEquals(map,
clientHelper.getMessageMetadataAsDictionary(groupId, query));
context.assertIsSatisfied();
}
@Test
@@ -210,6 +219,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
clientHelper.mergeGroupMetadata(groupId, dictionary);
context.assertIsSatisfied();
}
@Test
@@ -224,6 +234,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
clientHelper.mergeMessageMetadata(messageId, dictionary);
context.assertIsSatisfied();
}
@Test
@@ -231,6 +242,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
byte[] bytes = expectToByteArray(list);
assertArrayEquals(bytes, clientHelper.toByteArray(list));
context.assertIsSatisfied();
}
@Test
@@ -238,6 +250,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
expectToList(true);
assertEquals(list, clientHelper.toList(getRandomBytes(123)));
context.assertIsSatisfied();
}
@Test
@@ -249,6 +262,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
fail();
} catch (FormatException e) {
// expected
context.assertIsSatisfied();
}
}
@@ -265,6 +279,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
assertArrayEquals(signature,
clientHelper.sign(label, list, privateKey));
context.assertIsSatisfied();
}
@Test
@@ -280,6 +295,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
}});
clientHelper.verifySignature(signature, label, list, publicKey);
context.assertIsSatisfied();
}
@Test
@@ -299,6 +315,7 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
fail();
} catch (GeneralSecurityException e) {
// expected
context.assertIsSatisfied();
}
}

View File

@@ -2197,55 +2197,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close();
}
@Test
public void testResetRetransmissionTimes() throws Exception {
long now = System.currentTimeMillis();
AtomicLong time = new AtomicLong(now);
Database<Connection> db =
open(false, new TestMessageFactory(), new SettableClock(time));
Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message
db.addIdentity(txn, identity);
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), null, true));
db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, false, null);
// Time: now
// Retrieve the message from the database
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
ONE_MEGABYTE, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
// Time: now
// Mark the message as sent
db.updateExpiryTimeAndEta(txn, contactId, messageId, MAX_LATENCY);
// The message should expire after 2 * MAX_LATENCY
assertEquals(now + MAX_LATENCY * 2, db.getNextSendTime(txn, contactId));
// Time: now + MAX_LATENCY * 2 - 1
// The message should not yet be sendable
time.set(now + MAX_LATENCY * 2 - 1);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
assertTrue(ids.isEmpty());
// Reset the retransmission times
db.resetUnackedMessagesToSend(txn, contactId);
// The message should have infinitely short expiry
assertEquals(0, db.getNextSendTime(txn, contactId));
// The message should be sendable
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
assertFalse(ids.isEmpty());
db.commitTransaction(txn);
db.close();
}
@Test
public void testCompactionTime() throws Exception {
MessageFactory messageFactory = new TestMessageFactory();

View File

@@ -11,9 +11,8 @@ import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations;
import org.jmock.auto.Mock;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.integration.junit4.JUnitRuleMockery;
import org.jmock.lib.concurrent.Synchroniser;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Rule;
import org.junit.Test;
@@ -35,8 +34,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Rule
public JUnitRuleMockery context = new JUnitRuleMockery() {{
// So we can mock concrete classes like KeyAgreementTransport
setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
setThreadingPolicy(new Synchroniser());
setImposteriser(ClassImposteriser.INSTANCE);
}};
private final PublicKey alicePubKey = getAgreementPublicKey();

View File

@@ -14,7 +14,7 @@ import org.briarproject.bramble.api.record.RecordWriterFactory;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction;
import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
import java.io.InputStream;
@@ -58,7 +58,7 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
private KeyAgreementTransport kat;
public KeyAgreementTransportTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
context.setImposteriser(ClassImposteriser.INSTANCE);
inputStream = context.mock(InputStream.class);
outputStream = context.mock(OutputStream.class);
}

View File

@@ -1,802 +0,0 @@
package org.briarproject.bramble.mailbox;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.briarproject.bramble.api.WeakSingletonProvider;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxFileId;
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
import org.briarproject.bramble.api.mailbox.MailboxId;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import javax.net.SocketFactory;
import okhttp3.OkHttpClient;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.readBytes;
import static org.briarproject.bramble.test.TestUtils.writeBytes;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class MailboxApiTest extends BrambleTestCase {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
private final OkHttpClient client = new OkHttpClient.Builder()
.socketFactory(SocketFactory.getDefault())
.connectTimeout(60_000, MILLISECONDS)
.build();
private final WeakSingletonProvider<OkHttpClient> httpClientProvider =
new WeakSingletonProvider<OkHttpClient>() {
@Override
@Nonnull
public OkHttpClient createInstance() {
return client;
}
};
private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider);
private final MailboxAuthToken token = new MailboxAuthToken(getRandomId());
private final MailboxAuthToken token2 = new MailboxAuthToken(getRandomId());
private final ContactId contactId = getContactId();
private final MailboxAuthToken contactToken =
new MailboxAuthToken(getRandomId());
private final MailboxFolderId contactInboxId =
new MailboxFolderId(getRandomId());
private final MailboxFolderId contactOutboxId =
new MailboxFolderId(getRandomId());
private final MailboxContact mailboxContact = new MailboxContact(
contactId, contactToken, contactInboxId, contactOutboxId);
@Test
public void testSetup() throws Exception {
String validResponse = "{\"token\":\"" + token2 + "\"}";
String invalidResponse = "{\"foo\":\"bar\"}";
String invalidTokenResponse = "{\"token\":{\"foo\":\"bar\"}}";
String invalidTokenResponse2 =
"{\"token\":\"" + getRandomString(64) + "\"}";
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody(validResponse));
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setBody(invalidResponse));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.enqueue(new MockResponse().setBody(invalidTokenResponse));
server.enqueue(new MockResponse().setBody(invalidTokenResponse2));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
MailboxProperties properties2 =
new MailboxProperties(baseUrl, token2, true);
// valid response with valid token
assertEquals(token2, api.setup(properties));
RecordedRequest request1 = server.takeRequest();
assertEquals("/setup", request1.getPath());
assertEquals("PUT", request1.getMethod());
assertToken(request1, token);
// empty body
assertThrows(ApiException.class, () -> api.setup(properties));
RecordedRequest request2 = server.takeRequest();
assertEquals("/setup", request2.getPath());
assertEquals("PUT", request2.getMethod());
assertToken(request2, token);
// invalid response
assertThrows(ApiException.class, () -> api.setup(properties));
RecordedRequest request3 = server.takeRequest();
assertEquals("/setup", request3.getPath());
assertEquals("PUT", request3.getMethod());
assertToken(request3, token);
// 401 response
assertThrows(ApiException.class, () -> api.setup(properties2));
RecordedRequest request4 = server.takeRequest();
assertEquals("/setup", request4.getPath());
assertEquals("PUT", request4.getMethod());
assertToken(request4, token2);
// 500 response
assertThrows(ApiException.class, () -> api.setup(properties));
RecordedRequest request5 = server.takeRequest();
assertEquals("/setup", request5.getPath());
assertEquals("PUT", request5.getMethod());
assertToken(request5, token);
// invalid json dict token response
assertThrows(ApiException.class, () -> api.setup(properties));
RecordedRequest request6 = server.takeRequest();
assertEquals("/setup", request6.getPath());
assertEquals("PUT", request6.getMethod());
assertToken(request6, token);
// invalid non-hex string token response
assertThrows(ApiException.class, () -> api.setup(properties));
RecordedRequest request7 = server.takeRequest();
assertEquals("/setup", request7.getPath());
assertEquals("PUT", request7.getMethod());
assertToken(request7, token);
}
@Test
public void testSetupOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(
IllegalArgumentException.class,
() -> api.setup(properties)
);
}
@Test
public void testStatus() throws Exception {
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
MailboxProperties properties2 =
new MailboxProperties(baseUrl, token2, true);
assertTrue(api.checkStatus(properties));
RecordedRequest request1 = server.takeRequest();
assertEquals("/status", request1.getPath());
assertToken(request1, token);
assertThrows(ApiException.class, () -> api.checkStatus(properties2));
RecordedRequest request2 = server.takeRequest();
assertEquals("/status", request2.getPath());
assertToken(request2, token2);
assertFalse(api.checkStatus(properties));
RecordedRequest request3 = server.takeRequest();
assertEquals("/status", request3.getPath());
assertToken(request3, token);
}
@Test
public void testStatusOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(
IllegalArgumentException.class,
() -> api.checkStatus(properties)
);
}
@Test
public void testWipe() throws Exception {
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setResponseCode(204));
server.enqueue(new MockResponse().setResponseCode(200));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
MailboxProperties properties2 =
new MailboxProperties(baseUrl, token2, true);
api.wipeMailbox(properties);
RecordedRequest request1 = server.takeRequest();
assertEquals("/", request1.getPath());
assertEquals("DELETE", request1.getMethod());
assertToken(request1, token);
assertThrows(ApiException.class, () -> api.wipeMailbox(properties2));
RecordedRequest request2 = server.takeRequest();
assertEquals("/", request2.getPath());
assertEquals("DELETE", request2.getMethod());
assertToken(request2, token2);
assertThrows(ApiException.class, () -> api.wipeMailbox(properties));
RecordedRequest request3 = server.takeRequest();
assertEquals("/", request3.getPath());
assertEquals("DELETE", request3.getMethod());
assertToken(request3, token);
assertThrows(ApiException.class, () -> api.wipeMailbox(properties));
RecordedRequest request4 = server.takeRequest();
assertEquals("/", request4.getPath());
assertEquals("DELETE", request4.getMethod());
assertToken(request4, token);
}
@Test
public void testWipeOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(IllegalArgumentException.class, () ->
api.wipeMailbox(properties));
}
@Test
public void testAddContact() throws Exception {
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(409));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// contact gets added as expected
api.addContact(properties, mailboxContact);
RecordedRequest request1 = server.takeRequest();
assertEquals("/contacts", request1.getPath());
assertToken(request1, token);
String expected = "{\"contactId\":" + contactId.getInt() +
",\"token\":\"" + contactToken +
"\",\"inboxId\":\"" + contactInboxId +
"\",\"outboxId\":\"" + contactOutboxId +
"\"}";
assertEquals(expected, request1.getBody().readUtf8());
// request is not successful
assertThrows(ApiException.class, () ->
api.addContact(properties, mailboxContact));
RecordedRequest request2 = server.takeRequest();
assertEquals("/contacts", request2.getPath());
assertToken(request2, token);
// contact already exists
assertThrows(TolerableFailureException.class, () ->
api.addContact(properties, mailboxContact));
RecordedRequest request3 = server.takeRequest();
assertEquals("/contacts", request3.getPath());
assertToken(request3, token);
}
@Test
public void testAddContactOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(IllegalArgumentException.class, () ->
api.addContact(properties, mailboxContact));
}
@Test
public void testDeleteContact() throws Exception {
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setResponseCode(205));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(404));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// contact gets deleted as expected
api.deleteContact(properties, contactId);
RecordedRequest request1 = server.takeRequest();
assertEquals("DELETE", request1.getMethod());
assertEquals("/contacts/" + contactId.getInt(), request1.getPath());
assertToken(request1, token);
// request is not returning 200
assertThrows(ApiException.class, () ->
api.deleteContact(properties, contactId));
RecordedRequest request2 = server.takeRequest();
assertEquals("DELETE", request2.getMethod());
assertEquals("/contacts/" + contactId.getInt(), request2.getPath());
assertToken(request2, token);
// request is not authorized
assertThrows(ApiException.class, () ->
api.deleteContact(properties, contactId));
RecordedRequest request3 = server.takeRequest();
assertEquals("DELETE", request3.getMethod());
assertEquals("/contacts/" + contactId.getInt(), request3.getPath());
assertToken(request3, token);
// tolerable 404 not found error
assertThrows(TolerableFailureException.class,
() -> api.deleteContact(properties, contactId));
RecordedRequest request4 = server.takeRequest();
assertEquals("/contacts/" + contactId.getInt(), request4.getPath());
assertEquals("DELETE", request4.getMethod());
assertToken(request4, token);
}
@Test
public void testDeleteContactOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(IllegalArgumentException.class, () ->
api.deleteContact(properties, contactId));
}
@Test
public void testGetContacts() throws Exception {
ContactId contactId2 = getContactId();
String validResponse1 = "{\"contacts\": [" + contactId.getInt() + "] }";
String validResponse2 = "{\"contacts\": [" + contactId.getInt() + ", " +
contactId2.getInt() + "] }";
String invalidResponse1 = "{\"foo\":\"bar\"}";
String invalidResponse2 = "{\"contacts\":{\"foo\":\"bar\"}}";
String invalidResponse3 = "{\"contacts\": [1, 2, \"foo\"] }";
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody(validResponse1));
server.enqueue(new MockResponse().setBody(validResponse2));
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setBody(invalidResponse1));
server.enqueue(new MockResponse().setBody(invalidResponse2));
server.enqueue(new MockResponse().setBody(invalidResponse3));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// valid response with two contacts
assertEquals(singletonList(contactId), api.getContacts(properties));
RecordedRequest request1 = server.takeRequest();
assertEquals("/contacts", request1.getPath());
assertEquals("GET", request1.getMethod());
assertToken(request1, token);
// valid response with two contacts
List<ContactId> contacts = new ArrayList<>();
contacts.add(contactId);
contacts.add(contactId2);
assertEquals(contacts, api.getContacts(properties));
RecordedRequest request2 = server.takeRequest();
assertEquals("/contacts", request2.getPath());
assertEquals("GET", request2.getMethod());
assertToken(request2, token);
// empty body
assertThrows(ApiException.class, () -> api.getContacts(properties));
RecordedRequest request3 = server.takeRequest();
assertEquals("/contacts", request3.getPath());
assertEquals("GET", request3.getMethod());
assertToken(request3, token);
// invalid response: no contacts key
assertThrows(ApiException.class, () -> api.getContacts(properties));
RecordedRequest request4 = server.takeRequest();
assertEquals("/contacts", request4.getPath());
assertEquals("GET", request4.getMethod());
assertToken(request4, token);
// invalid response: no list in contacts
assertThrows(ApiException.class, () -> api.getContacts(properties));
RecordedRequest request5 = server.takeRequest();
assertEquals("/contacts", request5.getPath());
assertEquals("GET", request5.getMethod());
assertToken(request5, token);
// invalid response: list with non-numbers
assertThrows(ApiException.class, () -> api.getContacts(properties));
RecordedRequest request6 = server.takeRequest();
assertEquals("/contacts", request6.getPath());
assertEquals("GET", request6.getMethod());
assertToken(request6, token);
// 401 not authorized
assertThrows(ApiException.class, () -> api.getContacts(properties));
RecordedRequest request7 = server.takeRequest();
assertEquals("/contacts", request7.getPath());
assertEquals("GET", request7.getMethod());
assertToken(request7, token);
// 500 internal server error
assertThrows(ApiException.class, () -> api.getContacts(properties));
RecordedRequest request8 = server.takeRequest();
assertEquals("/contacts", request8.getPath());
assertEquals("GET", request8.getMethod());
assertToken(request8, token);
}
@Test
public void testGetContactsOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(
IllegalArgumentException.class,
() -> api.getContacts(properties)
);
}
@Test
public void testAddFile() throws Exception {
File file = folder.newFile();
byte[] bytes = getRandomBytes(1337);
writeBytes(file, bytes);
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// file gets uploaded as expected
api.addFile(properties, contactInboxId, file);
RecordedRequest request1 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request1.getPath());
assertEquals("POST", request1.getMethod());
assertToken(request1, token);
assertArrayEquals(bytes, request1.getBody().readByteArray());
// request is not successful
assertThrows(ApiException.class, () ->
api.addFile(properties, contactInboxId, file));
RecordedRequest request2 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request2.getPath());
assertEquals("POST", request1.getMethod());
assertToken(request2, token);
// server error
assertThrows(ApiException.class, () ->
api.addFile(properties, contactInboxId, file));
RecordedRequest request3 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request3.getPath());
assertEquals("POST", request1.getMethod());
assertToken(request3, token);
}
@Test
public void testGetFiles() throws Exception {
MailboxFile mailboxFile1 =
new MailboxFile(new MailboxFileId(getRandomId()), 1337);
MailboxFile mailboxFile2 =
new MailboxFile(new MailboxFileId(getRandomId()),
System.currentTimeMillis());
String fileResponse1 =
new ObjectMapper().writeValueAsString(mailboxFile1);
String fileResponse2 =
new ObjectMapper().writeValueAsString(mailboxFile2);
String validResponse1 = "{\"files\": [" + fileResponse1 + "] }";
String validResponse2 = "{\"files\": [" + fileResponse1 + ", " +
fileResponse2 + "] }";
String invalidResponse1 = "{\"files\":\"bar\"}";
String invalidResponse2 = "{\"files\":{\"foo\":\"bar\"}}";
String invalidResponse3 = "{\"files\": [" + fileResponse1 + ", 1] }";
String invalidResponse4 = "{\"contacts\": [ 1, 2 ] }";
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody(validResponse1));
server.enqueue(new MockResponse().setBody(validResponse2));
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setBody(invalidResponse1));
server.enqueue(new MockResponse().setBody(invalidResponse2));
server.enqueue(new MockResponse().setBody(invalidResponse3));
server.enqueue(new MockResponse().setBody(invalidResponse4));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// valid response with one file
List<MailboxFile> received1 = api.getFiles(properties, contactInboxId);
assertEquals(1, received1.size());
assertEquals(mailboxFile1.name, received1.get(0).name);
assertEquals(mailboxFile1.time, received1.get(0).time);
RecordedRequest request1 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request1.getPath());
assertEquals("GET", request1.getMethod());
assertToken(request1, token);
// valid response with two files
List<MailboxFile> received2 = api.getFiles(properties, contactInboxId);
assertEquals(2, received2.size());
assertEquals(mailboxFile1.name, received2.get(0).name);
assertEquals(mailboxFile1.time, received2.get(0).time);
assertEquals(mailboxFile2.name, received2.get(1).name);
assertEquals(mailboxFile2.time, received2.get(1).time);
RecordedRequest request2 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request1.getPath());
assertEquals("GET", request2.getMethod());
assertToken(request2, token);
// empty body
assertThrows(ApiException.class, () ->
api.getFiles(properties, contactInboxId));
RecordedRequest request3 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request3.getPath());
assertEquals("GET", request3.getMethod());
assertToken(request3, token);
// invalid response: string instead of list
assertThrows(ApiException.class, () ->
api.getFiles(properties, contactInboxId));
RecordedRequest request4 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request4.getPath());
assertEquals("GET", request4.getMethod());
assertToken(request4, token);
// invalid response: object instead of list
assertThrows(ApiException.class, () ->
api.getFiles(properties, contactInboxId));
RecordedRequest request5 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request5.getPath());
assertEquals("GET", request5.getMethod());
assertToken(request5, token);
// invalid response: list with non-objects
assertThrows(ApiException.class, () ->
api.getFiles(properties, contactInboxId));
RecordedRequest request6 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request6.getPath());
assertEquals("GET", request6.getMethod());
assertToken(request6, token);
// no files key in root object
assertThrows(ApiException.class, () ->
api.getFiles(properties, contactInboxId));
RecordedRequest request7 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request7.getPath());
assertEquals("GET", request7.getMethod());
assertToken(request7, token);
// 401 not authorized
assertThrows(ApiException.class, () ->
api.getFiles(properties, contactInboxId));
RecordedRequest request8 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request8.getPath());
assertEquals("GET", request8.getMethod());
assertToken(request8, token);
// 500 internal server error
assertThrows(ApiException.class,
() -> api.getFiles(properties, contactInboxId));
RecordedRequest request9 = server.takeRequest();
assertEquals("/files/" + contactInboxId, request9.getPath());
assertEquals("GET", request9.getMethod());
assertToken(request9, token);
}
@Test
public void testGetFile() throws Exception {
MailboxFileId name = new MailboxFileId(getRandomId());
File file1 = folder.newFile();
File file2 = folder.newFile();
File file3 = folder.newFile();
byte[] bytes = getRandomBytes(1337);
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody(new Buffer().write(bytes)));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// file gets downloaded as expected
api.getFile(properties, contactOutboxId, name, file1);
RecordedRequest request1 = server.takeRequest();
assertEquals("/files/" + contactOutboxId + "/" + name,
request1.getPath());
assertEquals("GET", request1.getMethod());
assertToken(request1, token);
assertArrayEquals(bytes, readBytes(file1));
// request is not successful
assertThrows(ApiException.class, () ->
api.getFile(properties, contactOutboxId, name, file2));
RecordedRequest request2 = server.takeRequest();
assertEquals("/files/" + contactOutboxId + "/" + name,
request2.getPath());
assertEquals("GET", request1.getMethod());
assertToken(request2, token);
assertEquals(0, readBytes(file2).length);
// server error
assertThrows(ApiException.class, () ->
api.getFile(properties, contactOutboxId, name, file3));
RecordedRequest request3 = server.takeRequest();
assertEquals("/files/" + contactOutboxId + "/" + name,
request3.getPath());
assertEquals("GET", request1.getMethod());
assertToken(request3, token);
assertEquals(0, readBytes(file3).length);
}
@Test
public void testDeleteFile() throws Exception {
MailboxFileId name = new MailboxFileId(getRandomId());
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setResponseCode(205));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(404));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// file gets deleted as expected
api.deleteFile(properties, contactInboxId, name);
RecordedRequest request1 = server.takeRequest();
assertEquals("DELETE", request1.getMethod());
assertEquals("/files/" + contactInboxId + "/" + name,
request1.getPath());
assertToken(request1, token);
// request is not returning 200
assertThrows(ApiException.class, () ->
api.deleteFile(properties, contactInboxId, name));
RecordedRequest request2 = server.takeRequest();
assertEquals("DELETE", request2.getMethod());
assertEquals("/files/" + contactInboxId + "/" + name,
request2.getPath());
assertToken(request2, token);
// request is not authorized
assertThrows(ApiException.class, () ->
api.deleteFile(properties, contactInboxId, name));
RecordedRequest request3 = server.takeRequest();
assertEquals("DELETE", request3.getMethod());
assertEquals("/files/" + contactInboxId + "/" + name,
request3.getPath());
assertToken(request3, token);
// file not found is tolerable
assertThrows(TolerableFailureException.class, () ->
api.deleteFile(properties, contactInboxId, name));
RecordedRequest request4 = server.takeRequest();
assertEquals("DELETE", request4.getMethod());
assertEquals("/files/" + contactInboxId + "/" + name,
request4.getPath());
assertToken(request4, token);
}
@Test
public void testGetFolders() throws Exception {
MailboxFolderId id1 = new MailboxFolderId(getRandomId());
MailboxFolderId id2 = new MailboxFolderId(getRandomId());
String validResponse1 = "{\"folders\": [ {\"id\": \"" + id1 + "\"} ] }";
String validResponse2 = "{\"folders\": [ {\"id\": \"" + id1 + "\"}, " +
"{ \"id\": \"" + id2 + "\"} ] }";
String invalidResponse1 = "{\"folders\":\"bar\"}";
String invalidResponse2 = "{\"folders\":{\"foo\":\"bar\"}}";
String invalidResponse3 =
"{\"folders\": [ {\"id\": \"" + id1 + "\", 1] }";
String invalidResponse4 = "{\"files\": [ 1, 2 ] }";
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody(validResponse1));
server.enqueue(new MockResponse().setBody(validResponse2));
server.enqueue(new MockResponse());
server.enqueue(new MockResponse().setBody(invalidResponse1));
server.enqueue(new MockResponse().setBody(invalidResponse2));
server.enqueue(new MockResponse().setBody(invalidResponse3));
server.enqueue(new MockResponse().setBody(invalidResponse4));
server.enqueue(new MockResponse().setResponseCode(401));
server.enqueue(new MockResponse().setResponseCode(500));
server.start();
String baseUrl = getBaseUrl(server);
MailboxProperties properties =
new MailboxProperties(baseUrl, token, true);
// valid response with one folders
assertEquals(singletonList(id1), api.getFolders(properties));
RecordedRequest request1 = server.takeRequest();
assertEquals("/folders", request1.getPath());
assertEquals("GET", request1.getMethod());
assertToken(request1, token);
// valid response with two folders
assertEquals(Arrays.asList(id1, id2), api.getFolders(properties));
RecordedRequest request2 = server.takeRequest();
assertEquals("/folders", request1.getPath());
assertEquals("GET", request2.getMethod());
assertToken(request2, token);
// empty body
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request3 = server.takeRequest();
assertEquals("/folders", request3.getPath());
assertEquals("GET", request3.getMethod());
assertToken(request3, token);
// invalid response: string instead of list
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request4 = server.takeRequest();
assertEquals("/folders", request4.getPath());
assertEquals("GET", request4.getMethod());
assertToken(request4, token);
// invalid response: object instead of list
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request5 = server.takeRequest();
assertEquals("/folders", request5.getPath());
assertEquals("GET", request5.getMethod());
assertToken(request5, token);
// invalid response: list with non-objects
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request6 = server.takeRequest();
assertEquals("/folders", request6.getPath());
assertEquals("GET", request6.getMethod());
assertToken(request6, token);
// no folders key in root object
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request7 = server.takeRequest();
assertEquals("/folders", request7.getPath());
assertEquals("GET", request7.getMethod());
assertToken(request7, token);
// 401 not authorized
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request8 = server.takeRequest();
assertEquals("/folders", request8.getPath());
assertEquals("GET", request8.getMethod());
assertToken(request8, token);
// 500 internal server error
assertThrows(ApiException.class, () -> api.getFolders(properties));
RecordedRequest request9 = server.takeRequest();
assertEquals("/folders", request9.getPath());
assertEquals("GET", request9.getMethod());
assertToken(request9, token);
}
@Test
public void testGetFoldersOnlyForOwner() {
MailboxProperties properties =
new MailboxProperties("", token, false);
assertThrows(IllegalArgumentException.class, () ->
api.getFolders(properties));
}
private String getBaseUrl(MockWebServer server) {
String baseUrl = server.url("").toString();
return baseUrl.substring(0, baseUrl.length() - 1);
}
private void assertToken(RecordedRequest request, MailboxId token) {
assertNotNull(request.getHeader("Authorization"));
assertEquals("Bearer " + token, request.getHeader("Authorization"));
}
}

View File

@@ -1,272 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.WeakSingletonProvider;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxFileId;
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import javax.net.SocketFactory;
import okhttp3.OkHttpClient;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
import static org.briarproject.bramble.test.TestUtils.readBytes;
import static org.briarproject.bramble.test.TestUtils.writeBytes;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class MailboxIntegrationTest extends BrambleTestCase {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
private final static String URL_BASE = "http://127.0.0.1:8000";
private final static MailboxAuthToken SETUP_TOKEN;
static {
try {
SETUP_TOKEN = MailboxAuthToken.fromString(
"54686973206973206120736574757020746f6b656e20666f722042726961722e");
} catch (InvalidMailboxIdException e) {
throw new IllegalStateException();
}
}
private static final OkHttpClient client = new OkHttpClient.Builder()
.socketFactory(SocketFactory.getDefault())
.connectTimeout(60_000, MILLISECONDS)
.build();
private static final WeakSingletonProvider<OkHttpClient>
httpClientProvider =
new WeakSingletonProvider<OkHttpClient>() {
@Override
@Nonnull
public OkHttpClient createInstance() {
return client;
}
};
private final static MailboxApiImpl api =
new MailboxApiImpl(httpClientProvider);
// needs to be static to keep values across different tests
private static MailboxProperties ownerProperties;
/**
* Called before each test to make sure the mailbox is setup once
* before starting with individual tests.
* {@link BeforeClass} needs to be static, so we can't use the API class.
*/
@Before
public void ensureSetup() throws IOException, ApiException {
// Skip this test unless it's explicitly enabled in the environment
assumeTrue(isOptionalTestEnabled(MailboxIntegrationTest.class));
if (ownerProperties != null) return;
MailboxProperties setupProperties =
new MailboxProperties(URL_BASE, SETUP_TOKEN, true);
MailboxAuthToken ownerToken = api.setup(setupProperties);
ownerProperties = new MailboxProperties(URL_BASE, ownerToken, true);
}
@AfterClass
// we can't test wiping as a regular test as it stops the mailbox
public static void wipe() throws IOException, ApiException {
if (!isOptionalTestEnabled(MailboxIntegrationTest.class)) return;
api.wipeMailbox(ownerProperties);
// check doesn't work anymore
assertThrows(ApiException.class, () ->
api.checkStatus(ownerProperties));
// new setup doesn't work as mailbox is stopping
MailboxProperties setupProperties =
new MailboxProperties(URL_BASE, SETUP_TOKEN, true);
assertThrows(ApiException.class, () -> api.setup(setupProperties));
}
@Test
public void testStatus() throws Exception {
assertTrue(api.checkStatus(ownerProperties));
}
@Test
public void testContactApi() throws Exception {
ContactId contactId1 = new ContactId(1);
ContactId contactId2 = new ContactId(2);
MailboxContact mailboxContact1 = getMailboxContact(contactId1);
MailboxContact mailboxContact2 = getMailboxContact(contactId2);
// no contacts initially
assertEquals(emptyList(), api.getContacts(ownerProperties));
// added contact gets returned
api.addContact(ownerProperties, mailboxContact1);
assertEquals(singletonList(contactId1),
api.getContacts(ownerProperties));
// second contact also gets returned
api.addContact(ownerProperties, mailboxContact2);
assertEquals(Arrays.asList(contactId1, contactId2),
api.getContacts(ownerProperties));
// after both contacts get deleted, the list is empty again
api.deleteContact(ownerProperties, contactId1);
api.deleteContact(ownerProperties, contactId2);
assertEquals(emptyList(), api.getContacts(ownerProperties));
// deleting again is tolerable
assertThrows(TolerableFailureException.class,
() -> api.deleteContact(ownerProperties, contactId2));
}
@Test
public void testFileManagementApi() throws Exception {
// add contact, so we can leave each other files
ContactId contactId = new ContactId(1);
MailboxContact contact = getMailboxContact(contactId);
MailboxProperties contactProperties = new MailboxProperties(
ownerProperties.getBaseUrl(), contact.token, false);
api.addContact(ownerProperties, contact);
// upload a file for our contact
File file1 = folder.newFile();
byte[] bytes1 = getRandomBytes(2048);
writeBytes(file1, bytes1);
api.addFile(ownerProperties, contact.inboxId, file1);
// contact checks files
List<MailboxFile> files1 =
api.getFiles(contactProperties, contact.inboxId);
assertEquals(1, files1.size());
MailboxFileId fileName1 = files1.get(0).name;
// owner can't check files
assertThrows(ApiException.class, () ->
api.getFiles(ownerProperties, contact.inboxId));
// contact downloads file
File file1downloaded = folder.newFile();
api.getFile(contactProperties, contact.inboxId, fileName1,
file1downloaded);
assertArrayEquals(bytes1, readBytes(file1downloaded));
// owner can't download file, even if knowing name
File file1forbidden = folder.newFile();
assertThrows(ApiException.class, () -> api.getFile(ownerProperties,
contact.inboxId, fileName1, file1forbidden));
assertEquals(0, file1forbidden.length());
// owner can't delete file
assertThrows(ApiException.class, () ->
api.deleteFile(ownerProperties, contact.inboxId, fileName1));
// contact deletes file
api.deleteFile(contactProperties, contact.inboxId, fileName1);
assertEquals(0,
api.getFiles(contactProperties, contact.inboxId).size());
// contact uploads two files for the owner
File file2 = folder.newFile();
File file3 = folder.newFile();
byte[] bytes2 = getRandomBytes(2048);
byte[] bytes3 = getRandomBytes(1024);
writeBytes(file2, bytes2);
writeBytes(file3, bytes3);
api.addFile(contactProperties, contact.outboxId, file2);
api.addFile(contactProperties, contact.outboxId, file3);
// owner checks folders with available files
List<MailboxFolderId> folders = api.getFolders(ownerProperties);
assertEquals(singletonList(contact.outboxId), folders);
// owner lists files in contact's outbox
List<MailboxFile> files2 =
api.getFiles(ownerProperties, contact.outboxId);
assertEquals(2, files2.size());
MailboxFileId file2name = files2.get(0).name;
MailboxFileId file3name = files2.get(1).name;
// contact can't list files in contact's outbox
assertThrows(ApiException.class, () ->
api.getFiles(contactProperties, contact.outboxId));
// owner downloads both files from contact's outbox
File file2downloaded = folder.newFile();
File file3downloaded = folder.newFile();
api.getFile(ownerProperties, contact.outboxId, file2name,
file2downloaded);
api.getFile(ownerProperties, contact.outboxId, file3name,
file3downloaded);
byte[] downloadedBytes2 = readBytes(file2downloaded);
byte[] downloadedBytes3 = readBytes(file3downloaded);
// file order is preserved (sorted by time),
// so we know what file is which
assertArrayEquals(bytes2, downloadedBytes2);
assertArrayEquals(bytes3, downloadedBytes3);
// contact can't download files again, even if knowing name
File file2forbidden = folder.newFile();
File file3forbidden = folder.newFile();
assertThrows(ApiException.class, () -> api.getFile(contactProperties,
contact.outboxId, file2name, file2forbidden));
assertThrows(ApiException.class, () -> api.getFile(contactProperties,
contact.outboxId, file3name, file3forbidden));
assertEquals(0, file1forbidden.length());
assertEquals(0, file2forbidden.length());
// contact can't delete files in outbox
assertThrows(ApiException.class, () ->
api.deleteFile(contactProperties, contact.outboxId, file2name));
assertThrows(ApiException.class, () ->
api.deleteFile(contactProperties, contact.outboxId, file3name));
// owner deletes files
api.deleteFile(ownerProperties, contact.outboxId, file2name);
api.deleteFile(ownerProperties, contact.outboxId, file3name);
assertEquals(emptyList(),
api.getFiles(ownerProperties, contact.outboxId));
assertEquals(emptyList(), api.getFolders(ownerProperties));
// deleting a non-existent file is tolerable
assertThrows(TolerableFailureException.class, () ->
api.deleteFile(ownerProperties, contact.outboxId, file3name));
// owner deletes contact again to leave clean state for other tests
api.deleteContact(ownerProperties, contactId);
assertEquals(emptyList(), api.getContacts(ownerProperties));
}
private MailboxContact getMailboxContact(ContactId contactId) {
MailboxAuthToken authToken = new MailboxAuthToken(getRandomId());
MailboxFolderId inboxId = new MailboxFolderId(getRandomId());
MailboxFolderId outboxId = new MailboxFolderId(getRandomId());
return new MailboxContact(contactId, authToken, inboxId, outboxId);
}
}

View File

@@ -1,196 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.PredicateMatcher;
import org.jmock.Expectations;
import org.junit.Test;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
private final Executor executor = new ImmediateExecutor();
private final TransactionManager db =
context.mock(TransactionManager.class);
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final Clock clock = context.mock(Clock.class);
private final MailboxApi api = context.mock(MailboxApi.class);
private final MailboxSettingsManager mailboxSettingsManager =
context.mock(MailboxSettingsManager.class);
private final MailboxPairingTaskFactory factory =
new MailboxPairingTaskFactoryImpl(executor, db, crypto, clock, api,
mailboxSettingsManager);
private final String onion = getRandomString(56);
private final byte[] onionBytes = getRandomBytes(32);
private final String onionAddress = "http://" + onion + ".onion";
private final MailboxAuthToken setupToken =
new MailboxAuthToken(getRandomId());
private final MailboxAuthToken ownerToken =
new MailboxAuthToken(getRandomId());
private final String validPayload = getValidPayload();
private final long time = System.currentTimeMillis();
private final MailboxProperties setupProperties =
new MailboxProperties(onionAddress, setupToken, true);
private final MailboxProperties ownerProperties =
new MailboxProperties(onionAddress, ownerToken, true);
@Test
public void testInitialQrCodeReceivedState() {
MailboxPairingTask task =
factory.createPairingTask(getRandomString(42));
task.addObserver(state ->
assertTrue(state instanceof MailboxPairingState.QrCodeReceived)
);
}
@Test
public void testInvalidQrCode() {
MailboxPairingTask task1 =
factory.createPairingTask(getRandomString(42));
task1.run();
task1.addObserver(state ->
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
);
String goodLength = "00" + getRandomString(63);
MailboxPairingTask task2 = factory.createPairingTask(goodLength);
task2.run();
task2.addObserver(state ->
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
);
}
@Test
public void testSuccessfulPairing() throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).encodeOnionAddress(onionBytes);
will(returnValue(onion));
oneOf(api).setup(with(matches(setupProperties)));
will(returnValue(ownerToken));
oneOf(clock).currentTimeMillis();
will(returnValue(time));
}});
Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(mailboxSettingsManager).setOwnMailboxProperties(
with(txn), with(matches(ownerProperties)));
oneOf(mailboxSettingsManager).recordSuccessfulConnection(txn, time);
}});
AtomicInteger i = new AtomicInteger(0);
MailboxPairingTask task = factory.createPairingTask(validPayload);
task.addObserver(state -> {
if (i.get() == 0) {
assertEquals(MailboxPairingState.QrCodeReceived.class,
state.getClass());
} else if (i.get() == 1) {
assertEquals(MailboxPairingState.Pairing.class,
state.getClass());
} else if (i.get() == 2) {
assertEquals(MailboxPairingState.Paired.class,
state.getClass());
} else fail("Unexpected change of state " + state.getClass());
i.getAndIncrement();
});
task.run();
}
@Test
public void testAlreadyPaired() throws Exception {
testApiException(new MailboxApi.MailboxAlreadyPairedException(),
MailboxPairingState.MailboxAlreadyPaired.class);
}
@Test
public void testMailboxApiException() throws Exception {
testApiException(new MailboxApi.ApiException(),
MailboxPairingState.UnexpectedError.class);
}
@Test
public void testApiIOException() throws Exception {
testApiException(new IOException(),
MailboxPairingState.ConnectionError.class);
}
private void testApiException(Exception e,
Class<? extends MailboxPairingState> s) throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).encodeOnionAddress(onionBytes);
will(returnValue(onion));
oneOf(api).setup(with(matches(setupProperties)));
will(throwException(e));
}});
MailboxPairingTask task = factory.createPairingTask(validPayload);
task.run();
task.addObserver(state -> assertEquals(state.getClass(), s));
}
@Test
public void testDbException() throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).encodeOnionAddress(onionBytes);
will(returnValue(onion));
oneOf(api).setup(with(matches(setupProperties)));
will(returnValue(ownerToken));
oneOf(clock).currentTimeMillis();
will(returnValue(time));
}});
Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(mailboxSettingsManager).setOwnMailboxProperties(
with(txn), with(matches(ownerProperties)));
will(throwException(new DbException()));
}});
MailboxPairingTask task = factory.createPairingTask(validPayload);
task.run();
task.addObserver(state -> assertEquals(state.getClass(),
MailboxPairingState.UnexpectedError.class));
}
private String getValidPayload() {
byte[] payloadBytes = ByteBuffer.allocate(65)
.put((byte) 32) // 1
.put(onionBytes) // 32
.put(setupToken.getBytes()) // 32
.array();
//noinspection CharsetObjectCanBeUsed
return new String(payloadBytes, Charset.forName("ISO-8859-1"));
}
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
return new PredicateMatcher<>(MailboxProperties.class, p1 ->
p1.getAuthToken().equals(p2.getAuthToken()) &&
p1.getBaseUrl().equals(p2.getBaseUrl()) &&
p1.isOwner() == p2.isOwner());
}
}

View File

@@ -1,228 +0,0 @@
package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.mailbox.MailboxStatus;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.Random;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_ATTEMPTS;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_LAST_ATTEMPT;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_LAST_SUCCESS;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_ONION;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_TOKEN;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_NAMESPACE;
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_UPLOADS_NAMESPACE;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
private final SettingsManager settingsManager =
context.mock(SettingsManager.class);
private final MailboxSettingsManager manager =
new MailboxSettingsManagerImpl(settingsManager);
private final Random random = new Random();
private final String onion = getRandomString(64);
private final MailboxAuthToken token = new MailboxAuthToken(getRandomId());
private final ContactId contactId1 = new ContactId(random.nextInt());
private final ContactId contactId2 = new ContactId(random.nextInt());
private final ContactId contactId3 = new ContactId(random.nextInt());
private final long now = System.currentTimeMillis();
private final long lastAttempt = now - 1234;
private final long lastSuccess = now - 2345;
private final int attempts = 123;
@Test
public void testReturnsNullPropertiesIfSettingsAreEmpty() throws Exception {
Transaction txn = new Transaction(null, true);
Settings emptySettings = new Settings();
context.checking(new Expectations() {{
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
will(returnValue(emptySettings));
}});
assertNull(manager.getOwnMailboxProperties(txn));
}
@Test
public void testReturnsProperties() throws Exception {
Transaction txn = new Transaction(null, true);
Settings settings = new Settings();
settings.put(SETTINGS_KEY_ONION, onion);
settings.put(SETTINGS_KEY_TOKEN, token.toString());
context.checking(new Expectations() {{
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
will(returnValue(settings));
}});
MailboxProperties properties = manager.getOwnMailboxProperties(txn);
assertNotNull(properties);
assertEquals(onion, properties.getBaseUrl());
assertEquals(token, properties.getAuthToken());
assertTrue(properties.isOwner());
}
@Test
public void testStoresProperties() throws Exception {
Transaction txn = new Transaction(null, false);
Settings expectedSettings = new Settings();
expectedSettings.put(SETTINGS_KEY_ONION, onion);
expectedSettings.put(SETTINGS_KEY_TOKEN, token.toString());
MailboxProperties properties =
new MailboxProperties(onion, token, true);
context.checking(new Expectations() {{
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
SETTINGS_NAMESPACE);
}});
manager.setOwnMailboxProperties(txn, properties);
}
@Test
public void testReturnsDefaultStatusIfSettingsAreEmpty() throws Exception {
Transaction txn = new Transaction(null, true);
Settings emptySettings = new Settings();
context.checking(new Expectations() {{
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
will(returnValue(emptySettings));
}});
MailboxStatus status = manager.getOwnMailboxStatus(txn);
assertEquals(-1, status.getTimeOfLastAttempt());
assertEquals(-1, status.getTimeOfLastSuccess());
assertEquals(0, status.getAttemptsSinceSuccess());
}
@Test
public void testReturnsStatus() throws Exception {
Transaction txn = new Transaction(null, true);
Settings settings = new Settings();
settings.putLong(SETTINGS_KEY_LAST_ATTEMPT, lastAttempt);
settings.putLong(SETTINGS_KEY_LAST_SUCCESS, lastSuccess);
settings.putInt(SETTINGS_KEY_ATTEMPTS, attempts);
context.checking(new Expectations() {{
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
will(returnValue(settings));
}});
MailboxStatus status = manager.getOwnMailboxStatus(txn);
assertEquals(lastAttempt, status.getTimeOfLastAttempt());
assertEquals(lastSuccess, status.getTimeOfLastSuccess());
assertEquals(attempts, status.getAttemptsSinceSuccess());
}
@Test
public void testRecordsSuccess() throws Exception {
Transaction txn = new Transaction(null, false);
Settings expectedSettings = new Settings();
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
expectedSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, 0);
context.checking(new Expectations() {{
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
SETTINGS_NAMESPACE);
}});
manager.recordSuccessfulConnection(txn, now);
}
@Test
public void testRecordsFailureOnFirstAttempt() throws Exception {
testRecordsFailure(new Settings(), 0);
}
@Test
public void testRecordsFailureOnLaterAttempt() throws Exception {
Settings oldSettings = new Settings();
oldSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, lastAttempt);
oldSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, lastSuccess);
oldSettings.putInt(SETTINGS_KEY_ATTEMPTS, attempts);
testRecordsFailure(oldSettings, attempts);
}
private void testRecordsFailure(Settings oldSettings, int oldAttempts)
throws Exception {
Transaction txn = new Transaction(null, false);
Settings expectedSettings = new Settings();
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, oldAttempts + 1);
context.checking(new Expectations() {{
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
will(returnValue(oldSettings));
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
SETTINGS_NAMESPACE);
}});
manager.recordFailedConnectionAttempt(txn, now);
}
@Test
public void testGettingPendingUploads() throws Exception {
Transaction txn = new Transaction(null, true);
Settings settings = new Settings();
settings.put(String.valueOf(contactId1.getInt()), onion);
settings.put(String.valueOf(contactId2.getInt()), token.toString());
settings.put(String.valueOf(contactId3.getInt()), "");
context.checking(new Expectations() {{
exactly(4).of(settingsManager)
.getSettings(txn, SETTINGS_UPLOADS_NAMESPACE);
will(returnValue(settings));
}});
String filename1 = manager.getPendingUpload(txn, contactId1);
assertEquals(onion, filename1);
String filename2 = manager.getPendingUpload(txn, contactId2);
assertNotNull(filename2);
assertEquals(token.toString(), filename2);
String filename3 = manager.getPendingUpload(txn, contactId3);
assertNull(filename3);
String filename4 =
manager.getPendingUpload(txn, new ContactId(random.nextInt()));
assertNull(filename4);
}
@Test
public void testSettingPendingUploads() throws Exception {
Transaction txn = new Transaction(null, false);
// setting a pending upload stores expected settings
Settings expectedSettings1 = new Settings();
expectedSettings1.put(String.valueOf(contactId1.getInt()), onion);
context.checking(new Expectations() {{
oneOf(settingsManager).mergeSettings(txn, expectedSettings1,
SETTINGS_UPLOADS_NAMESPACE);
}});
manager.setPendingUpload(txn, contactId1, onion);
// nulling a pending upload empties stored settings
Settings expectedSettings2 = new Settings();
expectedSettings2.put(String.valueOf(contactId2.getInt()), "");
context.checking(new Expectations() {{
oneOf(settingsManager).mergeSettings(txn, expectedSettings2,
SETTINGS_UPLOADS_NAMESPACE);
}});
manager.setPendingUpload(txn, contactId2, null);
}
}

View File

@@ -12,8 +12,10 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser;
import org.junit.Test;
import java.util.Arrays;
@@ -22,10 +24,13 @@ import java.util.concurrent.Executors;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class PluginManagerImplTest extends BrambleMockTestCase {
public class PluginManagerImplTest extends BrambleTestCase {
@Test
public void testStartAndStop() throws Exception {
Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
Executor ioExecutor = Executors.newSingleThreadExecutor();
EventBus eventBus = context.mock(EventBus.class);
PluginConfig pluginConfig = context.mock(PluginConfig.class);
@@ -111,5 +116,7 @@ public class PluginManagerImplTest extends BrambleMockTestCase {
// Two plugins should be started and stopped
p.startService();
p.stopService();
context.assertIsSatisfied();
}
}

View File

@@ -25,7 +25,7 @@ import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.RunAction;
import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Test;
@@ -69,7 +69,7 @@ public class PollerImplTest extends BrambleMockTestCase {
private PollerImpl poller;
public PollerImplTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
context.setImposteriser(ClassImposteriser.INSTANCE);
random = context.mock(SecureRandom.class);
}

View File

@@ -1,68 +0,0 @@
package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static java.util.Arrays.asList;
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED;
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.DEFAULT_OBFS4_BRIDGES;
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.MEEK_BRIDGES;
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_OBFS4_BRIDGES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class CircumventionProviderTest extends BrambleTestCase {
private final CircumventionProvider provider =
new CircumventionProviderImpl();
@Test
public void testInvariants() {
Set<String> blocked = new HashSet<>(asList(BLOCKED));
Set<String> bridges = new HashSet<>(asList(BRIDGES));
Set<String> defaultObfs4Bridges =
new HashSet<>(asList(DEFAULT_OBFS4_BRIDGES));
Set<String> nonDefaultObfs4Bridges =
new HashSet<>(asList(NON_DEFAULT_OBFS4_BRIDGES));
Set<String> meekBridges = new HashSet<>(asList(MEEK_BRIDGES));
// BRIDGES should be a subset of BLOCKED
assertTrue(blocked.containsAll(bridges));
// BRIDGES should be the union of the bridge type sets
Set<String> union = new HashSet<>(defaultObfs4Bridges);
union.addAll(nonDefaultObfs4Bridges);
union.addAll(meekBridges);
assertEquals(bridges, union);
// The bridge type sets should not overlap
assertEmptyIntersection(defaultObfs4Bridges, nonDefaultObfs4Bridges);
assertEmptyIntersection(defaultObfs4Bridges, meekBridges);
assertEmptyIntersection(nonDefaultObfs4Bridges, meekBridges);
}
@Test
public void testGetBestBridgeType() {
for (String country : DEFAULT_OBFS4_BRIDGES) {
assertEquals(DEFAULT_OBFS4, provider.getBestBridgeType(country));
}
for (String country : NON_DEFAULT_OBFS4_BRIDGES) {
assertEquals(NON_DEFAULT_OBFS4,
provider.getBestBridgeType(country));
}
for (String country : MEEK_BRIDGES) {
assertEquals(MEEK, provider.getBestBridgeType(country));
}
assertEquals(DEFAULT_OBFS4, provider.getBestBridgeType("ZZ"));
}
private <T> void assertEmptyIntersection(Set<T> a, Set<T> b) {
Set<T> intersection = new HashSet<>(a);
intersection.retainAll(b);
assertTrue(intersection.isEmpty());
}
}

View File

@@ -26,22 +26,12 @@ public class TestFeatureFlagModule {
}
@Override
public boolean shouldEnableMailbox() {
public boolean shouldEnableTransferData() {
return true;
}
@Override
public boolean shouldEnablePrivateGroupsInCore() {
return true;
}
@Override
public boolean shouldEnableForumsInCore() {
return true;
}
@Override
public boolean shouldEnableBlogsInCore() {
public boolean shouldEnableShareAppViaOfflineHotspot() {
return true;
}
};

View File

@@ -1,17 +1,19 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.StreamDecrypter;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.junit.Assert.assertEquals;
public class StreamReaderImplTest extends BrambleMockTestCase {
public class StreamReaderImplTest extends BrambleTestCase {
@Test
public void testEmptyFramesAreSkipped() throws Exception {
Mockery context = new Mockery();
StreamDecrypter decrypter = context.mock(StreamDecrypter.class);
context.checking(new Expectations() {{
oneOf(decrypter).readFrame(with(any(byte[].class)));
@@ -28,11 +30,13 @@ public class StreamReaderImplTest extends BrambleMockTestCase {
assertEquals(0, r.read()); // Read another byte
assertEquals(-1, r.read()); // Skip the second empty frame, reach EOF
assertEquals(-1, r.read()); // Still at EOF
context.assertIsSatisfied();
r.close();
}
@Test
public void testEmptyFramesAreSkippedWithBuffer() throws Exception {
Mockery context = new Mockery();
StreamDecrypter decrypter = context.mock(StreamDecrypter.class);
context.checking(new Expectations() {{
oneOf(decrypter).readFrame(with(any(byte[].class)));
@@ -52,11 +56,13 @@ public class StreamReaderImplTest extends BrambleMockTestCase {
assertEquals(-1, r.read(buf));
// Still at EOF
assertEquals(-1, r.read(buf));
context.assertIsSatisfied();
r.close();
}
@Test
public void testMultipleReadsPerFrame() throws Exception {
Mockery context = new Mockery();
StreamDecrypter decrypter = context.mock(StreamDecrypter.class);
context.checking(new Expectations() {{
oneOf(decrypter).readFrame(with(any(byte[].class)));
@@ -72,11 +78,13 @@ public class StreamReaderImplTest extends BrambleMockTestCase {
assertEquals(MAX_PAYLOAD_LENGTH / 2, r.read(buf));
// Reach EOF
assertEquals(-1, r.read(buf, 0, buf.length));
context.assertIsSatisfied();
r.close();
}
@Test
public void testMultipleReadsPerFrameWithOffsets() throws Exception {
Mockery context = new Mockery();
StreamDecrypter decrypter = context.mock(StreamDecrypter.class);
context.checking(new Expectations() {{
oneOf(decrypter).readFrame(with(any(byte[].class)));
@@ -94,6 +102,7 @@ public class StreamReaderImplTest extends BrambleMockTestCase {
MAX_PAYLOAD_LENGTH / 2));
// Reach EOF
assertEquals(-1, r.read(buf, 0, buf.length));
context.assertIsSatisfied();
r.close();
}
}

View File

@@ -1,17 +1,19 @@
package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.StreamEncrypter;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.junit.Assert.assertEquals;
public class StreamWriterImplTest extends BrambleMockTestCase {
public class StreamWriterImplTest extends BrambleTestCase {
@Test
public void testCloseWithoutWritingWritesFinalFrame() throws Exception {
Mockery context = new Mockery();
StreamEncrypter encrypter = context.mock(StreamEncrypter.class);
context.checking(new Expectations() {{
// Write an empty final frame
@@ -22,11 +24,13 @@ public class StreamWriterImplTest extends BrambleMockTestCase {
}});
StreamWriterImpl w = new StreamWriterImpl(encrypter);
w.close();
context.assertIsSatisfied();
}
@Test
public void testFlushWithoutBufferedDataWritesFrameAndFlushes()
throws Exception {
Mockery context = new Mockery();
StreamEncrypter encrypter = context.mock(StreamEncrypter.class);
StreamWriterImpl w = new StreamWriterImpl(encrypter);
context.checking(new Expectations() {{
@@ -47,11 +51,13 @@ public class StreamWriterImplTest extends BrambleMockTestCase {
oneOf(encrypter).flush();
}});
w.close();
context.assertIsSatisfied();
}
@Test
public void testFlushWithBufferedDataWritesFrameAndFlushes()
throws Exception {
Mockery context = new Mockery();
StreamEncrypter encrypter = context.mock(StreamEncrypter.class);
StreamWriterImpl w = new StreamWriterImpl(encrypter);
context.checking(new Expectations() {{
@@ -73,10 +79,12 @@ public class StreamWriterImplTest extends BrambleMockTestCase {
oneOf(encrypter).flush();
}});
w.close();
context.assertIsSatisfied();
}
@Test
public void testSingleByteWritesWriteFullFrame() throws Exception {
Mockery context = new Mockery();
StreamEncrypter encrypter = context.mock(StreamEncrypter.class);
StreamWriterImpl w = new StreamWriterImpl(encrypter);
context.checking(new Expectations() {{
@@ -95,10 +103,12 @@ public class StreamWriterImplTest extends BrambleMockTestCase {
oneOf(encrypter).flush();
}});
w.close();
context.assertIsSatisfied();
}
@Test
public void testMultiByteWritesWriteFullFrames() throws Exception {
Mockery context = new Mockery();
StreamEncrypter encrypter = context.mock(StreamEncrypter.class);
StreamWriterImpl w = new StreamWriterImpl(encrypter);
context.checking(new Expectations() {{
@@ -124,10 +134,12 @@ public class StreamWriterImplTest extends BrambleMockTestCase {
oneOf(encrypter).flush();
}});
w.close();
context.assertIsSatisfied();
}
@Test
public void testLargeMultiByteWriteWritesFullFrames() throws Exception {
Mockery context = new Mockery();
StreamEncrypter encrypter = context.mock(StreamEncrypter.class);
StreamWriterImpl w = new StreamWriterImpl(encrypter);
context.checking(new Expectations() {{
@@ -145,5 +157,6 @@ public class StreamWriterImplTest extends BrambleMockTestCase {
w.write(b);
// There should be one byte left in the buffer
w.close();
context.assertIsSatisfied();
}
}

View File

@@ -393,76 +393,6 @@ public class TransportKeyManagerImplTest extends BrambleMockTestCase {
assertNull(transportKeyManager.getStreamContext(txn, tag));
}
@Test
public void testGetStreamContextOnlyAndMarkTag() throws Exception {
boolean alice = random.nextBoolean();
TransportKeys transportKeys = createTransportKeys(1000, 0, true);
Transaction txn = new Transaction(null, false);
// Keep a copy of the tags
List<byte[]> tags = new ArrayList<>();
context.checking(new Expectations() {{
oneOf(transportCrypto).deriveRotationKeys(transportId, rootKey,
1000, alice, true);
will(returnValue(transportKeys));
// Get the current time (the start of time period 1000)
oneOf(clock).currentTimeMillis();
will(returnValue(timePeriodLength * 1000));
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(transportCrypto).encodeTag(
with(any(byte[].class)), with(tagKey),
with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags));
}
// Updated the transport keys (the keys are unaffected)
oneOf(transportCrypto).updateTransportKeys(transportKeys, 1000);
will(returnValue(transportKeys));
// Save the keys
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
will(returnValue(keySetId));
// Encode a new tag after sliding the window
oneOf(transportCrypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags));
// Save the reordering window (previous time period, base 1)
oneOf(db).setReorderingWindow(txn, keySetId, transportId, 999,
1, new byte[REORDERING_WINDOW_SIZE / 8]);
}});
// The timestamp is at the start of time period 1000
long timestamp = timePeriodLength * 1000;
assertEquals(keySetId, transportKeyManager.addRotationKeys(
txn, contactId, rootKey, timestamp, alice, true));
assertTrue(transportKeyManager.canSendOutgoingStreams(contactId));
// Use the first tag (previous time period, stream number 0)
assertEquals(REORDERING_WINDOW_SIZE * 3, tags.size());
byte[] tag = tags.get(0);
// Repeated request should return same stream context
StreamContext ctx = transportKeyManager.getStreamContextOnly(txn, tag);
assertNotNull(ctx);
assertEquals(contactId, ctx.getContactId());
assertEquals(transportId, ctx.getTransportId());
assertEquals(tagKey, ctx.getTagKey());
assertEquals(headerKey, ctx.getHeaderKey());
assertEquals(0L, ctx.getStreamNumber());
ctx = transportKeyManager.getStreamContextOnly(txn, tag);
assertNotNull(ctx);
assertEquals(contactId, ctx.getContactId());
assertEquals(transportId, ctx.getTransportId());
assertEquals(tagKey, ctx.getTagKey());
assertEquals(headerKey, ctx.getHeaderKey());
assertEquals(0L, ctx.getStreamNumber());
// Then mark tag as recognised
transportKeyManager.markTagAsRecognised(txn, tag);
// Another tag should have been encoded
assertEquals(REORDERING_WINDOW_SIZE * 3 + 1, tags.size());
// Finally ensure the used tag is not recognised again
assertNull(transportKeyManager.getStreamContextOnly(txn, tag));
}
@Test
public void testKeysAreUpdatedToCurrentPeriod() throws Exception {
TransportKeys transportKeys = createTransportKeys(1000, 0, true);

View File

@@ -1,9 +1,6 @@
dependencyVerification {
verify = [
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
'com.fasterxml.jackson.core:jackson-annotations:2.13.0:jackson-annotations-2.13.0.jar:81f9724d8843e8b08f8f6c0609e7a2b030d00c34861c4ac7e2099a7235047d6f',
'com.fasterxml.jackson.core:jackson-core:2.13.0:jackson-core-2.13.0.jar:348bc59b348df2e807b356f1d62d2afb41a974073328abc773eb0932b855d2c8',
'com.fasterxml.jackson.core:jackson-databind:2.13.0:jackson-databind-2.13.0.jar:9c826d27176268777adcf97e1c6e2051c7e33a7aaa2c370c2e8c6077fd9da3f4',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.33:dagger-compiler-2.33.jar:aa8a0d8370c578fd6999802d0d90b9829377a46d2c1141e11b8f737970e7155e',
@@ -18,11 +15,6 @@ dependencyVerification {
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.squareup.okhttp3:mockwebserver:4.9.3:mockwebserver-4.9.3.jar:9c8c581c29f22f877a35d11380462f75bb24bf1886204fe835ee695594a2784e',
'com.squareup.okhttp3:okhttp:3.12.13:okhttp-3.12.13.jar:508234e024ef7e270ab1a6d5b356f5b98e786511239ca986d684fd1e2cf7bc82',
'com.squareup.okhttp3:okhttp:4.9.3:okhttp-4.9.3.jar:93ecd6cba19d87dccfe566ec848d91aae799e3cf16c00709358ea69bd9227219',
'com.squareup.okio:okio:1.15.0:okio-1.15.0.jar:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2',
'com.squareup.okio:okio:2.8.0:okio-jvm-2.8.0.jar:4496b06e73982fcdd8a5393f46e5df2ce2fa4465df5895454cac68a32f09bbc8',
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
@@ -34,7 +26,7 @@ dependencyVerification {
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.bouncycastle:bcprov-jdk15to18:1.70:bcprov-jdk15to18-1.70.jar:7df4c54f29ce2dd616dc3b198ca4db3dfcc79e3cb397c084a0aff97b85c0bf38',
'org.bouncycastle:bcprov-jdk15on:1.69:bcprov-jdk15on-1.69.jar:e469bd39f936999f256002631003ff022a22951da9d5bd9789c7abfa9763a292',
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
'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',
@@ -46,11 +38,7 @@ dependencyVerification {
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.10:kotlin-stdlib-common-1.4.10.jar:4681f2d436a68c7523595d84ed5758e1382f9da0f67c91e6a848690d711274fe',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10:kotlin-stdlib-jdk7-1.4.10.jar:f9566380c08722c780ce33ceee23e98ddf765ca98fabd3e2fabae7975c8d232b',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10:kotlin-stdlib-jdk8-1.4.10.jar:39b7a9442d7a3865e0f4a732c56c1d5da0e11ffb3bb82a461d32deb0c0ca7673',
'org.jetbrains.kotlin:kotlin-stdlib:1.4.10:kotlin-stdlib-1.4.10.jar:01ecb09782c042b931c1839acf21a188340b295d05400afd6e3415d4475b8daa',
'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656',
'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',

View File

@@ -12,21 +12,22 @@ configurations {
}
dependencies {
implementation project(path: ':bramble-core', configuration: 'default')
implementation project(path: ':briar:bramble-core', configuration: 'default')
implementation fileTree(dir: 'libs', include: '*.jar')
def jna_version = '4.5.2'
implementation "net.java.dev.jna:jna:$jna_version"
implementation "net.java.dev.jna:jna-platform:$jna_version"
tor "org.briarproject:tor:$tor_version"
tor "org.briarproject:obfs4proxy:$obfs4proxy_version@zip"
tor 'org.briarproject:tor:0.3.5.15'
tor 'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a@zip'
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
testImplementation project(path: ':briar:bramble-api', configuration: 'testOutput')
testImplementation project(path: ':briar:bramble-core', configuration: 'testOutput')
testImplementation "junit:junit:$junit_version"
testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-legacy:$jmock_version"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
}

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.plugin.TorPorts;
import java.io.File;
import java.net.URI;
@@ -25,6 +26,7 @@ abstract class JavaTorPlugin extends TorPlugin {
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
TorPorts torPorts,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
@@ -35,15 +37,12 @@ abstract class JavaTorPlugin extends TorPlugin {
String architecture,
long maxLatency,
int maxIdleTime,
File torDirectory,
int torSocksPort,
int torControlPort) {
File torDirectory) {
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
torSocketFactory, torPorts, clock, resourceProvider,
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture,
maxLatency, maxIdleTime, torDirectory, torSocksPort,
torControlPort);
maxLatency, maxIdleTime, torDirectory);
}
@Override

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.plugin.TorPorts;
import java.io.File;
import java.util.concurrent.Executor;
@@ -25,6 +26,7 @@ class UnixTorPlugin extends JavaTorPlugin {
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
TorPorts torPorts,
Clock clock,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
@@ -35,15 +37,12 @@ class UnixTorPlugin extends JavaTorPlugin {
String architecture,
long maxLatency,
int maxIdleTime,
File torDirectory,
int torSocksPort,
int torControlPort) {
File torDirectory) {
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
torSocketFactory, torPorts, clock, resourceProvider,
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture,
maxLatency, maxIdleTime, torDirectory, torSocksPort,
torControlPort);
maxLatency, maxIdleTime, torDirectory);
}
@Override

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager;
@@ -10,9 +9,7 @@ import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorControlPort;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TorSocksPort;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
@@ -20,6 +17,7 @@ import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.plugin.TorPorts;
import java.io.File;
import java.util.concurrent.Executor;
@@ -51,15 +49,13 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
private final LocationUtils locationUtils;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final TorPorts torPorts;
private final BackoffFactory backoffFactory;
private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager;
private final Clock clock;
private final File torDirectory;
private int torSocksPort;
private int torControlPort;
private final CryptoComponent crypto;
@Inject
UnixTorPluginFactory(@IoExecutor Executor ioExecutor,
@@ -68,30 +64,26 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
TorPorts torPorts,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
Clock clock,
@TorDirectory File torDirectory,
@TorSocksPort int torSocksPort,
@TorControlPort int torControlPort,
CryptoComponent crypto) {
@TorDirectory File torDirectory) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager;
this.locationUtils = locationUtils;
this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory;
this.torPorts = torPorts;
this.backoffFactory = backoffFactory;
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager;
this.clock = clock;
this.torDirectory = torDirectory;
this.torSocksPort = torSocksPort;
this.torControlPort = torControlPort;
this.crypto = crypto;
}
@Override
@@ -132,14 +124,12 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto =
new TorRendezvousCryptoImpl(crypto);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
networkManager, locationUtils, torSocketFactory, clock,
resourceProvider, circumventionProvider, batteryManager,
networkManager, locationUtils, torSocketFactory, torPorts,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
torControlPort);
MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -2,18 +2,16 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType;
import org.briarproject.bramble.plugin.TorPorts;
import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent;
@@ -36,14 +34,9 @@ import javax.inject.Inject;
import javax.net.SocketFactory;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT;
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.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
@@ -62,21 +55,14 @@ public class BridgeTest extends BrambleTestCase {
.injectEagerSingletons(component);
// Share a failure counter among all the test instances
AtomicInteger failures = new AtomicInteger(0);
CircumventionProvider provider = component.getCircumventionProvider();
List<Params> states = new ArrayList<>();
for (String bridge : provider.getBridges(DEFAULT_OBFS4)) {
states.add(new Params(bridge, DEFAULT_OBFS4, failures, false));
}
for (String bridge : provider.getBridges(NON_DEFAULT_OBFS4)) {
states.add(new Params(bridge, NON_DEFAULT_OBFS4, failures, false));
}
for (String bridge : provider.getBridges(MEEK)) {
states.add(new Params(bridge, MEEK, failures, true));
}
List<String> bridges =
component.getCircumventionProvider().getBridges(false);
List<Params> states = new ArrayList<>(bridges.size());
for (String bridge : bridges) states.add(new Params(bridge, failures));
return states;
}
private final static long TIMEOUT = MINUTES.toMillis(5);
private final static long TIMEOUT = SECONDS.toMillis(60);
private final static int NUM_FAILURES_ALLOWED = 1;
private final static Logger LOG = getLogger(BridgeTest.class.getName());
@@ -102,15 +88,17 @@ public class BridgeTest extends BrambleTestCase {
@Inject
Clock clock;
@Inject
CryptoComponent crypto;
TorPorts torPorts;
private final File torDir = getTestDirectory();
private final Params params;
private final String bridge;
private final AtomicInteger failures;
private UnixTorPluginFactory factory;
public BridgeTest(Params params) {
this.params = params;
bridge = params.bridge;
failures = params.failures;
}
@Before
@@ -130,7 +118,6 @@ public class BridgeTest extends BrambleTestCase {
LocationUtils locationUtils = () -> "US";
SocketFactory torSocketFactory = SocketFactory.getDefault();
@NotNullByDefault
CircumventionProvider bridgeProvider = new CircumventionProvider() {
@Override
public boolean isTorProbablyBlocked(String countryCode) {
@@ -143,20 +130,19 @@ public class BridgeTest extends BrambleTestCase {
}
@Override
public BridgeType getBestBridgeType(String countryCode) {
return params.bridgeType;
public boolean needsMeek(String countryCode) {
return false;
}
@Override
public List<String> getBridges(BridgeType bridgeType) {
return singletonList(params.bridge);
public List<String> getBridges(boolean useMeek) {
return singletonList(bridge);
}
};
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
networkManager, locationUtils, eventBus, torSocketFactory,
backoffFactory, resourceProvider, bridgeProvider,
batteryManager, clock, torDir, DEFAULT_SOCKS_PORT,
DEFAULT_CONTROL_PORT, crypto);
torPorts, backoffFactory, resourceProvider, bridgeProvider,
batteryManager, clock, torDir);
}
@After
@@ -171,7 +157,7 @@ public class BridgeTest extends BrambleTestCase {
assertNotNull(duplexPlugin);
UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin;
LOG.warning("Testing " + params.bridge);
LOG.warning("Testing " + bridge);
try {
plugin.start();
long start = clock.currentTimeMillis();
@@ -181,11 +167,8 @@ public class BridgeTest extends BrambleTestCase {
}
if (plugin.getState() != ACTIVE) {
LOG.warning("Could not connect to Tor within timeout");
if (params.failures.incrementAndGet() > NUM_FAILURES_ALLOWED) {
fail(params.failures.get() + " bridges are unreachable");
}
if (params.mustSucceed) {
fail("essential bridge is unreachable");
if (failures.incrementAndGet() > NUM_FAILURES_ALLOWED) {
fail(failures.get() + " bridges are unreachable");
}
}
} finally {
@@ -196,16 +179,11 @@ public class BridgeTest extends BrambleTestCase {
private static class Params {
private final String bridge;
private final BridgeType bridgeType;
private final AtomicInteger failures;
private final boolean mustSucceed;
private Params(String bridge, BridgeType bridgeType,
AtomicInteger failures, boolean mustSucceed) {
private Params(String bridge, AtomicInteger failures) {
this.bridge = bridge;
this.bridgeType = bridgeType;
this.failures = failures;
this.mustSucceed = mustSucceed;
}
}
}

View File

@@ -25,7 +25,7 @@ dependencyVerification {
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a:obfs4proxy-0.0.12-dev-40245c4a.zip:172029e7058b3a83ac93ac4991a44bf76e16ce8d46f558f5836d57da3cb3a766',
'org.briarproject:tor:0.3.5.17:tor-0.3.5.17.jar:ce0e1f4d8f14878e61b23a35a452bc0f2a8e3117ced5a74773cd78475fa7af39',
'org.briarproject:tor:0.3.5.15:tor-0.3.5.15.jar:2ff5b5a3b5eaa97d699629ad24ba9584b3199d0ffdb1ea7d8a02de3016b80e7a',
'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',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',

Some files were not shown because too many files have changed in this diff Show More