Compare commits

..

10 Commits

Author SHA1 Message Date
akwizgran
024e5aa90f Bumped version numbers for beta release. 2017-12-04 14:43:27 +00:00
akwizgran
6d791481d5 Merge branch '1007-samsung-transition-npe-beta' into 'maintenance-0.16'
Beta: Don't set scene transition for Samsung devices running Android 7.0

See merge request !641
2017-12-04 14:35:39 +00:00
Torsten Grote
0a807d0893 Don't set scene transition for Samsung devices running Android 7.0 2017-12-04 10:58:20 -02:00
akwizgran
23596bbdd4 Merge branch origin/maintenance-0.16 into maintenance-0.16 2017-12-01 17:19:42 +00:00
Torsten Grote
fe79954138 Merge branch 'briar-beta-app-name' into 'maintenance-0.16'
Change app name for beta debug builds

See merge request !636
2017-12-01 16:43:45 +00:00
akwizgran
9902c023ca Bump version number for beta release. 2017-12-01 16:30:18 +00:00
akwizgran
e8baee6734 Specify 7 characters for Git revision.
(cherry picked from commit f0d8532)
2017-12-01 16:29:45 +00:00
akwizgran
a8dc029e56 Change app name for beta debug builds. 2017-12-01 16:21:20 +00:00
akwizgran
74e3fee7aa Merge branch '1124-notification-channel-crash-beta' into 'maintenance-0.16'
Beta: Use NotificationChannel for foreground service to avoid crash on Android 8.1

See merge request !635
2017-12-01 16:00:53 +00:00
Torsten Grote
05aac696b7 Use NotificationChannel for foreground service to avoid crash on Android 8.1
This also seems to address #1075 at least on an emulator
2017-12-01 13:47:02 -02:00
586 changed files with 17609 additions and 31101 deletions

1
.gitignore vendored
View File

@@ -20,7 +20,6 @@ local.properties
.idea/*
!.idea/runConfigurations/
!.idea/codeStyleSettings.xml
!.idea/codeStyles
.gradle
build/
*.iml

View File

@@ -1,31 +1,29 @@
image: briar/ci-image-android:latest
image: registry.gitlab.com/fdroid/ci-images-base:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
# Accept the license for the Android build tools
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;26.0.2"
# Download OpenJDK 6 so we can compile against its standard library
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
- if [ ! -d openjdk ]
- then
- wget -q http://ftp.uk.debian.org/debian/pool/main/o/openjdk-6/$JDK_FILE
- dpkg-deb -x $JDK_FILE openjdk
- fi
- export JAVA_6_HOME=$PWD/openjdk/usr/lib/jvm/java-6-openjdk-amd64
test:
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
cache:
paths:
- .gradle/wrapper
- .gradle/caches
script:
- ./gradlew --no-daemon animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon test
- ./gradlew test
after_script:
# these file change every time but should not be cached
after_script:
# this file changes 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/
test_reproducible:
image: briar/reproducer:latest
script:
- cd /opt/briar-reproducer
- ./reproduce.py ${CI_COMMIT_REF_NAME}
only:
- tags

View File

@@ -1,261 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
</JavaCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="Groovy">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="80" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="RESOURCE_LIST_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="ASSERT_STATEMENT_WRAP" value="1" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="1" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-core" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<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="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-core" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<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="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -1,21 +1,19 @@
import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify
import java.security.NoSuchAlgorithmException
apply plugin: 'com.android.library'
apply plugin: 'witness'
apply plugin: 'de.undercouch.download'
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
buildToolsVersion '26.0.2'
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 10005
versionName "1.0.5"
versionCode 1614
versionName "0.16.14"
consumerProguardFiles 'proguard-rules.txt'
}
@@ -36,157 +34,62 @@ dependencies {
dependencyVerification {
verify = [
'com.android.tools.analytics-library:protos:26.1.2:protos-26.1.2.jar:52672a0b42b572a06aecc3535d5068eb46c0e15d129b9f1085d3c16a1da5cdbb',
'com.android.tools.analytics-library:shared:26.1.2:shared-26.1.2.jar:5c7e0eda18c6f87feeb83628c707e8aaa3298b41fb72e38efe31ad1675f9e8e9',
'com.android.tools.analytics-library:tracker:26.1.2:tracker-26.1.2.jar:06f97aa0adf44ffb06f8681c6a79d9be153a08f61d21eddc42b8d3db96df4282',
'com.android.tools.build:apksig:3.1.2:apksig-3.1.2.jar:40696a4559124d1d57873d208857eee059d48859239d569c7d18374ac644a8be',
'com.android.tools.build:builder-model:3.1.2:builder-model-3.1.2.jar:d49bfa2a135c9562b6ca7aa4342036cfa1582c7074c2d1d93d1dae8b3a134e17',
'com.android.tools.build:builder-test-api:3.1.2:builder-test-api-3.1.2.jar:dfe2a50b740d41b11189101062434d4283d18647e89a492ad51710c719363e9f',
'com.android.tools.build:builder:3.1.2:builder-3.1.2.jar:b60f825a42e2efe8433619fbc759f3d9effecab718279048d36881188ceb1d14',
'com.android.tools.build:gradle-api:3.1.2:gradle-api-3.1.2.jar:e58bcc5b893e4583ab0f5c8ef89c4dbcce202b405a9d7fcc116d21e5357d4893',
'com.android.tools.build:manifest-merger:26.1.2:manifest-merger-26.1.2.jar:9c61c27ea5266573107b954acf1216d398f4d7e7ae6fad6409d6b2b767eb091c',
'com.android.tools.ddms:ddmlib:26.1.2:ddmlib-26.1.2.jar:18a2a5fbef36882f07d03c2b9e59eba05cf8248177bf5cbff736e4b582804c44',
'com.android.tools.external.com-intellij:intellij-core:26.1.2:intellij-core-26.1.2.jar:37c5acf279f1ae3e85b1a5be3c9f15f43bde7b08f978eefefffb9c4035760c52',
'com.android.tools.external.com-intellij:kotlin-compiler:26.1.2:kotlin-compiler-26.1.2.jar:152df0bee7580326c77316b669a9d96e3b09efb1d45f545dce4147271b0b8944',
'com.android.tools.external.org-jetbrains:uast:26.1.2:uast-26.1.2.jar:02d39582206d3f5fc0a6cb18bfd9e8b9f9c1acb805ec6dac08b4e3a56849d279',
'com.android.tools.layoutlib:layoutlib-api:26.1.2:layoutlib-api-26.1.2.jar:20220039fcc7d799f928153beff862e704457c0f55ab44258f3745ebeb662b4f',
'com.android.tools.lint:lint-api:26.1.2:lint-api-26.1.2.jar:e1d5b62b870a7c566e9877a6b96b27784a4d713f8caa07fdcb4705d47a40a1d9',
'com.android.tools.lint:lint-checks:26.1.2:lint-checks-26.1.2.jar:211e2afd58504372385d71b1e5be982c2b5121ab6fee1c04ddabeb75a8729e07',
'com.android.tools.lint:lint-gradle-api:26.1.2:lint-gradle-api-26.1.2.jar:71284f2a8b03c3e55c94511c9eb36f8184fbb85324325fc6b78abf5183f03d90',
'com.android.tools.lint:lint-gradle:26.1.2:lint-gradle-26.1.2.jar:855f0c82b7fc690df1b7319c0774f7517f7f8f5dd4eee1f6077dcf50e07c6240',
'com.android.tools.lint:lint-kotlin:26.1.2:lint-kotlin-26.1.2.jar:1e591f70bcbbc11569720a9bbcca2bc1f3d4f789f01f40f642848d920643d484',
'com.android.tools.lint:lint:26.1.2:lint-26.1.2.jar:93736c62e9f1976998c2b4aa716aea0734cdb162d05502f4af7292654aedb182',
'com.android.tools:annotations:26.1.2:annotations-26.1.2.jar:72773dcaf5c4ccca828e3c8467f1b78a8a00b3cc5f8ad1aab88fcf9379928018',
'com.android.tools:common:26.1.2:common-26.1.2.jar:ea4320f0c17dcbc4491896bb705c4d25ec08bd62ef02ab0579fe154e75e788e6',
'com.android.tools:dvlib:26.1.2:dvlib-26.1.2.jar:1187aa4fb666595c96c4deb6bc0e0f4b7e396bde9f6243330b49a232946130ea',
'com.android.tools:repository:26.1.2:repository-26.1.2.jar:8b86e512ad6d32bd76989451eefe2b271f5efce6d4d65ecb173afaf14606e01a',
'com.android.tools:sdk-common:26.1.2:sdk-common-26.1.2.jar:23584720a60a21cdcb5b1ec10269e3013789d6805d153cc696c39ec7ce251896',
'com.android.tools:sdklib:26.1.2:sdklib-26.1.2.jar:d3870fafc59ab8efa70d3f9649f40ee299c8ec5b58377b06e8853d7272a5bf4e',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.gson:gson:2.7:gson-2.7.jar:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'com.google.guava:guava:22.0:guava-22.0.jar:1158e94c7de4da480873f0b4ab4a1da14c0d23d4b1902cc94a58a6f0f9ab579e',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
'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:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
'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.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
'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.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
]
}
ext.torBinaryDir = 'src/main/res/raw'
ext.torVersion = '0.2.9.14'
ext.geoipVersion = '2017-11-06'
ext.torVersion = '0.2.9.12'
ext.geoipVersion = '2017-09-06'
ext.torDownloadUrl = 'https://briarproject.org/build/'
def torBinaries = [
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
"tor_arm" : '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688',
"tor_arm_pie": '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283',
"tor_x86" : '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab',
"tor_x86_pie": '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288',
"geoip" : 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
]
def verifyOrDeleteBinary(name, chksum, alreadyVerified) {
return tasks.create("verifyOrDeleteBinary${name}", VerifyOrDelete) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
result alreadyVerified
onlyIf {
src.exists()
}
}
}
def downloadBinary(name, chksum, alreadyVerified) {
return tasks.create([
name: "downloadBinary${name}",
type: Download,
dependsOn: verifyOrDeleteBinary(name, chksum, alreadyVerified)]) {
def downloadBinary(name) {
return tasks.create("downloadBinary${name}", Download) {
src "${torDownloadUrl}${name}.zip"
.replace('tor_', "tor-${torVersion}-")
.replace('geoip', "geoip-${geoipVersion}")
.replaceAll('_', '-')
dest "${torBinaryDir}/${name}.zip"
onlyIf {
!dest.exists()
}
onlyIfNewer true
}
}
def verifyBinary(name, chksum) {
boolean[] alreadyVerified = [false]
return tasks.create([
name : "verifyBinary${name}",
type : Verify,
dependsOn: downloadBinary(name, chksum, alreadyVerified)]) {
dependsOn: downloadBinary(name)]) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
onlyIf {
!alreadyVerified[0]
}
}
}
project.afterEvaluate {
torBinaries.every { name, checksum ->
preBuild.dependsOn.add(verifyBinary(name, checksum))
}
}
class VerifyOrDelete extends Verify {
boolean[] result
@TaskAction
@Override
void verify() throws IOException, NoSuchAlgorithmException {
try {
super.verify()
result[0] = true
} catch (Exception e) {
println "${src} failed verification - deleting"
src.delete()
}
torBinaries.every { key, value ->
preBuild.dependsOn.add(verifyBinary(key, value))
}
}

View File

@@ -8,10 +8,6 @@
-dontwarn dagger.**
-dontnote dagger.**
-keep class net.i2p.crypto.eddsa.** { *; }
-keep class org.whispersystems.curve25519.** { *; }
-dontwarn sun.misc.Unsafe
-dontnote com.google.common.**

View File

@@ -10,10 +10,10 @@ import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.droidtooth.DroidtoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
@@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
@@ -34,19 +33,18 @@ public class AndroidPluginModule {
@Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, LocationUtils locationUtils, EventBus eventBus) {
Application app, LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
appContext, locationUtils, eventBus, torSocketFactory,
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
androidExecutor, appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
locationUtils, reporter, eventBus, torSocketFactory,
backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
scheduler, backoffFactory, appContext);
backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan);
@NotNullByDefault

View File

@@ -1,209 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG =
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
private final AndroidExecutor androidExecutor;
private final Context appContext;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
}
@Override
public void start() throws PluginException {
super.start();
// Listen for changes to the Bluetooth state
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver();
appContext.registerReceiver(receiver, filter);
}
@Override
public void stop() {
super.stop();
if (receiver != null) appContext.unregisterReceiver(receiver);
}
@Override
void initialiseAdapter() throws IOException {
// BluetoothAdapter.getDefaultAdapter() must be called on a thread
// with a message queue, so submit it to the AndroidExecutor
try {
adapter = androidExecutor.runOnBackgroundThread(
BluetoothAdapter::getDefaultAdapter).get();
} catch (InterruptedException | ExecutionException e) {
throw new IOException(e);
}
if (adapter == null)
throw new IOException("Bluetooth is not supported");
}
@Override
boolean isAdapterEnabled() {
return adapter != null && adapter.isEnabled();
}
@Override
void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
void disableAdapterIfEnabledByUs() {
if (isAdapterEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false;
}
}
@Override
void setEnabledByUs() {
wasEnabledByUs = true;
}
@Override
@Nullable
String getBluetoothAddress() {
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
return address.isEmpty() ? null : address;
}
@Override
BluetoothServerSocket openServerSocket(String uuid) throws IOException {
return adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", UUID.fromString(uuid));
}
@Override
void tryToClose(@Nullable BluetoothServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException {
return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this,
connectionLimiter, s);
}
@Override
boolean isValidAddress(String address) {
return BluetoothAdapter.checkBluetoothAddress(address);
}
@Override
DuplexTransportConnection connectTo(String address, String uuid)
throws IOException {
BluetoothDevice d = adapter.getRemoteDevice(address);
UUID u = UUID.fromString(uuid);
BluetoothSocket s = null;
try {
s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect();
return wrapSocket(s);
} catch (IOException e) {
tryToClose(s);
throw e;
}
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON) onAdapterEnabled();
else if (state == STATE_OFF) onAdapterDisabled();
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_NONE) {
LOG.info("Scan mode: None");
} else if (scanMode == SCAN_MODE_CONNECTABLE) {
LOG.info("Scan mode: Connectable");
} else if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
LOG.info("Scan mode: Discoverable");
}
}
}
}

View File

@@ -0,0 +1,490 @@
package org.briarproject.bramble.plugin.droidtooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class DroidtoothPlugin implements DuplexPlugin, EventListener {
private static final Logger LOG =
Logger.getLogger(DroidtoothPlugin.class.getName());
private final Executor ioExecutor;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final SecureRandom secureRandom;
private final Backoff backoff;
private final DuplexPluginCallback callback;
private final int maxLatency;
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
private volatile BluetoothServerSocket socket = null;
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.secureRandom = secureRandom;
this.backoff = backoff;
this.callback = callback;
this.maxLatency = maxLatency;
}
@Override
public TransportId getId() {
return ID;
}
@Override
public int getMaxLatency() {
return maxLatency;
}
@Override
public int getMaxIdleTime() {
// Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
}
@Override
public void start() throws PluginException {
if (used.getAndSet(true)) throw new IllegalStateException();
// BluetoothAdapter.getDefaultAdapter() must be called on a thread
// with a message queue, so submit it to the AndroidExecutor
try {
adapter = androidExecutor.runOnBackgroundThread(
BluetoothAdapter::getDefaultAdapter).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.warning("Interrupted while getting BluetoothAdapter");
throw new PluginException(e);
} catch (ExecutionException e) {
throw new PluginException(e);
}
if (adapter == null) {
LOG.info("Bluetooth is not supported");
throw new PluginException();
}
running = true;
// Listen for changes to the Bluetooth state
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver();
appContext.registerReceiver(receiver, filter);
// If Bluetooth is enabled, bind a socket
if (adapter.isEnabled()) {
bind();
} else {
// Enable Bluetooth if settings allow
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
enableAdapter();
} else {
LOG.info("Not enabling Bluetooth");
}
}
}
private void bind() {
ioExecutor.execute(() -> {
if (!isRunning()) return;
String address = AndroidUtils.getBluetoothAddress(appContext,
adapter);
if (LOG.isLoggable(INFO))
LOG.info("Local address " + scrubMacAddress(address));
if (!StringUtils.isNullOrEmpty(address)) {
// Advertise the Bluetooth address to contacts
TransportProperties p = new TransportProperties();
p.put(PROP_ADDRESS, address);
callback.mergeLocalProperties(p);
}
// Bind a server socket to accept connections from contacts
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", getUuid());
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return;
}
if (!isRunning()) {
tryToClose(ss);
return;
}
LOG.info("Socket bound");
socket = ss;
backoff.reset();
callback.transportEnabled();
acceptContactConnections();
});
}
private UUID getUuid() {
String uuid = callback.getLocalProperties().get(PROP_UUID);
if (uuid == null) {
byte[] random = new byte[UUID_BYTES];
secureRandom.nextBytes(random);
uuid = UUID.nameUUIDFromBytes(random).toString();
TransportProperties p = new TransportProperties();
p.put(PROP_UUID, uuid);
callback.mergeLocalProperties(p);
}
return UUID.fromString(uuid);
}
private void tryToClose(@Nullable BluetoothServerSocket ss) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally {
callback.transportDisabled();
}
}
private void acceptContactConnections() {
while (isRunning()) {
BluetoothSocket s;
try {
s = socket.accept();
} catch (IOException e) {
// This is expected when the socket is closed
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
return;
}
if (LOG.isLoggable(INFO)) {
String address = s.getRemoteDevice().getAddress();
LOG.info("Connection from " + scrubMacAddress(address));
}
backoff.reset();
callback.incomingConnectionCreated(wrapSocket(s));
}
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new DroidtoothTransportConnection(this, s);
}
private void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
public void stop() {
running = false;
if (receiver != null) appContext.unregisterReceiver(receiver);
tryToClose(socket);
disableAdapter();
}
private void disableAdapter() {
if (adapter != null && adapter.isEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
}
}
@Override
public boolean isRunning() {
return running && adapter != null && adapter.isEnabled();
}
@Override
public boolean shouldPoll() {
return true;
}
@Override
public int getPollingInterval() {
return backoff.getPollingInterval();
}
@Override
public void poll(Collection<ContactId> connected) {
if (!isRunning()) return;
backoff.increment();
// Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (connected.contains(c)) continue;
String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(() -> {
if (!running) return;
BluetoothSocket s = connect(address, uuid);
if (s != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, wrapSocket(s));
}
});
}
}
@Nullable
private BluetoothSocket connect(String address, String uuid) {
// Validate the address
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
if (LOG.isLoggable(WARNING))
// not scrubbing here to be able to figure out the problem
LOG.warning("Invalid address " + address);
return null;
}
// Validate the UUID
UUID u;
try {
u = UUID.fromString(uuid);
} catch (IllegalArgumentException e) {
if (LOG.isLoggable(WARNING)) LOG.warning("Invalid UUID " + uuid);
return null;
}
// Try to connect
BluetoothDevice d = adapter.getRemoteDevice(address);
BluetoothSocket s = null;
try {
s = d.createInsecureRfcommSocketToServiceRecord(u);
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address));
s.connect();
if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubMacAddress(address));
return s;
} catch (IOException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Failed to connect to " + scrubMacAddress(address)
+ ": " + e);
}
tryToClose(s);
return null;
}
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties(c);
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
BluetoothSocket s = connect(address, uuid);
if (s == null) return null;
return new DroidtoothTransportConnection(this, s);
}
@Override
public boolean supportsKeyAgreement() {
return true;
}
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (!isRunning()) return null;
// There's no point listening if we can't discover our own address
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
if (address.isEmpty()) return null;
// No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
// Bind a server socket for receiving key agreement connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", uuid);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
BdfList descriptor = new BdfList();
descriptor.add(TRANSPORT_ID_BLUETOOTH);
descriptor.add(StringUtils.macToBytes(address));
return new BluetoothKeyAgreementListener(descriptor, ss);
}
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
BluetoothSocket s = connect(address, uuid.toString());
if (s == null) return null;
return new DroidtoothTransportConnection(this, s);
}
private String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof EnableBluetoothEvent) {
enableAdapterAsync();
} else if (e instanceof DisableBluetoothEvent) {
disableAdapterAsync();
}
}
private void enableAdapterAsync() {
ioExecutor.execute(this::enableAdapter);
}
private void disableAdapterAsync() {
ioExecutor.execute(this::disableAdapter);
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON) {
LOG.info("Bluetooth enabled");
bind();
} else if (state == STATE_OFF) {
LOG.info("Bluetooth disabled");
tryToClose(socket);
}
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_NONE) {
LOG.info("Scan mode: None");
} else if (scanMode == SCAN_MODE_CONNECTABLE) {
LOG.info("Scan mode: Connectable");
} else if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
LOG.info("Scan mode: Discoverable");
}
}
}
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
private final BluetoothServerSocket ss;
private BluetoothKeyAgreementListener(BdfList descriptor,
BluetoothServerSocket ss) {
super(descriptor);
this.ss = ss;
}
@Override
public Callable<KeyAgreementConnection> listen() {
return () -> {
BluetoothSocket s = ss.accept();
if (LOG.isLoggable(INFO))
LOG.info(ID.getString() + ": Incoming connection");
return new KeyAgreementConnection(
new DroidtoothTransportConnection(
DroidtoothPlugin.this, s), ID);
};
}
@Override
public void close() {
try {
ss.close();
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.plugin.bluetooth;
package org.briarproject.bramble.plugin.droidtooth;
import android.content.Context;
@@ -21,7 +21,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
@Immutable
@NotNullByDefault
public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
public class DroidtoothPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
@@ -35,7 +35,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private final EventBus eventBus;
private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor,
public DroidtoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus,
BackoffFactory backoffFactory) {
@@ -59,13 +59,11 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY);
DroidtoothPlugin plugin = new DroidtoothPlugin(ioExecutor,
androidExecutor, appContext, secureRandom, backoff, callback,
MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.plugin.bluetooth;
package org.briarproject.bramble.plugin.droidtooth;
import android.bluetooth.BluetoothSocket;
@@ -11,17 +11,12 @@ import java.io.InputStream;
import java.io.OutputStream;
@NotNullByDefault
class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
class DroidtoothTransportConnection extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionManager;
private final BluetoothSocket socket;
AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionManager,
BluetoothSocket socket) {
DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
super(plugin);
this.connectionManager = connectionManager;
this.socket = socket;
}
@@ -37,10 +32,6 @@ class AndroidBluetoothTransportConnection
@Override
protected void closeConnection(boolean exception) throws IOException {
try {
socket.close();
} finally {
connectionManager.connectionClosed(this);
}
socket.close();
}
}

View File

@@ -5,84 +5,37 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.net.SocketFactory;
import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.WIFI_SERVICE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.SECONDS;
@NotNullByDefault
class AndroidLanTcpPlugin extends LanTcpPlugin {
// See android.net.wifi.WifiManager
private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED";
private static final int WIFI_AP_STATE_ENABLED = 13;
private static final byte[] WIFI_AP_ADDRESS_BYTES =
{(byte) 192, (byte) 168, 43, 1};
private static final InetAddress WIFI_AP_ADDRESS;
private static final Logger LOG =
Logger.getLogger(AndroidLanTcpPlugin.class.getName());
static {
try {
WIFI_AP_ADDRESS = InetAddress.getByAddress(WIFI_AP_ADDRESS_BYTES);
} catch (UnknownHostException e) {
// Should only be thrown if the address has an illegal length
throw new AssertionError(e);
}
}
private final ScheduledExecutorService scheduler;
private final Context appContext;
private final ConnectivityManager connectivityManager;
@Nullable
private final WifiManager wifiManager;
@Nullable
private volatile BroadcastReceiver networkStateReceiver = null;
private volatile SocketFactory socketFactory;
AndroidLanTcpPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Backoff backoff, Context appContext, DuplexPluginCallback callback,
int maxLatency, int maxIdleTime) {
AndroidLanTcpPlugin(Executor ioExecutor, Backoff backoff,
Context appContext, DuplexPluginCallback callback, int maxLatency,
int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
this.scheduler = scheduler;
this.appContext = appContext;
ConnectivityManager connectivityManager = (ConnectivityManager)
appContext.getSystemService(CONNECTIVITY_SERVICE);
if (connectivityManager == null) throw new AssertionError();
this.connectivityManager = connectivityManager;
wifiManager = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
socketFactory = SocketFactory.getDefault();
}
@Override
@@ -91,9 +44,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
running = true;
// Register to receive network status events
networkStateReceiver = new NetworkStateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
appContext.registerReceiver(networkStateReceiver, filter);
}
@@ -105,92 +56,21 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
tryToClose(socket);
}
@Override
protected Socket createSocket() throws IOException {
return socketFactory.createSocket();
}
@Override
protected Collection<InetAddress> getLocalIpAddresses() {
// If the device doesn't have wifi, don't open any sockets
if (wifiManager == null) return emptyList();
// If we're connected to a wifi network, use that network
WifiInfo info = wifiManager.getConnectionInfo();
if (info != null && info.getIpAddress() != 0)
return singletonList(intToInetAddress(info.getIpAddress()));
// If we're running an access point, return its address
if (super.getLocalIpAddresses().contains(WIFI_AP_ADDRESS))
return singletonList(WIFI_AP_ADDRESS);
// No suitable addresses
return emptyList();
}
private InetAddress intToInetAddress(int ip) {
byte[] ipBytes = new byte[4];
ipBytes[0] = (byte) (ip & 0xFF);
ipBytes[1] = (byte) ((ip >> 8) & 0xFF);
ipBytes[2] = (byte) ((ip >> 16) & 0xFF);
ipBytes[3] = (byte) ((ip >> 24) & 0xFF);
try {
return InetAddress.getByAddress(ipBytes);
} catch (UnknownHostException e) {
// Should only be thrown if address has illegal length
throw new AssertionError(e);
}
}
// On API 21 and later, a socket that is not created with the wifi
// network's socket factory may try to connect via another network
private SocketFactory getSocketFactory() {
if (SDK_INT < 21) return SocketFactory.getDefault();
for (Network net : connectivityManager.getAllNetworks()) {
NetworkInfo info = connectivityManager.getNetworkInfo(net);
if (info != null && info.getType() == TYPE_WIFI)
return net.getSocketFactory();
}
LOG.warning("Could not find suitable socket factory");
return SocketFactory.getDefault();
}
private class NetworkStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent i) {
if (!running) return;
if (isApEnabledEvent(i)) {
// The state change may be broadcast before the AP address is
// visible, so delay handling the event
scheduler.schedule(this::handleConnectivityChange, 1, SECONDS);
} else {
handleConnectivityChange();
}
}
private void handleConnectivityChange() {
if (!running) return;
Collection<InetAddress> addrs = getLocalIpAddresses();
if (addrs.contains(WIFI_AP_ADDRESS)) {
LOG.info("Providing wifi hotspot");
// There's no corresponding Network object and thus no way
// to get a suitable socket factory, so we won't be able to
// make outgoing connections on API 21+ if another network
// has internet access
socketFactory = SocketFactory.getDefault();
Object o = ctx.getSystemService(CONNECTIVITY_SERVICE);
ConnectivityManager cm = (ConnectivityManager) o;
NetworkInfo net = cm.getActiveNetworkInfo();
if (net != null && net.getType() == TYPE_WIFI && net.isConnected()) {
LOG.info("Connected to Wi-Fi");
if (socket == null || socket.isClosed()) bind();
} else if (addrs.isEmpty()) {
LOG.info("Not connected to wifi");
socketFactory = SocketFactory.getDefault();
} else {
LOG.info("Not connected to Wi-Fi");
tryToClose(socket);
} else {
LOG.info("Connected to wifi");
socketFactory = getSocketFactory();
if (socket == null || socket.isClosed()) bind();
}
}
private boolean isApEnabledEvent(Intent i) {
return WIFI_AP_STATE_CHANGED_ACTION.equals(i.getAction()) &&
i.getIntExtra(EXTRA_WIFI_STATE, 0) == WIFI_AP_STATE_ENABLED;
}
}
}

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.concurrent.Immutable;
@@ -28,15 +27,12 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final BackoffFactory backoffFactory;
private final Context appContext;
public AndroidLanTcpPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, BackoffFactory backoffFactory,
Context appContext) {
BackoffFactory backoffFactory, Context appContext) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.backoffFactory = backoffFactory;
this.appContext = appContext;
}
@@ -55,7 +51,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
return new AndroidLanTcpPlugin(ioExecutor, scheduler, backoff,
appContext, callback, MAX_LATENCY, MAX_IDLE_TIME);
return new AndroidLanTcpPlugin(ioExecutor, backoff, appContext,
callback, MAX_LATENCY, MAX_IDLE_TIME);
}
}

View File

@@ -16,7 +16,6 @@ import android.os.PowerManager;
import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
@@ -32,9 +31,11 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
@@ -58,10 +59,7 @@ import java.util.Map.Entry;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
@@ -72,15 +70,10 @@ import javax.net.SocketFactory;
import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.POWER_SERVICE;
import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
@@ -108,10 +101,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final Logger LOG =
Logger.getLogger(TorPlugin.class.getName());
private final Executor ioExecutor, connectionStatusExecutor;
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor;
private final Context appContext;
private final LocationUtils locationUtils;
private final DevReporter reporter;
private final SocketFactory torSocketFactory;
private final Backoff backoff;
private final DuplexPluginCallback callback;
@@ -121,8 +114,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final File torDirectory, torFile, geoIpFile, configFile;
private final File doneFile, cookieFile;
private final PowerManager.WakeLock wakeLock;
private final AtomicReference<Future<?>> connectivityCheck =
new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false;
@@ -131,15 +122,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private volatile TorControlConnection controlConnection = null;
private volatile BroadcastReceiver networkStateReceiver = null;
TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Context appContext, LocationUtils locationUtils,
TorPlugin(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, DevReporter reporter,
SocketFactory torSocketFactory, Backoff backoff,
DuplexPluginCallback callback, String architecture,
int maxLatency, int maxIdleTime) {
DuplexPluginCallback callback, String architecture, int maxLatency,
int maxIdleTime) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.reporter = reporter;
this.torSocketFactory = torSocketFactory;
this.backoff = backoff;
this.callback = callback;
@@ -161,9 +152,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// This tag will prevent Huawei's powermanager from killing us.
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService");
wakeLock.setReferenceCounted(false);
// Don't execute more than one connection status check at a time
connectionStatusExecutor = new PoliteExecutor("TorPlugin",
ioExecutor, 1);
}
@Override
@@ -216,11 +204,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) {
Scanner stdout = new Scanner(torProcess.getInputStream());
Scanner stderr = new Scanner(torProcess.getErrorStream());
while (stdout.hasNextLine() || stderr.hasNextLine()) {
if (stdout.hasNextLine()) {
while (stdout.hasNextLine() || stderr.hasNextLine()){
if(stdout.hasNextLine()) {
LOG.info(stdout.nextLine());
}
if (stderr.hasNextLine()) {
if(stderr.hasNextLine()){
LOG.info(stderr.nextLine());
}
}
@@ -269,11 +257,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
// Register to receive network status events
networkStateReceiver = new NetworkStateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(ACTION_SCREEN_ON);
filter.addAction(ACTION_SCREEN_OFF);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
appContext.registerReceiver(networkStateReceiver, filter);
// Bind a server socket to receive incoming hidden service connections
bind();
@@ -385,6 +369,14 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
}
private void sendDevReports() {
ioExecutor.execute(() -> {
// TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
});
}
private void bind() {
ioExecutor.execute(() -> {
// If there's already a port number stored in config, reuse it
@@ -602,7 +594,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) {
byte[] commitment, BdfList descriptor, long timeout) {
throw new UnsupportedOperationException();
}
@@ -612,7 +604,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
connectionStatus.getAndSetCircuitBuilt()) {
LOG.info("First circuit built");
backoff.reset();
if (isRunning()) callback.transportEnabled();
if (isRunning()) {
sendDevReports();
callback.transportEnabled();
}
}
}
@@ -623,8 +618,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public void orConnStatus(String status, String orName) {
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
if (status.equals("CLOSED") || status.equals("FAILED"))
updateConnectionStatus(); // Check whether we've lost connectivity
}
@Override
@@ -641,7 +634,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
connectionStatus.setBootstrapped();
backoff.reset();
if (isRunning()) callback.transportEnabled();
if (isRunning()) {
sendDevReports();
callback.transportEnabled();
}
}
}
@@ -661,7 +657,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
@Override
public void onEvent(int event, @Nullable String path) {
public void onEvent(int event, String path) {
stopWatching();
latch.countDown();
}
@@ -679,8 +675,9 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
private void updateConnectionStatus() {
connectionStatusExecutor.execute(() -> {
ioExecutor.execute(() -> {
if (!running) return;
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
ConnectivityManager cm = (ConnectivityManager) o;
NetworkInfo net = cm.getActiveNetworkInfo();
@@ -719,25 +716,14 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
});
}
private void scheduleConnectionStatusUpdate() {
Future<?> newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, 1, MINUTES);
Future<?> oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
}
private class NetworkStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent i) {
if (!running) return;
String action = i.getAction();
if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action);
updateConnectionStatus();
if (ACTION_SCREEN_ON.equals(action)
|| ACTION_SCREEN_OFF.equals(action)) {
scheduleConnectionStatusUpdate();
if (CONNECTIVITY_ACTION.equals(i.getAction())) {
LOG.info("Detected connectivity change");
updateConnectionStatus();
}
}
}
@@ -760,7 +746,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private synchronized void enableNetwork(boolean enable) {
networkEnabled = enable;
if (!enable) circuitBuilt = false;
circuitBuilt = false;
}
private synchronized boolean isConnected() {

View File

@@ -12,11 +12,11 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
@@ -36,21 +36,21 @@ public class TorPluginFactory implements DuplexPluginFactory {
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final Context appContext;
private final LocationUtils locationUtils;
private final DevReporter reporter;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory;
public TorPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, Context appContext,
LocationUtils locationUtils, EventBus eventBus,
SocketFactory torSocketFactory, BackoffFactory backoffFactory) {
public TorPluginFactory(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.reporter = reporter;
this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory;
this.backoffFactory = backoffFactory;
@@ -89,9 +89,9 @@ public class TorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext,
locationUtils, torSocketFactory, backoff, callback,
architecture, MAX_LATENCY, MAX_IDLE_TIME);
TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils,
reporter, torSocketFactory, backoff, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -9,7 +9,6 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Parcel;
import android.os.StrictMode;
import android.provider.Settings;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -67,12 +66,9 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
@Override
protected void writeSeed() {
// Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed();
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix();
StrictMode.setThreadPolicy(tp);
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html

View File

@@ -1,9 +1,7 @@
package org.briarproject.bramble.util;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.provider.Settings;
@@ -12,16 +10,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import static android.content.Context.MODE_PRIVATE;
import static java.util.logging.Level.INFO;
public class AndroidUtils {
private static final Logger LOG =
Logger.getLogger(AndroidUtils.class.getName());
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
@@ -42,7 +35,6 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds")
String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) return address;
// Return the address from settings if it's valid and not fake
@@ -59,57 +51,17 @@ public class AndroidUtils {
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
}
public static void logDataDirContents(Context ctx) {
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of data directory:");
logFileOrDir(new File(ctx.getApplicationInfo().dataDir));
}
}
private static void logFileOrDir(File f) {
LOG.info(f.getAbsolutePath() + " " + f.length());
if (f.isDirectory()) {
File[] children = f.listFiles();
if (children == null) {
LOG.info("Could not list files in " + f.getAbsolutePath());
} else {
for (File child : children) logFileOrDir(child);
}
}
}
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
// Clear and commit shared preferences
for (SharedPreferences prefs : clear) {
boolean cleared = prefs.edit().clear().commit();
if (LOG.isLoggable(INFO)) {
if (cleared) LOG.info("Cleared shared preferences");
else LOG.info("Could not clear shared preferences");
}
}
// Delete files, except lib and shared_prefs directories
public static void deleteAppData(Context ctx) {
File dataDir = new File(ctx.getApplicationInfo().dataDir);
if (LOG.isLoggable(INFO))
LOG.info("Deleting app data from " + dataDir.getAbsolutePath());
File[] children = dataDir.listFiles();
if (children != null) {
for (File child : children) {
String name = child.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) {
if (LOG.isLoggable(INFO))
LOG.info("Deleting " + child.getAbsolutePath());
if (!child.getName().equals("lib"))
IoUtils.deleteFileOrDir(child);
}
}
} else if (LOG.isLoggable(INFO)) {
LOG.info("Could not list files in " + dataDir.getAbsolutePath());
}
// Recreate the cache dir as some OpenGL drivers expect it to exist
boolean recreated = new File(dataDir, "cache").mkdir();
if (LOG.isLoggable(INFO)) {
if (recreated) LOG.info("Recreated cache dir");
else LOG.info("Could not recreate cache dir");
}
new File(dataDir, "cache").mkdir();
}
public static File getReportDir(Context ctx) {

View File

@@ -2,7 +2,6 @@ apply plugin: 'java-library'
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'ru.vyarus.animalsniffer'
apply plugin: 'witness'
dependencies {
@@ -15,8 +14,6 @@ dependencies {
testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3"
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
}
dependencyVerification {
@@ -29,9 +26,6 @@ dependencyVerification {
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
@@ -39,7 +33,6 @@ dependencyVerification {
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
]
}
@@ -55,3 +48,8 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts {
testOutput jarTest
}
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -1,101 +0,0 @@
package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
public class Multiset<T> {
private final Map<T, Integer> map = new HashMap<>();
private int total = 0;
/**
* Returns how many items the multiset contains in total.
*/
public int getTotal() {
return total;
}
/**
* Returns how many unique items the multiset contains.
*/
public int getUnique() {
return map.size();
}
/**
* Returns how many of the given item the multiset contains.
*/
public int getCount(T t) {
Integer count = map.get(t);
return count == null ? 0 : count;
}
/**
* Adds the given item to the multiset and returns how many of the item
* the multiset now contains.
*/
public int add(T t) {
Integer count = map.get(t);
if (count == null) count = 0;
map.put(t, count + 1);
total++;
return count + 1;
}
/**
* Removes the given item from the multiset and returns how many of the
* item the multiset now contains.
* @throws NoSuchElementException if the item is not in the multiset.
*/
public int remove(T t) {
Integer count = map.get(t);
if (count == null) throw new NoSuchElementException();
if (count == 1) map.remove(t);
else map.put(t, count - 1);
total--;
return count - 1;
}
/**
* Removes all occurrences of the given item from the multiset.
*/
public int removeAll(T t) {
Integer count = map.remove(t);
if (count == null) return 0;
total -= count;
return count;
}
/**
* Returns true if the multiset contains any occurrences of the given item.
*/
public boolean contains(T t) {
return map.containsKey(t);
}
/**
* Removes all items from the multiset.
*/
public void clear() {
map.clear();
total = 0;
}
/**
* Returns the set of unique items the multiset contains. The returned set
* is unmodifiable.
*/
public Set<T> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api;
import java.io.IOException;
/**
* An exception that indicates an unrecoverable version mismatch.
*/
public class UnsupportedVersionException extends IOException {
}

View File

@@ -5,10 +5,7 @@ import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
@@ -90,30 +87,16 @@ public interface ClientHelper {
BdfDictionary toDictionary(byte[] b, int off, int len)
throws FormatException;
BdfDictionary toDictionary(TransportProperties transportProperties);
BdfDictionary toDictionary(Map<TransportId, TransportProperties> map);
BdfList toList(byte[] b, int off, int len) throws FormatException;
BdfList toList(byte[] b) throws FormatException;
BdfList toList(Message m) throws FormatException;
BdfList toList(Author a);
byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException;
void verifySignature(byte[] signature, String label, BdfList signed,
byte[] publicKey) throws FormatException, GeneralSecurityException;
Author parseAndValidateAuthor(BdfList author) throws FormatException;
TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException;
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
BdfDictionary properties) throws FormatException;
void verifySignature(String label, byte[] sig, byte[] publicKey,
BdfList signed) throws FormatException, GeneralSecurityException;
}

View File

@@ -12,19 +12,18 @@ public interface ContactGroupFactory {
/**
* Creates a group that is not shared with any contacts.
*/
Group createLocalGroup(ClientId clientId, int majorVersion);
Group createLocalGroup(ClientId clientId);
/**
* Creates a group for the given client to share with the given contact.
*/
Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact);
Group createContactGroup(ClientId clientId, Contact contact);
/**
* Creates a group for the given client to share between the given authors
* identified by their AuthorIds.
*/
Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2);
Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId2);
}

View File

@@ -12,32 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault
public interface ContactExchangeTask {
/**
* The current version of the contact exchange protocol.
*/
byte PROTOCOL_VERSION = 1;
/**
* Label for deriving Alice's header key from the master secret.
*/
String ALICE_KEY_LABEL =
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
/**
* Label for deriving Bob's header key from the master secret.
*/
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
/**
* Label for deriving Alice's key binding nonce from the master secret.
*/
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
/**
* Label for deriving Bob's key binding nonce from the master secret.
*/
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
/**
* Exchanges contact information with a remote peer.
*/

View File

@@ -6,7 +6,7 @@ import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for an integer that uniquely identifies a contact within
* the scope of the local device.
* the scope of a single node.
*/
@Immutable
@NotNullByDefault

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection;
@@ -14,40 +13,30 @@ import java.util.Collection;
public interface ContactManager {
/**
* Registers a hook to be called whenever a contact is added or removed.
* This method should be called before
* {@link LifecycleManager#startServices(String)}.
* Registers a hook to be called whenever a contact is added.
*/
void registerContactHook(ContactHook hook);
void registerAddContactHook(AddContactHook hook);
/**
* Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID
* for the contact.
*
* @param alice true if the local party is Alice
* Registers a hook to be called whenever a contact is removed.
*/
void registerRemoveContactHook(RemoveContactHook hook);
/**
* Stores a contact within the given transaction associated with the given
* local and remote pseudonyms, and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID
* for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active)
throws DbException;
ContactId addContact(Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
/**
* Returns the contact with the given ID.
@@ -105,10 +94,11 @@ public interface ContactManager {
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
interface ContactHook {
interface AddContactHook {
void addingContact(Transaction txn, Contact c) throws DbException;
}
interface RemoveContactHook {
void removingContact(Transaction txn, Contact c) throws DbException;
}
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.contact;
/**
* Record types for the contact exchange protocol.
*/
public interface RecordTypes {
byte CONTACT_INFO = 0;
}

View File

@@ -1,13 +1,11 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import javax.annotation.Nullable;
@NotNullByDefault
public interface CryptoComponent {
SecretKey generateSecretKey();
@@ -25,83 +23,153 @@ public interface CryptoComponent {
KeyParser getMessageKeyParser();
/**
* Derives another secret key from the given secret key.
*
* @param label a namespaced label indicating the purpose of the derived
* key, to prevent it from being repurposed or colliding with a key derived
* for another purpose
* Derives a stream header key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
*/
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs);
SecretKey deriveHeaderKey(SecretKey master, boolean alice);
/**
* Derives a message authentication code key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
*/
SecretKey deriveMacKey(SecretKey master, boolean alice);
/**
* Derives a nonce from the given master secret for one of the parties to
* sign.
* @param alice whether the nonce is for use by Alice or Bob.
*/
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
/**
* Derives a commitment to the provided public key.
* <p/>
* Part of BQP.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(byte[] publicKey);
/**
* Derives a common shared secret from two public keys and one of the
* corresponding private keys.
* <p/>
* Part of BQP.
*
* @param label a namespaced label indicating the purpose of this shared
* secret, to prevent it from being repurposed or colliding with a shared
* secret derived for another purpose
* @param theirPublicKey the public key of the remote party
* @param ourKeyPair the key pair of the local party
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @return the shared secret
* @throws GeneralSecurityException
*/
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
KeyPair ourKeyPair, byte[]... inputs)
throws GeneralSecurityException;
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/**
* Signs the given byte[] with the given private key.
* Derives the content of a confirmation record.
* <p/>
* Part of BQP.
*
* @param label a namespaced label indicating the purpose of this
* signature, to prevent it from being repurposed or colliding with a
* signature created for another purpose
* @param sharedSecret the common shared secret
* @param theirPayload the commit payload from the remote party
* @param ourPayload the commit payload we sent
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
/**
* Derives a master secret from the given shared secret.
* <p/>
* Part of BQP.
*
* @param sharedSecret the common shared secret
* @return the master secret
*/
SecretKey deriveMasterSecret(SecretKey sharedSecret);
/**
* Derives a master secret from two public keys and one of the corresponding
* private keys.
* <p/>
* This is a helper method that calls
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
*
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @return the shared secret
* @throws GeneralSecurityException
*/
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/**
* Derives initial transport keys for the given transport in the given
* rotation period from the given master secret.
* @param alice whether the keys are for use by Alice or Bob.
*/
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long rotationPeriod, boolean alice);
/**
* Rotates the given transport keys to the given rotation period. If the
* keys are for a future rotation period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
/**
* Signs the given byte[] with the given PrivateKey.
*
* @param label A label specific to this signature
* to ensure that the signature cannot be repurposed
*/
byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException;
/**
* Verifies that the given signature is valid for the signed data
* and the given public key.
* Verifies that the given signature is valid for the signedData
* and the given publicKey.
*
* @param label a namespaced label indicating the purpose of this
* signature, to prevent it from being repurposed or colliding with a
* signature created for another purpose
* @param label A label that was specific to this signature
* to ensure that the signature cannot be repurposed
* @return true if the signature was valid, false otherwise.
*/
boolean verifySignature(byte[] signature, String label, byte[] signed,
byte[] publicKey) throws GeneralSecurityException;
boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException;
/**
* Returns the hash of the given inputs. The inputs are unambiguously
* combined by prefixing each input with its length.
*
* @param label a namespaced label indicating the purpose of this hash, to
* prevent it from being repurposed or colliding with a hash created for
* another purpose
* @param label A label specific to this hash to ensure that hashes
* calculated for distinct purposes don't collide.
*/
byte[] hash(String label, byte[]... inputs);
/**
* Returns the length of hashes produced by
* the {@link CryptoComponent#hash(String, byte[]...)} method.
*/
int getHashLength();
/**
* Returns a message authentication code with the given key over the
* given inputs. The inputs are unambiguously combined by prefixing each
* input with its length.
*
* @param label a namespaced label indicating the purpose of this MAC, to
* prevent it from being repurposed or colliding with a MAC created for
* another purpose
*/
byte[] mac(String label, SecretKey macKey, byte[]... inputs);
/**
* Verifies that the given message authentication code is valid for the
* given secret key and inputs.
*
* @param label a namespaced label indicating the purpose of this MAC, to
* prevent it from being repurposed or colliding with a MAC created for
* another purpose
* @return true if the MAC was valid, false otherwise.
*/
boolean verifyMac(byte[] mac, String label, SecretKey macKey,
byte[]... inputs);
byte[] mac(SecretKey macKey, byte[]... inputs);
/**
* Encrypts and authenticates the given plaintext so it can be written to
@@ -117,7 +185,6 @@ public interface CryptoComponent {
* given password. Returns null if the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong).
*/
@Nullable
byte[] decryptWithPassword(byte[] ciphertext, String password);
/**

View File

@@ -1,25 +0,0 @@
package org.briarproject.bramble.api.crypto;
public interface CryptoConstants {
/**
* The maximum length of an agreement public key in bytes.
*/
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
/**
* The maximum length of a signature public key in bytes.
*/
int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32;
/**
* The maximum length of a signature in bytes.
*/
int MAX_SIGNATURE_BYTES = 64;
/**
* The length of a MAC in bytes.
*/
int MAC_BYTES = SecretKey.LENGTH;
}

View File

@@ -1,50 +0,0 @@
package org.briarproject.bramble.api.crypto;
/**
* Crypto operations for the key agreement protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
*/
public interface KeyAgreementCrypto {
/**
* Hash label for public key commitment.
*/
String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT";
/**
* Key derivation label for confirmation record.
*/
String CONFIRMATION_KEY_LABEL =
"org.briarproject.bramble.keyagreement/CONFIRMATION_KEY";
/**
* MAC label for confirmation record.
*/
String CONFIRMATION_MAC_LABEL =
"org.briarproject.bramble.keyagreement/CONFIRMATION_MAC";
/**
* Derives a commitment to the provided public key.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(PublicKey publicKey);
/**
* Derives the content of a confirmation record.
*
* @param sharedSecret the common shared secret
* @param theirPayload the key exchange payload of the remote party
* @param ourPayload the key exchange payload of the local party
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral key pair of the local party
* @param alice true if the local party is Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
PublicKey theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey}.
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey).
*/
@Immutable
@NotNullByDefault

View File

@@ -1,33 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
/**
* Crypto operations for the transport security protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
*/
public interface TransportCrypto {
/**
* Derives initial transport keys for the given transport in the given
* rotation period from the given master secret.
*
* @param alice whether the keys are for use by Alice or Bob.
* @param active whether the keys are usable for outgoing streams.
*/
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long rotationPeriod, boolean alice, boolean active);
/**
* Rotates the given transport keys to the given rotation period. If the
* keys are for the given period or any later period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/**
* Encodes the pseudo-random tag that is used to recognise a stream.
*/
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
}

View File

@@ -24,9 +24,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
* );
* </pre>
*/
public static BdfDictionary of(Entry<String, ?>... entries) {
public static BdfDictionary of(Entry<String, Object>... entries) {
BdfDictionary d = new BdfDictionary();
for (Entry<String, ?> e : entries) d.put(e.getKey(), e.getValue());
for (Entry<String, Object> e : entries) d.put(e.getKey(), e.getValue());
return d;
}
@@ -34,7 +34,7 @@ public class BdfDictionary extends TreeMap<String, Object> {
super();
}
public BdfDictionary(Map<String, ?> m) {
public BdfDictionary(Map<String, Object> m) {
super(m);
}

View File

@@ -8,7 +8,6 @@ import java.io.IOException;
public interface BdfReader {
int DEFAULT_NESTED_LIMIT = 5;
int DEFAULT_MAX_BUFFER_SIZE = 64 * 1024;
boolean eof() throws IOException;
@@ -40,13 +39,13 @@ public interface BdfReader {
boolean hasString() throws IOException;
String readString() throws IOException;
String readString(int maxLength) throws IOException;
void skipString() throws IOException;
boolean hasRaw() throws IOException;
byte[] readRaw() throws IOException;
byte[] readRaw(int maxLength) throws IOException;
void skipRaw() throws IOException;

View File

@@ -9,6 +9,5 @@ public interface BdfReaderFactory {
BdfReader createReader(InputStream in);
BdfReader createReader(InputStream in, int nestedLimit,
int maxBufferSize);
BdfReader createReader(InputStream in, int nestedLimit);
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.bramble.api.data;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface ObjectReader<T> {
T readObject(BdfReader r) throws IOException;
}

View File

@@ -1,7 +0,0 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when the database uses a newer schema than the current code.
*/
public class DataTooNewException extends DbException {
}

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when the database uses an older schema than the current code and
* cannot be migrated.
*/
public class DataTooOldException extends DbException {
}

View File

@@ -18,8 +18,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection;
@@ -38,13 +37,8 @@ public interface DatabaseComponent {
/**
* Opens the database and returns true if the database already existed.
*
* @throws DataTooNewException if the data uses a newer schema than the
* current code
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/
boolean open(@Nullable MigrationListener listener) throws DbException;
boolean open() throws DbException;
/**
* Waits for any open transactions to finish and closes the database.
@@ -104,11 +98,10 @@ public interface DatabaseComponent {
throws DbException;
/**
* Stores the given transport keys for the given contact and returns a
* key set ID.
* Stores transport keys for a newly added contact.
*/
KeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException;
void addTransportKeys(Transaction txn, ContactId c, TransportKeys k)
throws DbException;
/**
* Returns true if the database contains the given contact for the given
@@ -130,8 +123,8 @@ public interface DatabaseComponent {
/**
* Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID,
* dependencies, metadata, and any other associated state are not deleted.
* {@link #removeMessage(Transaction, MessageId)}, the message ID and any
* other associated data are not deleted.
*/
void deleteMessage(Transaction txn, MessageId m) throws DbException;
@@ -235,8 +228,7 @@ public interface DatabaseComponent {
* <p/>
* Read-only.
*/
Collection<Group> getGroups(Transaction txn, ClientId c, int majorVersion)
throws DbException;
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
/**
* Returns the given group's visibility to the given contact, or
@@ -262,38 +254,31 @@ public interface DatabaseComponent {
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/**
* Returns the IDs of all delivered messages in the given group.
* Returns the IDs of any messages that need to be validated by the given
* client.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
throws DbException;
/**
* Returns the IDs of any messages that need to be validated.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToValidate(Transaction txn)
Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that are pending delivery due to
* dependencies on other messages.
* Returns the IDs of any messages that are valid but pending delivery due
* to dependencies on other messages for the given client.
* <p/>
* Read-only.
*/
Collection<MessageId> getPendingMessages(Transaction txn)
Collection<MessageId> getPendingMessages(Transaction txn, ClientId c)
throws DbException;
/**
* Returns the IDs of any messages that have shared dependents but have
* not yet been shared themselves.
* Returns the IDs of any messages from the given client
* that have a shared dependent, but are still not shared themselves.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessagesToShare(Transaction txn)
throws DbException;
Collection<MessageId> getMessagesToShare(Transaction txn,
ClientId c) throws DbException;
/**
* Returns the message with the given ID, in serialised form, or null if
@@ -313,9 +298,9 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the metadata for any delivered messages in the given group with
* metadata that matches all entries in the given query. If the query is
* empty, the metadata for all delivered messages is returned.
* Returns the metadata for any messages in the given group with metadata
* that matches all entries in the given query. If the query is empty, the
* metadata for all messages is returned.
* <p/>
* Read-only.
*/
@@ -331,8 +316,8 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the metadata for the given delivered or pending message.
* This is only meant to be used by the ValidationManager.
* Returns the metadata for the given delivered and pending message.
* This is meant to be only used by the ValidationManager
* <p/>
* Read-only.
*/
@@ -340,8 +325,8 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the status of all delivered messages in the given group with
* respect to the given contact.
* Returns the status of all messages in the given group with respect to
* the given contact.
* <p/>
* Read-only.
*/
@@ -350,8 +335,12 @@ public interface DatabaseComponent {
/**
* Returns the IDs and states of all dependencies of the given message.
* For missing dependencies and dependencies in other groups, the state
* {@link State UNKNOWN} is returned.
* Missing dependencies have the state
* {@link ValidationManager.State UNKNOWN}.
* Dependencies in other groups have the state
* {@link ValidationManager.State INVALID}.
* Note that these states are not set on the dependencies themselves; the
* returned states should only be taken in the context of the given message.
* <p/>
* Read-only.
*/
@@ -359,9 +348,9 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the IDs and states of all dependents of the given message.
* Dependents in other groups are not returned. If the given message is
* missing, no dependents are returned.
* Returns all IDs of messages that depend on the given message.
* Messages in other groups that declare a dependency on the given message
* will be returned even though such dependencies are invalid.
* <p/>
* Read-only.
*/
@@ -376,24 +365,14 @@ public interface DatabaseComponent {
State getMessageState(Transaction txn, MessageId m) throws DbException;
/**
* Returns the status of the given delivered message with respect to the
* given contact.
* Returns the status of the given message with respect to the given
* contact.
* <p/>
* Read-only.
*/
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
throws DbException;
/*
* Returns the next time (in milliseconds since the Unix epoch) when a
* message is due to be sent to the given contact. The returned value may
* be zero if a message is due to be sent immediately, or Long.MAX_VALUE if
* no messages are scheduled to be sent.
* <p/>
* Read-only.
*/
long getNextSendTime(Transaction txn, ContactId c) throws DbException;
/**
* Returns all settings in the given namespace.
* <p/>
@@ -406,14 +385,15 @@ public interface DatabaseComponent {
* <p/>
* Read-only.
*/
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t)
throws DbException;
Map<ContactId, TransportKeys> getTransportKeys(Transaction txn,
TransportId t) throws DbException;
/**
* Increments the outgoing stream counter for the given transport keys.
* Increments the outgoing stream counter for the given contact and
* transport in the given rotation period .
*/
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k)
throws DbException;
void incrementStreamCounter(Transaction txn, ContactId c, TransportId t,
long rotationPeriod) throws DbException;
/**
* Merges the given metadata with the existing metadata for the given
@@ -483,12 +463,6 @@ public interface DatabaseComponent {
*/
void removeTransport(Transaction txn, TransportId t) throws DbException;
/**
* Removes the given transport keys from the database.
*/
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/**
* Marks the given contact as verified.
*/
@@ -524,21 +498,15 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException;
/**
* Sets the reordering window for the given key set and transport in the
* Sets the reordering window for the given contact and transport in the
* given rotation period.
*/
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
void setReorderingWindow(Transaction txn, ContactId c, TransportId t,
long rotationPeriod, long base, byte[] bitmap) throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams.
*/
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/**
* Stores the given transport keys, deleting any keys they have replaced.
*/
void updateTransportKeys(Transaction txn, Collection<KeySet> keys)
throws DbException;
void updateTransportKeys(Transaction txn,
Map<ContactId, TransportKeys> keys) throws DbException;
}

View File

@@ -14,8 +14,6 @@ public interface DatabaseConfig {
File getDatabaseDirectory();
File getDatabaseKeyDirectory();
void setEncryptionKey(SecretKey key);
@Nullable

View File

@@ -1,11 +0,0 @@
package org.briarproject.bramble.api.db;
public interface MigrationListener {
/**
* This is called when a migration is started while opening the database.
* It will be called once for each migration being applied.
*/
void onMigrationRun();
}

View File

@@ -1,13 +1,11 @@
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import java.io.UnsupportedEncodingException;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/**
* A pseudonym for a user.
*/
@@ -19,25 +17,20 @@ public class Author {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
}
/**
* The current version of the author structure.
*/
public static final int FORMAT_VERSION = 1;
private final AuthorId id;
private final int formatVersion;
private final String name;
private final byte[] publicKey;
public Author(AuthorId id, int formatVersion, String name,
byte[] publicKey) {
int nameLength = StringUtils.toUtf8(name).length;
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException();
if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH)
public Author(AuthorId id, String name, byte[] publicKey) {
int length;
try {
length = name.getBytes("UTF-8").length;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
if (length == 0 || length > AuthorConstants.MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException();
this.id = id;
this.formatVersion = formatVersion;
this.name = name;
this.publicKey = publicKey;
}
@@ -49,13 +42,6 @@ public class Author {
return id;
}
/**
* Returns the version of the author structure used to create the author.
*/
public int getFormatVersion() {
return formatVersion;
}
/**
* Returns the author's name.
*/

View File

@@ -1,8 +1,5 @@
package org.briarproject.bramble.api.identity;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
public interface AuthorConstants {
/**
@@ -11,14 +8,26 @@ public interface AuthorConstants {
int MAX_AUTHOR_NAME_LENGTH = 50;
/**
* The maximum length of a public key in bytes. This applies to the
* signature algorithm used by the current {@link Author format version}.
* The maximum length of a public key in bytes.
* <p>
* Public keys use SEC1 format: 0x04 x y, where x and y are unsigned
* big-endian integers.
* <p>
* For a 256-bit elliptic curve, the maximum length is 2 * 256 / 8 + 1.
*/
int MAX_PUBLIC_KEY_LENGTH = MAX_SIGNATURE_PUBLIC_KEY_BYTES;
int MAX_PUBLIC_KEY_LENGTH = 65;
/**
* The maximum length of a signature in bytes. This applies to the
* signature algorithm used by the current {@link Author format version}.
* The maximum length of a signature in bytes.
* <p>
* A signature is an ASN.1 DER sequence containing two integers, r and s.
* The format is 0x30 len1 0x02 len2 r 0x02 len3 s, where len1 is
* len(0x02 len2 r 0x02 len3 s) as a DER length, len2 is len(r) as a DER
* length, len3 is len(s) as a DER length, and r and s are signed
* big-endian integers of minimal length.
* <p>
* For a 256-bit elliptic curve, the lengths are one byte each, so the
* maximum length is 2 * 256 / 8 + 8.
*/
int MAX_SIGNATURE_LENGTH = MAX_SIGNATURE_BYTES;
int MAX_SIGNATURE_LENGTH = 72;
}

View File

@@ -5,27 +5,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface AuthorFactory {
/**
* Creates an author with the current format version and the given name and
* public key.
*/
Author createAuthor(String name, byte[] publicKey);
/**
* Creates an author with the given format version, name and public key.
*/
Author createAuthor(int formatVersion, String name, byte[] publicKey);
/**
* Creates a local author with the current format version and the given
* name and keys.
*/
LocalAuthor createLocalAuthor(String name, byte[] publicKey,
byte[] privateKey);
/**
* Creates a local author with the given format version, name and keys.
*/
LocalAuthor createLocalAuthor(int formatVersion, String name,
byte[] publicKey, byte[] privateKey);
}

View File

@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
/**
* Label for hashing authors to calculate their identities.
*/
public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID";
public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
public AuthorId(byte[] id) {
super(id);

View File

@@ -14,9 +14,9 @@ public class LocalAuthor extends Author {
private final byte[] privateKey;
private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, long created) {
super(id, formatVersion, name, publicKey);
public LocalAuthor(AuthorId id, String name, byte[] publicKey,
byte[] privateKey, long created) {
super(id, name, publicKey);
this.privateKey = privateKey;
this.created = created;
}

View File

@@ -3,9 +3,9 @@ package org.briarproject.bramble.api.keyagreement;
public interface KeyAgreementConstants {
/**
* The current version of the BQP protocol. Version number 89 is reserved.
* The current version of the BQP protocol.
*/
byte PROTOCOL_VERSION = 4;
byte PROTOCOL_VERSION = 2;
/**
* The length of the record header in bytes.
@@ -22,10 +22,7 @@ public interface KeyAgreementConstants {
*/
int COMMIT_LENGTH = 16;
/**
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 20 * 1000;
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
/**
* The transport identifier for Bluetooth.
@@ -36,16 +33,4 @@ public interface KeyAgreementConstants {
* The transport identifier for LAN.
*/
int TRANSPORT_ID_LAN = 1;
/**
* Label for deriving the shared secret.
*/
String SHARED_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
/**
* Label for deriving the master secret.
*/
String MASTER_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
}

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.keyagreement;
import org.briarproject.bramble.api.data.BdfList;
import java.io.IOException;
import java.util.concurrent.Callable;
/**
* An class for managing a particular key agreement listener.
@@ -24,11 +24,11 @@ public abstract class KeyAgreementListener {
}
/**
* Blocks until an incoming connection is received and returns it.
*
* @throws IOException if an error occurs or {@link #close()} is called.
* Starts listening for incoming connections, and returns a Callable that
* will return a KeyAgreementConnection when an incoming connection is
* received.
*/
public abstract KeyAgreementConnection accept() throws IOException;
public abstract Callable<KeyAgreementConnection> listen();
/**
* Closes the underlying server socket.

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.keyagreement;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Manages tasks for conducting key agreements with remote peers.
*/
@NotNullByDefault
public interface KeyAgreementTaskFactory {
/**
* Gets the current key agreement task.
*/
KeyAgreementTask createTask();
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.keyagreement.event;
import org.briarproject.bramble.api.event.Event;
/**
* An event that is broadcast when a BQP task stops listening.
*/
public class KeyAgreementStoppedListeningEvent extends Event {
}

View File

@@ -21,42 +21,21 @@ public interface LifecycleManager {
* The result of calling {@link #startServices(String)}.
*/
enum StartResult {
ALREADY_RUNNING,
DB_ERROR,
DATA_TOO_OLD_ERROR,
DATA_TOO_NEW_ERROR,
SERVICE_ERROR,
SUCCESS
ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS
}
/**
* The state the lifecycle can be in.
* Returned by {@link #getLifecycleState()}
*/
enum LifecycleState {
STARTING, MIGRATING_DATABASE, STARTING_SERVICES, RUNNING, STOPPING;
public boolean isAfter(LifecycleState state) {
return ordinal() > state.ordinal();
}
}
/**
* Registers a {@link Service} to be started and stopped. This method
* should be called before {@link #startServices(String)}.
* Registers a {@link Service} to be started and stopped.
*/
void registerService(Service s);
/**
* Registers a {@link Client} to be started. This method should be called
* before {@link #startServices(String)}.
* Registers a {@link Client} to be started.
*/
void registerClient(Client c);
/**
* Registers an {@link ExecutorService} to be shut down. This method
* should be called before {@link #startServices(String)}.
* Registers an {@link ExecutorService} to be shut down.
*/
void registerForShutdown(ExecutorService e);
@@ -92,10 +71,4 @@ public interface LifecycleManager {
* the {@link DatabaseComponent} to be closed before returning.
*/
void waitForShutdown() throws InterruptedException;
/**
* Returns the current state of the lifecycle.
*/
LifecycleState getLifecycleState();
}

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.lifecycle.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState;
/**
* An event that is broadcast when the app enters a new lifecycle state.
*/
public class LifecycleEvent extends Event {
private final LifecycleState state;
public LifecycleEvent(LifecycleState state) {
this.state = state;
}
public LifecycleState getLifecycleState() {
return state;
}
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.lifecycle.event;
import org.briarproject.bramble.api.event.Event;
/**
* An event that is broadcast when the app is shutting down.
*/
public class ShutdownEvent extends Event {
}

View File

@@ -1,23 +1,22 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.util.StringUtils;
import java.nio.charset.Charset;
/**
* Type-safe wrapper for a namespaced string that uniquely identifies a
* transport plugin.
* Type-safe wrapper for a string that uniquely identifies a transport plugin.
*/
public class TransportId {
/**
* The maximum length of a transport identifier in UTF-8 bytes.
* The maximum length of transport identifier in UTF-8 bytes.
*/
public static int MAX_TRANSPORT_ID_LENGTH = 100;
public static int MAX_TRANSPORT_ID_LENGTH = 64;
private final String id;
public TransportId(String id) {
int length = StringUtils.toUtf8(id).length;
if (length == 0 || length > MAX_TRANSPORT_ID_LENGTH)
byte[] b = id.getBytes(Charset.forName("UTF-8"));
if (b.length == 0 || b.length > MAX_TRANSPORT_ID_LENGTH)
throw new IllegalArgumentException();
this.id = id;
}

View File

@@ -36,9 +36,9 @@ public interface DuplexPlugin extends Plugin {
/**
* Attempts to connect to the remote peer specified in the given descriptor.
* Returns null if no connection can be established.
* Returns null if no connection can be established within the given time.
*/
@Nullable
DuplexTransportConnection createKeyAgreementConnection(
byte[] remoteCommitment, BdfList descriptor);
byte[] remoteCommitment, BdfList descriptor, long timeout);
}

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that informs the Bluetooth plugin that we have enabled the
* Bluetooth adapter.
*/
@Immutable
@NotNullByDefault
public class BluetoothEnabledEvent extends Event {
}

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that asks the Bluetooth plugin to enable the Bluetooth adapter.
* An event asks the Bluetooth plugin to enable the Bluetooth adapter.
*/
@Immutable
@NotNullByDefault

View File

@@ -15,17 +15,7 @@ public interface TransportPropertyManager {
/**
* The unique ID of the transport property client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.properties");
/**
* The current major version of the transport property client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the transport property client.
*/
int MINOR_VERSION = 0;
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
/**
* Stores the given properties received while adding a contact - they will
@@ -42,8 +32,8 @@ public interface TransportPropertyManager {
/**
* Returns the local transport properties for all transports.
* <p/>
* Read-only.
* <br/>
* TODO: Transaction can be read-only when code is simplified
*/
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException;

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class Record {
public static final int RECORD_HEADER_BYTES = 4;
public static final int MAX_RECORD_PAYLOAD_BYTES = 48 * 1024; // 48 KiB
private final byte protocolVersion, recordType;
private final byte[] payload;
public Record(byte protocolVersion, byte recordType, byte[] payload) {
if (payload.length > MAX_RECORD_PAYLOAD_BYTES)
throw new IllegalArgumentException();
this.protocolVersion = protocolVersion;
this.recordType = recordType;
this.payload = payload;
}
public byte getProtocolVersion() {
return protocolVersion;
}
public byte getRecordType() {
return recordType;
}
public byte[] getPayload() {
return payload;
}
}

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.EOFException;
import java.io.IOException;
@NotNullByDefault
public interface RecordReader {
/**
* Reads and returns the next record.
*
* @throws EOFException if the end of the stream is reached without reading
* a complete record
*/
Record readRecord() throws IOException;
void close() throws IOException;
}

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.record;
import java.io.InputStream;
public interface RecordReaderFactory {
RecordReader createRecordReader(InputStream in);
}

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface RecordWriter {
void writeRecord(Record r) throws IOException;
void flush() throws IOException;
void close() throws IOException;
}

View File

@@ -1,8 +0,0 @@
package org.briarproject.bramble.api.record;
import java.io.OutputStream;
public interface RecordWriterFactory {
RecordWriter createRecordWriter(OutputStream out);
}

View File

@@ -3,14 +3,10 @@ package org.briarproject.bramble.api.reporting;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
@NotNullByDefault
public interface DevConfig {
PublicKey getDevPublicKey();
String getDevOnionAddress();
File getReportDir();
}

View File

@@ -23,6 +23,8 @@ public interface DevReporter {
/**
* Sends any reports previously stored on disk.
*
* @param reportDir the directory where reports are stored.
*/
void sendReports();
void sendReports(File reportDir);
}

View File

@@ -1,29 +1,19 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for a namespaced string that uniquely identifies a sync
* client.
* Wrapper for a name-spaced string that uniquely identifies a sync client.
*/
@Immutable
@NotNullByDefault
public class ClientId implements Comparable<ClientId> {
/**
* The maximum length of a client identifier in UTF-8 bytes.
*/
public static int MAX_CLIENT_ID_LENGTH = 100;
private final String id;
public ClientId(String id) {
int length = StringUtils.toUtf8(id).length;
if (length == 0 || length > MAX_CLIENT_ID_LENGTH)
throw new IllegalArgumentException();
this.id = id;
}
@@ -46,8 +36,4 @@ public class ClientId implements Comparable<ClientId> {
return id.hashCode();
}
@Override
public String toString() {
return id;
}
}

View File

@@ -5,43 +5,20 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
public class Group {
public enum Visibility {
INVISIBLE(0), // The group is not visible
VISIBLE(1), // The group is visible, messages are accepted but not sent
SHARED(2); // The group is visible, messages are accepted and sent
private final int value;
Visibility(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Visibility min(Visibility a, Visibility b) {
return a.getValue() < b.getValue() ? a : b;
}
INVISIBLE, // The group is not visible
VISIBLE, // The group is visible but messages are not shared
SHARED // The group is visible and messages are shared
}
/**
* The current version of the group format.
*/
public static final int FORMAT_VERSION = 1;
private final GroupId id;
private final ClientId clientId;
private final int majorVersion;
private final byte[] descriptor;
public Group(GroupId id, ClientId clientId, int majorVersion,
byte[] descriptor) {
public Group(GroupId id, ClientId clientId, byte[] descriptor) {
if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH)
throw new IllegalArgumentException();
this.id = id;
this.clientId = clientId;
this.majorVersion = majorVersion;
this.descriptor = descriptor;
}
@@ -59,13 +36,6 @@ public class Group {
return clientId;
}
/**
* Returns the major version of the client to which the group belongs.
*/
public int getMajorVersion() {
return majorVersion;
}
/**
* Returns the group's descriptor.
*/

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface GroupFactory {
/**
* Creates a group with the given client ID, major version and descriptor.
* Creates a group with the given client ID and descriptor.
*/
Group createGroup(ClientId c, int majorVersion, byte[] descriptor);
Group createGroup(ClientId c, byte[] descriptor);
}

View File

@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
/**
* Label for hashing groups to calculate their identifiers.
*/
public static final String LABEL = "org.briarproject.bramble/GROUP_ID";
public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
public GroupId(byte[] id) {
super(id);

View File

@@ -5,11 +5,6 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LEN
public class Message {
/**
* The current version of the message format.
*/
public static final int FORMAT_VERSION = 1;
private final MessageId id;
private final GroupId groupId;
private final long timestamp;

View File

@@ -7,7 +7,5 @@ public interface MessageFactory {
Message createMessage(GroupId g, long timestamp, byte[] body);
Message createMessage(byte[] raw);
Message createMessage(MessageId m, byte[] raw);
}

View File

@@ -16,13 +16,7 @@ public class MessageId extends UniqueId {
/**
* Label for hashing messages to calculate their identifiers.
*/
public static final String ID_LABEL = "org.briarproject.bramble/MESSAGE_ID";
/**
* Label for hashing blocks of messages.
*/
public static final String BLOCK_LABEL =
"org.briarproject.bramble/MESSAGE_BLOCK";
public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
public MessageId(byte[] id) {
super(id);

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface SyncRecordReader {
public interface RecordReader {
boolean eof() throws IOException;

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.InputStream;
@NotNullByDefault
public interface SyncRecordReaderFactory {
public interface RecordReaderFactory {
SyncRecordReader createRecordReader(InputStream in);
RecordReader createRecordReader(InputStream in);
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface SyncRecordWriter {
public interface RecordWriter {
void writeAck(Ack a) throws IOException;

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.OutputStream;
@NotNullByDefault
public interface SyncRecordWriterFactory {
public interface RecordWriterFactory {
SyncRecordWriter createRecordWriter(OutputStream out);
RecordWriter createRecordWriter(OutputStream out);
}

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.UniqueId;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
public interface SyncConstants {
/**
@@ -12,8 +10,16 @@ public interface SyncConstants {
byte PROTOCOL_VERSION = 0;
/**
* The maximum length of a group descriptor in bytes.
* The length of the record header in bytes.
*/
int RECORD_HEADER_LENGTH = 4;
/**
* The maximum length of the record payload in bytes.
*/
int MAX_RECORD_PAYLOAD_LENGTH = 48 * 1024; // 48 KiB
/** The maximum length of a group descriptor in bytes. */
int MAX_GROUP_DESCRIPTOR_LENGTH = 16 * 1024; // 16 KiB
/**
@@ -34,5 +40,5 @@ public interface SyncConstants {
/**
* The maximum number of message IDs in an ack, offer or request record.
*/
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_LENGTH / UniqueId.LENGTH;
}

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream;
import java.io.OutputStream;
@NotNullByDefault
public interface SyncSessionFactory {
@@ -12,8 +12,8 @@ public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
StreamWriter streamWriter);
OutputStream out);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, StreamWriter streamWriter);
int maxIdleTime, OutputStream out);
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
@@ -34,20 +33,15 @@ public interface ValidationManager {
}
/**
* Registers the message validator for the given client. This method
* should be called before {@link LifecycleManager#startServices(String)}.
* Sets the message validator for the given client.
*/
void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v);
void registerMessageValidator(ClientId c, MessageValidator v);
/**
* Registers the incoming message hook for the given client. The hook will
* be called once for each incoming message that passes validation. This
* method should be called before
* {@link LifecycleManager#startServices(String)}.
* Sets the incoming message hook for the given client. The hook will be
* called once for each incoming message that passes validation.
*/
void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook);
void registerIncomingMessageHook(ClientId c, IncomingMessageHook hook);
interface MessageValidator {

View File

@@ -7,12 +7,12 @@ package org.briarproject.bramble.api.system;
public interface Clock {
/**
* @see System#currentTimeMillis()
* @see {@link System#currentTimeMillis()}
*/
long currentTimeMillis();
/**
* @see Thread#sleep(long)
* @see {@link Thread#sleep(long)}
*/
void sleep(long milliseconds) throws InterruptedException;
}

View File

@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.plugin.TransportId;
import java.util.Map;
import javax.annotation.Nullable;
/**
@@ -18,30 +16,12 @@ public interface KeyManager {
/**
* Informs the key manager that a new contact has been added. Derives and
* stores a set of transport keys for communicating with the contact over
* each transport and returns the key set IDs.
* <p/>
* stores transport keys for communicating with the contact.
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams
*/
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
SecretKey master, long timestamp, boolean alice, boolean active)
throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams.
*/
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException;
/**
* Returns true if we have keys that can be used for outgoing streams to
* the given contact over the given transport.
*/
boolean canSendOutgoingStreams(ContactId c, TransportId t);
void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException;
/**
* Returns a {@link StreamContext} for sending a stream to the given

View File

@@ -1,47 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A set of transport keys for communicating with a contact.
*/
@Immutable
@NotNullByDefault
public class KeySet {
private final KeySetId keySetId;
private final ContactId contactId;
private final TransportKeys transportKeys;
public KeySet(KeySetId keySetId, ContactId contactId,
TransportKeys transportKeys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.transportKeys = transportKeys;
}
public KeySetId getKeySetId() {
return keySetId;
}
public ContactId getContactId() {
return contactId;
}
public TransportKeys getTransportKeys() {
return transportKeys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof KeySet && keySetId.equals(((KeySet) o).keySetId);
}
}

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for an integer that uniquely identifies a set of transport
* keys within the scope of the local device.
* <p/>
* Key sets created on a given device must have increasing identifiers.
*/
@Immutable
@NotNullByDefault
public class KeySetId {
private final int id;
public KeySetId(int id) {
this.id = id;
}
public int getInt() {
return id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return o instanceof KeySetId && id == ((KeySetId) o).id;
}
}

View File

@@ -10,20 +10,18 @@ public class OutgoingKeys {
private final SecretKey tagKey, headerKey;
private final long rotationPeriod, streamCounter;
private final boolean active;
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, boolean active) {
this(tagKey, headerKey, rotationPeriod, 0, active);
long rotationPeriod) {
this(tagKey, headerKey, rotationPeriod, 0);
}
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, long streamCounter, boolean active) {
long rotationPeriod, long streamCounter) {
this.tagKey = tagKey;
this.headerKey = headerKey;
this.rotationPeriod = rotationPeriod;
this.streamCounter = streamCounter;
this.active = active;
}
public SecretKey getTagKey() {
@@ -41,8 +39,4 @@ public class OutgoingKeys {
public long getStreamCounter() {
return streamCounter;
}
public boolean isActive() {
return active;
}
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.transport;
import java.io.IOException;
import java.io.OutputStream;
/**
* An interface for writing data to a transport connection. Data will be
* encrypted and authenticated before being written to the connection.
*/
public interface StreamWriter {
OutputStream getOutputStream();
/**
* Sends the end of stream marker, informing the recipient that no more
* data will be sent. The connection is flushed but not closed.
*/
void sendEndOfStream() throws IOException;
}

View File

@@ -12,12 +12,12 @@ public interface StreamWriterFactory {
* Creates an {@link OutputStream OutputStream} for writing to a
* transport stream
*/
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
/**
* Creates an {@link OutputStream OutputStream} for writing to a contact
* exchange stream.
*/
StreamWriter createContactExchangeStreamWriter(OutputStream out,
OutputStream createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey);
}

View File

@@ -7,7 +7,7 @@ public interface TransportConstants {
/**
* The current version of the transport protocol.
*/
int PROTOCOL_VERSION = 4;
int PROTOCOL_VERSION = 3;
/**
* The length of the pseudo-random tag in bytes.
@@ -80,32 +80,4 @@ public interface TransportConstants {
* The size of the reordering window.
*/
int REORDERING_WINDOW_SIZE = 32;
/**
* Label for deriving Alice's initial tag key from the master secret.
*/
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
/**
* Label for deriving Bob's initial tag key from the master secret.
*/
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
/**
* Label for deriving Alice's initial header key from the master secret.
*/
String ALICE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HEADER_KEY";
/**
* Label for deriving Bob's initial header key from the master secret.
*/
String BOB_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HEADER_KEY";
/**
* Label for deriving the next period's key in key rotation.
*/
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
}

View File

@@ -1,50 +0,0 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ClientMajorVersion implements Comparable<ClientMajorVersion> {
private final ClientId clientId;
private final int majorVersion;
public ClientMajorVersion(ClientId clientId, int majorVersion) {
this.clientId = clientId;
this.majorVersion = majorVersion;
}
public ClientId getClientId() {
return clientId;
}
public int getMajorVersion() {
return majorVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientMajorVersion) {
ClientMajorVersion cv = (ClientMajorVersion) o;
return clientId.equals(cv.clientId)
&& majorVersion == cv.majorVersion;
}
return false;
}
@Override
public int hashCode() {
return (clientId.hashCode() << 16) + majorVersion;
}
@Override
public int compareTo(ClientMajorVersion cv) {
int compare = clientId.compareTo(cv.clientId);
if (compare != 0) return compare;
return majorVersion - cv.majorVersion;
}
}

View File

@@ -1,45 +0,0 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.contact.Contact;
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.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group.Visibility;
@NotNullByDefault
public interface ClientVersioningManager {
/**
* The unique ID of the versioning client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.versioning");
/**
* The current major version of the versioning client.
*/
int MAJOR_VERSION = 0;
/**
* Registers a client that will be advertised to contacts. The hook will
* be called when the visibility of the client changes. This method should
* be called before {@link LifecycleManager#startServices(String)}.
*/
void registerClient(ClientId clientId, int majorVersion, int minorVersion,
ClientVersioningHook hook);
/**
* Returns the visibility of the given client with respect to the given
* contact.
*/
Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook {
void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException;
}
}

View File

@@ -9,37 +9,25 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.INFO;
@NotNullByDefault
public class IoUtils {
private static final Logger LOG = Logger.getLogger(IoUtils.class.getName());
public static void deleteFileOrDir(File f) {
if (f.isFile()) {
delete(f);
f.delete();
} else if (f.isDirectory()) {
File[] children = f.listFiles();
if (children != null)
for (File child : children) deleteFileOrDir(child);
delete(f);
f.delete();
}
}
private static void delete(File f) {
boolean deleted = f.delete();
if (LOG.isLoggable(INFO)) {
if (deleted) LOG.info("Deleted " + f.getAbsolutePath());
else LOG.info("Could not delete " + f.getAbsolutePath());
}
}
public static void copyAndClose(InputStream in, OutputStream out) {
public static void copyAndClose(InputStream in, OutputStream out)
throws IOException {
byte[] buf = new byte[4096];
try {
while (true) {

View File

@@ -126,10 +126,6 @@ public class StringUtils {
return toUtf8(s).length > maxLength;
}
public static boolean isValidMac(String mac) {
return MAC.matcher(mac).matches();
}
public static byte[] macToBytes(String mac) {
if (!MAC.matcher(mac).matches()) throw new IllegalArgumentException();
return fromHexString(mac.replaceAll(":", ""));

View File

@@ -2,39 +2,12 @@ package org.briarproject.bramble.test;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.util.IoUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
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.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
public class TestUtils {
private static final AtomicInteger nextTestDir =
@@ -61,117 +34,8 @@ public class TestUtils {
return getRandomBytes(UniqueId.LENGTH);
}
public static ClientId getClientId() {
return new ClientId(getRandomString(MAX_CLIENT_ID_LENGTH));
}
public static TransportId getTransportId() {
return new TransportId(getRandomString(MAX_TRANSPORT_ID_LENGTH));
}
public static TransportProperties getTransportProperties(int number) {
TransportProperties tp = new TransportProperties();
for (int i = 0; i < number; i++) {
tp.put(getRandomString(1 + random.nextInt(MAX_PROPERTY_LENGTH)),
getRandomString(1 + random.nextInt(MAX_PROPERTY_LENGTH))
);
}
return tp;
}
public static Map<TransportId, TransportProperties> getTransportPropertiesMap(
int number) {
Map<TransportId, TransportProperties> map = new HashMap<>();
for (int i = 0; i < number; i++) {
map.put(getTransportId(), getTransportProperties(number));
}
return map;
}
public static SecretKey getSecretKey() {
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
}
public static LocalAuthor getLocalAuthor() {
return getLocalAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
}
public static LocalAuthor getLocalAuthor(int nameLength) {
AuthorId id = new AuthorId(getRandomId());
String name = getRandomString(nameLength);
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
long created = System.currentTimeMillis();
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey,
created);
}
public static Author getAuthor() {
return getAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
}
public static Author getAuthor(int nameLength) {
AuthorId id = new AuthorId(getRandomId());
String name = getRandomString(nameLength);
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new Author(id, FORMAT_VERSION, name, publicKey);
}
public static Group getGroup(ClientId clientId, int majorVersion) {
int descriptorLength = 1 + random.nextInt(MAX_GROUP_DESCRIPTOR_LENGTH);
return getGroup(clientId, majorVersion, descriptorLength);
}
public static Group getGroup(ClientId clientId, int majorVersion,
int descriptorLength) {
GroupId groupId = new GroupId(getRandomId());
byte[] descriptor = getRandomBytes(descriptorLength);
return new Group(groupId, clientId, majorVersion, descriptor);
}
public static Message getMessage(GroupId groupId) {
int bodyLength = 1 + random.nextInt(MAX_MESSAGE_BODY_LENGTH);
return getMessage(groupId, MESSAGE_HEADER_LENGTH + bodyLength);
}
public static Message getMessage(GroupId groupId, int rawLength) {
MessageId id = new MessageId(getRandomId());
byte[] raw = getRandomBytes(rawLength);
long timestamp = System.currentTimeMillis();
return new Message(id, groupId, timestamp, raw);
}
public static double getMedian(Collection<? extends Number> samples) {
int size = samples.size();
if (size == 0) throw new IllegalArgumentException();
List<Double> sorted = new ArrayList<>(size);
for (Number n : samples) sorted.add(n.doubleValue());
Collections.sort(sorted);
if (size % 2 == 1) return sorted.get(size / 2);
double low = sorted.get(size / 2 - 1), high = sorted.get(size / 2);
return (low + high) / 2;
}
public static double getMean(Collection<? extends Number> samples) {
if (samples.isEmpty()) throw new IllegalArgumentException();
double sum = 0;
for (Number n : samples) sum += n.doubleValue();
return sum / samples.size();
}
public static double getVariance(Collection<? extends Number> samples) {
if (samples.size() < 2) throw new IllegalArgumentException();
double mean = getMean(samples);
double sumSquareDiff = 0;
for (Number n : samples) {
double diff = n.doubleValue() - mean;
sumSquareDiff += diff * diff;
}
return sumSquareDiff / (samples.size() - 1);
}
public static double getStandardDeviation(
Collection<? extends Number> samples) {
return Math.sqrt(getVariance(samples));
}
}

View File

@@ -2,7 +2,6 @@ apply plugin: 'java-library'
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'ru.vyarus.animalsniffer'
apply plugin: 'net.ltgt.apt'
apply plugin: 'idea'
apply plugin: 'witness'
@@ -10,15 +9,12 @@ apply plugin: 'witness'
dependencies {
implementation project(path: ':bramble-api', configuration: 'default')
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
implementation 'com.h2database:h2:1.4.192' // This is 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.4.1'
apt 'com.google.dagger:dagger-compiler:2.0.2'
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:2.8.2"
@@ -27,13 +23,12 @@ dependencies {
testImplementation "org.hamcrest:hamcrest-core:1.3"
testApt 'com.google.dagger:dagger-compiler:2.0.2'
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
}
dependencyVerification {
verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
@@ -42,25 +37,18 @@ dependencyVerification {
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
]
}
@@ -75,3 +63,8 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts {
testOutput jarTest
}
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -1,23 +1,21 @@
package org.briarproject.bramble;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
public interface BrambleCoreEagerSingletons {
void inject(ContactModule.EagerSingletons init);
void inject(CryptoExecutorModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(DatabaseExecutorModule.EagerSingletons init);
@@ -29,13 +27,9 @@ public interface BrambleCoreEagerSingletons {
void inject(PropertiesModule.EagerSingletons init);
void inject(ReportingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
}

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble;
import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule;
import org.briarproject.bramble.db.DatabaseExecutorModule;
@@ -13,7 +12,6 @@ import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.record.RecordModule;
import org.briarproject.bramble.reliability.ReliabilityModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.settings.SettingsModule;
@@ -21,7 +19,6 @@ import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import dagger.Module;
@@ -29,7 +26,6 @@ import dagger.Module;
ClientModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,
DataModule.class,
DatabaseModule.class,
DatabaseExecutorModule.class,
@@ -39,30 +35,26 @@ import dagger.Module;
LifecycleModule.class,
PluginModule.class,
PropertiesModule.class,
RecordModule.class,
ReliabilityModule.class,
ReportingModule.class,
SettingsModule.class,
SocksModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,
VersioningModule.class
TransportModule.class
})
public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.inject(new ContactModule.EagerSingletons());
c.inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new CryptoModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
}

View File

@@ -15,11 +15,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
@@ -36,14 +32,7 @@ import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
@Immutable
@NotNullByDefault
@@ -62,14 +51,12 @@ class ClientHelperImpl implements ClientHelper {
private final MetadataParser metadataParser;
private final MetadataEncoder metadataEncoder;
private final CryptoComponent crypto;
private final AuthorFactory authorFactory;
@Inject
ClientHelperImpl(DatabaseComponent db, MessageFactory messageFactory,
BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
MetadataEncoder metadataEncoder, CryptoComponent crypto,
AuthorFactory authorFactory) {
MetadataEncoder metadataEncoder, CryptoComponent crypto) {
this.db = db;
this.messageFactory = messageFactory;
this.bdfReaderFactory = bdfReaderFactory;
@@ -77,7 +64,6 @@ class ClientHelperImpl implements ClientHelper {
this.metadataParser = metadataParser;
this.metadataEncoder = metadataEncoder;
this.crypto = crypto;
this.authorFactory = authorFactory;
}
@Override
@@ -328,20 +314,6 @@ class ClientHelperImpl implements ClientHelper {
}
}
@Override
public BdfDictionary toDictionary(TransportProperties transportProperties) {
return new BdfDictionary(transportProperties);
}
@Override
public BdfDictionary toDictionary(
Map<TransportId, TransportProperties> map) {
BdfDictionary d = new BdfDictionary();
for (Entry<TransportId, TransportProperties> e : map.entrySet())
d.put(e.getKey().getString(), new BdfDictionary(e.getValue()));
return d;
}
@Override
public BdfList toList(byte[] b, int off, int len) throws FormatException {
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
@@ -369,11 +341,6 @@ class ClientHelperImpl implements ClientHelper {
raw.length - MESSAGE_HEADER_LENGTH);
}
@Override
public BdfList toList(Author a) {
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
}
@Override
public byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException {
@@ -381,53 +348,11 @@ class ClientHelperImpl implements ClientHelper {
}
@Override
public void verifySignature(byte[] signature, String label, BdfList signed,
byte[] publicKey) throws FormatException, GeneralSecurityException {
if (!crypto.verifySignature(signature, label, toByteArray(signed),
publicKey)) {
public void verifySignature(String label, byte[] sig, byte[] publicKey,
BdfList signed) throws FormatException, GeneralSecurityException {
if (!crypto.verify(label, toByteArray(signed), publicKey, sig)) {
throw new GeneralSecurityException("Invalid signature");
}
}
@Override
public Author parseAndValidateAuthor(BdfList author)
throws FormatException {
checkSize(author, 3);
int formatVersion = author.getLong(0).intValue();
if (formatVersion != FORMAT_VERSION) throw new FormatException();
String name = author.getString(1);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = author.getRaw(2);
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
return authorFactory.createAuthor(formatVersion, name, publicKey);
}
@Override
public TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException {
checkSize(properties, 0, MAX_PROPERTIES_PER_TRANSPORT);
TransportProperties p = new TransportProperties();
for (String key : properties.keySet()) {
checkLength(key, 1, MAX_PROPERTY_LENGTH);
String value = properties.getString(key);
checkLength(value, 1, MAX_PROPERTY_LENGTH);
p.put(key, value);
}
return p;
}
@Override
public Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
BdfDictionary properties) throws FormatException {
Map<TransportId, TransportProperties> tpMap = new HashMap<>();
for (String key : properties.keySet()) {
TransportId transportId = new TransportId(key);
TransportProperties transportProperties =
parseAndValidateTransportProperties(
properties.getDictionary(key));
tpMap.put(transportId, transportProperties);
}
return tpMap;
}
}

View File

@@ -2,6 +2,14 @@ package org.briarproject.bramble.client;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.bramble.api.sync.MessageFactory;
import dagger.Module;
import dagger.Provides;
@@ -10,14 +18,19 @@ import dagger.Provides;
public class ClientModule {
@Provides
ClientHelper provideClientHelper(ClientHelperImpl clientHelper) {
return clientHelper;
ClientHelper provideClientHelper(DatabaseComponent db,
MessageFactory messageFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
MetadataEncoder metadataEncoder, CryptoComponent cryptoComponent) {
return new ClientHelperImpl(db, messageFactory, bdfReaderFactory,
bdfWriterFactory, metadataParser, metadataEncoder,
cryptoComponent);
}
@Provides
ContactGroupFactory provideContactGroupFactory(
ContactGroupFactoryImpl contactGroupFactory) {
return contactGroupFactory;
ContactGroupFactory provideContactGroupFactory(GroupFactory groupFactory,
ClientHelper clientHelper) {
return new ContactGroupFactoryImpl(groupFactory, clientHelper);
}
}

View File

@@ -32,25 +32,23 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
}
@Override
public Group createLocalGroup(ClientId clientId, int majorVersion) {
return groupFactory.createGroup(clientId, majorVersion,
LOCAL_GROUP_DESCRIPTOR);
public Group createLocalGroup(ClientId clientId) {
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
}
@Override
public Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact) {
public Group createContactGroup(ClientId clientId, Contact contact) {
AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, majorVersion, descriptor);
return groupFactory.createGroup(clientId, descriptor);
}
@Override
public Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2) {
public Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId2) {
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
return groupFactory.createGroup(clientId, majorVersion, descriptor);
return groupFactory.createGroup(clientId, descriptor);
}
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {

View File

@@ -1,20 +1,23 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactExchangeListener;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -23,31 +26,29 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.record.Record;
import org.briarproject.bramble.api.record.RecordReader;
import org.briarproject.bramble.api.record.RecordReaderFactory;
import org.briarproject.bramble.api.record.RecordWriter;
import org.briarproject.bramble.api.record.RecordWriterFactory;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.contact.RecordTypes.CONTACT_INFO;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -60,9 +61,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
"org.briarproject.briar.contact/EXCHANGE";
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final RecordReaderFactory recordReaderFactory;
private final RecordWriterFactory recordWriterFactory;
private final AuthorFactory authorFactory;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final Clock clock;
private final ConnectionManager connectionManager;
private final ContactManager contactManager;
@@ -79,17 +80,17 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private volatile boolean alice;
@Inject
ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper,
RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory, Clock clock,
ContactExchangeTaskImpl(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
this.db = db;
this.clientHelper = clientHelper;
this.recordReaderFactory = recordReaderFactory;
this.recordWriterFactory = recordWriterFactory;
this.authorFactory = authorFactory;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.clock = clock;
this.connectionManager = connectionManager;
this.contactManager = contactManager;
@@ -124,174 +125,184 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn);
tryToClose(conn, true);
return;
}
// Get the local transport properties
Map<TransportId, TransportProperties> localProperties;
Map<TransportId, TransportProperties> localProperties, remoteProperties;
try {
localProperties = transportPropertyManager.getLocalProperties();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn);
tryToClose(conn, true);
return;
}
// Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
masterSecret, new byte[] {PROTOCOL_VERSION});
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
// Create the readers
InputStream streamReader =
streamReaderFactory.createContactExchangeStreamReader(in,
alice ? bobHeaderKey : aliceHeaderKey);
RecordReader recordReader =
recordReaderFactory.createRecordReader(streamReader);
BdfReader r = bdfReaderFactory.createReader(streamReader);
// Create the writers
StreamWriter streamWriter =
OutputStream streamWriter =
streamWriterFactory.createContactExchangeStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
RecordWriter recordWriter =
recordWriterFactory.createRecordWriter(streamWriter.getOutputStream());
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
byte[] localNonce = alice ? aliceNonce : bobNonce;
byte[] remoteNonce = alice ? bobNonce : aliceNonce;
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
// Sign the nonce
byte[] localSignature = sign(localAuthor, localNonce);
// Exchange contact info
// Exchange pseudonyms, signed nonces, and timestamps
long localTimestamp = clock.currentTimeMillis();
ContactInfo remoteInfo;
Author remoteAuthor;
long remoteTimestamp;
try {
if (alice) {
sendContactInfo(recordWriter, localAuthor, localProperties,
localSignature, localTimestamp);
recordWriter.flush();
remoteInfo = receiveContactInfo(recordReader);
sendPseudonym(w, aliceNonce);
sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
remoteAuthor = receivePseudonym(r, bobNonce);
remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
} else {
remoteInfo = receiveContactInfo(recordReader);
sendContactInfo(recordWriter, localAuthor, localProperties,
localSignature, localTimestamp);
recordWriter.flush();
remoteAuthor = receivePseudonym(r, aliceNonce);
remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
sendPseudonym(w, bobNonce);
sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
}
// Send EOF on the outgoing stream
streamWriter.sendEndOfStream();
// Skip any remaining records from the incoming stream
try {
while (true) recordReader.readRecord();
} catch (EOFException expected) {
LOG.info("End of stream");
}
} catch (IOException e) {
// Close the outgoing stream and expect EOF on the incoming stream
w.close();
if (!r.eof()) LOG.warning("Unexpected data at end of connection");
} catch (GeneralSecurityException | IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn);
return;
}
// Verify the contact's signature
if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) {
LOG.warning("Invalid signature");
listener.contactExchangeFailed();
tryToClose(conn);
tryToClose(conn, true);
return;
}
// The agreed timestamp is the minimum of the peers' timestamps
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
long timestamp = Math.min(localTimestamp, remoteTimestamp);
try {
// Add the contact
ContactId contactId = addContact(remoteInfo.author, timestamp,
remoteInfo.properties);
ContactId contactId = addContact(remoteAuthor, masterSecret,
timestamp, alice, remoteProperties);
// Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId,
conn);
// Pseudonym exchange succeeded
LOG.info("Pseudonym exchange succeeded");
listener.contactExchangeSucceeded(remoteInfo.author);
listener.contactExchangeSucceeded(remoteAuthor);
} catch (ContactExistsException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
tryToClose(conn);
listener.duplicateContact(remoteInfo.author);
tryToClose(conn, true);
listener.duplicateContact(remoteAuthor);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
tryToClose(conn);
tryToClose(conn, true);
listener.contactExchangeFailed();
}
}
private byte[] sign(LocalAuthor author, byte[] nonce) {
try {
return crypto.sign(SIGNING_LABEL_EXCHANGE, nonce,
author.getPrivateKey());
} catch (GeneralSecurityException e) {
throw new AssertionError();
private void sendPseudonym(BdfWriter w, byte[] nonce)
throws GeneralSecurityException, IOException {
// Sign the nonce
byte[] privateKey = localAuthor.getPrivateKey();
byte[] sig = crypto.sign(SIGNING_LABEL_EXCHANGE, nonce, privateKey);
// Write the name, public key and signature
w.writeListStart();
w.writeString(localAuthor.getName());
w.writeRaw(localAuthor.getPublicKey());
w.writeRaw(sig);
w.writeListEnd();
LOG.info("Sent pseudonym");
}
private Author receivePseudonym(BdfReader r, byte[] nonce)
throws GeneralSecurityException, IOException {
// Read the name, public key and signature
r.readListStart();
String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH);
r.readListEnd();
LOG.info("Received pseudonym");
// Verify the signature
if (!crypto.verify(SIGNING_LABEL_EXCHANGE, nonce, publicKey, sig)) {
if (LOG.isLoggable(INFO))
LOG.info("Invalid signature");
throw new GeneralSecurityException();
}
return authorFactory.createAuthor(name, publicKey);
}
private boolean verify(Author author, byte[] nonce, byte[] signature) {
try {
return crypto.verifySignature(signature, SIGNING_LABEL_EXCHANGE,
nonce, author.getPublicKey());
} catch (GeneralSecurityException e) {
return false;
}
}
private void sendContactInfo(RecordWriter recordWriter, Author author,
Map<TransportId, TransportProperties> properties, byte[] signature,
long timestamp) throws IOException {
BdfList authorList = clientHelper.toList(author);
BdfDictionary props = clientHelper.toDictionary(properties);
BdfList payload = BdfList.of(authorList, props, signature, timestamp);
recordWriter.writeRecord(new Record(PROTOCOL_VERSION, CONTACT_INFO,
clientHelper.toByteArray(payload)));
LOG.info("Sent contact info");
}
private ContactInfo receiveContactInfo(RecordReader recordReader)
private void sendTimestamp(BdfWriter w, long timestamp)
throws IOException {
Record record;
do {
record = recordReader.readRecord();
if (record.getProtocolVersion() != PROTOCOL_VERSION)
throw new FormatException();
} while (record.getRecordType() != CONTACT_INFO);
LOG.info("Received contact info");
BdfList payload = clientHelper.toList(record.getPayload());
checkSize(payload, 4);
Author author = clientHelper.parseAndValidateAuthor(payload.getList(0));
BdfDictionary props = payload.getDictionary(1);
Map<TransportId, TransportProperties> properties =
clientHelper.parseAndValidateTransportPropertiesMap(props);
byte[] signature = payload.getRaw(2);
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
long timestamp = payload.getLong(3);
if (timestamp < 0) throw new FormatException();
return new ContactInfo(author, properties, signature, timestamp);
w.writeLong(timestamp);
LOG.info("Sent timestamp");
}
private ContactId addContact(Author remoteAuthor, long timestamp,
private long receiveTimestamp(BdfReader r) throws IOException {
long timestamp = r.readLong();
if (timestamp < 0) throw new FormatException();
LOG.info("Received timestamp");
return timestamp;
}
private void sendTransportProperties(BdfWriter w,
Map<TransportId, TransportProperties> local) throws IOException {
w.writeListStart();
for (Entry<TransportId, TransportProperties> e : local.entrySet())
w.writeList(BdfList.of(e.getKey().getString(), e.getValue()));
w.writeListEnd();
}
private Map<TransportId, TransportProperties> receiveTransportProperties(
BdfReader r) throws IOException {
Map<TransportId, TransportProperties> remote = new HashMap<>();
r.readListStart();
while (!r.hasListEnd()) {
r.readListStart();
String id = r.readString(MAX_TRANSPORT_ID_LENGTH);
if (id.isEmpty()) throw new FormatException();
TransportProperties p = new TransportProperties();
r.readDictionaryStart();
while (!r.hasDictionaryEnd()) {
if (p.size() == MAX_PROPERTIES_PER_TRANSPORT)
throw new FormatException();
String key = r.readString(MAX_PROPERTY_LENGTH);
String value = r.readString(MAX_PROPERTY_LENGTH);
p.put(key, value);
}
r.readDictionaryEnd();
r.readListEnd();
remote.put(new TransportId(id), p);
}
r.readListEnd();
return remote;
}
private ContactId addContact(Author remoteAuthor, SecretKey master,
long timestamp, boolean alice,
Map<TransportId, TransportProperties> remoteProperties)
throws DbException {
ContactId contactId;
Transaction txn = db.startTransaction(false);
try {
contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), masterSecret, timestamp, alice,
true, true);
localAuthor.getId(), master, timestamp, alice, true, true);
transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties);
db.commitTransaction(txn);
@@ -301,30 +312,14 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return contactId;
}
private void tryToClose(DuplexTransportConnection conn) {
private void tryToClose(DuplexTransportConnection conn,
boolean exception) {
try {
LOG.info("Closing connection");
conn.getReader().dispose(true, true);
conn.getWriter().dispose(true);
conn.getReader().dispose(exception, true);
conn.getWriter().dispose(exception);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private static class ContactInfo {
private final Author author;
private final Map<TransportId, TransportProperties> properties;
private final byte[] signature;
private final long timestamp;
private ContactInfo(Author author,
Map<TransportId, TransportProperties> properties,
byte[] signature, long timestamp) {
this.author = author;
this.properties = properties;
this.signature = signature;
this.timestamp = timestamp;
}
}
}

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