mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Compare commits
2 Commits
bluetooth_
...
elliptic-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b38c33bf58 | ||
|
|
5bb00597ef |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,7 +20,6 @@ local.properties
|
|||||||
.idea/*
|
.idea/*
|
||||||
!.idea/runConfigurations/
|
!.idea/runConfigurations/
|
||||||
!.idea/codeStyleSettings.xml
|
!.idea/codeStyleSettings.xml
|
||||||
!.idea/codeStyles
|
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
build/
|
||||||
*.iml
|
*.iml
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ cache:
|
|||||||
before_script:
|
before_script:
|
||||||
- set -e
|
- set -e
|
||||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
- 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
|
# 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
|
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
|
||||||
- if [ ! -d openjdk ]
|
- if [ ! -d openjdk ]
|
||||||
|
|||||||
261
.idea/codeStyles/Project.xml
generated
261
.idea/codeStyles/Project.xml
generated
@@ -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>
|
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
||||||
23
.idea/runConfigurations/H2_Performance_Test.xml
generated
23
.idea/runConfigurations/H2_Performance_Test.xml
generated
@@ -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>
|
|
||||||
@@ -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>
|
|
||||||
@@ -1,21 +1,19 @@
|
|||||||
import de.undercouch.gradle.tasks.download.Download
|
import de.undercouch.gradle.tasks.download.Download
|
||||||
import de.undercouch.gradle.tasks.download.Verify
|
import de.undercouch.gradle.tasks.download.Verify
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply plugin: 'de.undercouch.download'
|
apply plugin: 'de.undercouch.download'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
buildToolsVersion '27.0.3'
|
buildToolsVersion '26.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10001
|
versionCode 1700
|
||||||
versionName "1.0.1"
|
versionName "0.17.0"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,157 +34,63 @@ dependencies {
|
|||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.android.tools.analytics-library:protos:26.1.2:protos-26.1.2.jar:52672a0b42b572a06aecc3535d5068eb46c0e15d129b9f1085d3c16a1da5cdbb',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'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.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
'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-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.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:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
'com.google.guava:guava:22.0:guava-22.0.jar:1158e94c7de4da480873f0b4ab4a1da14c0d23d4b1902cc94a58a6f0f9ab579e',
|
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
|
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||||
'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',
|
|
||||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'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.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
|
||||||
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
|
||||||
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
|
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
|
||||||
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
|
||||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
|
||||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
|
||||||
'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',
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.torBinaryDir = 'src/main/res/raw'
|
ext.torBinaryDir = 'src/main/res/raw'
|
||||||
ext.torVersion = '0.2.9.14'
|
ext.torVersion = '0.2.9.12'
|
||||||
ext.geoipVersion = '2017-11-06'
|
ext.geoipVersion = '2017-09-06'
|
||||||
ext.torDownloadUrl = 'https://briarproject.org/build/'
|
ext.torDownloadUrl = 'https://briarproject.org/build/'
|
||||||
|
|
||||||
def torBinaries = [
|
def torBinaries = [
|
||||||
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
|
"tor_arm" : '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688',
|
||||||
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
|
"tor_arm_pie": '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283',
|
||||||
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
|
"tor_x86" : '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab',
|
||||||
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
|
"tor_x86_pie": '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288',
|
||||||
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
|
"geoip" : 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
|
||||||
]
|
]
|
||||||
|
|
||||||
def verifyOrDeleteBinary(name, chksum, alreadyVerified) {
|
def downloadBinary(name) {
|
||||||
return tasks.create("verifyOrDeleteBinary${name}", VerifyOrDelete) {
|
return tasks.create("downloadBinary${name}", Download) {
|
||||||
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)]) {
|
|
||||||
src "${torDownloadUrl}${name}.zip"
|
src "${torDownloadUrl}${name}.zip"
|
||||||
.replace('tor_', "tor-${torVersion}-")
|
.replace('tor_', "tor-${torVersion}-")
|
||||||
.replace('geoip', "geoip-${geoipVersion}")
|
.replace('geoip', "geoip-${geoipVersion}")
|
||||||
.replaceAll('_', '-')
|
.replaceAll('_', '-')
|
||||||
dest "${torBinaryDir}/${name}.zip"
|
dest "${torBinaryDir}/${name}.zip"
|
||||||
onlyIf {
|
onlyIfNewer true
|
||||||
!dest.exists()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def verifyBinary(name, chksum) {
|
def verifyBinary(name, chksum) {
|
||||||
boolean[] alreadyVerified = [false]
|
|
||||||
return tasks.create([
|
return tasks.create([
|
||||||
name : "verifyBinary${name}",
|
name : "verifyBinary${name}",
|
||||||
type : Verify,
|
type : Verify,
|
||||||
dependsOn: downloadBinary(name, chksum, alreadyVerified)]) {
|
dependsOn: downloadBinary(name)]) {
|
||||||
src "${torBinaryDir}/${name}.zip"
|
src "${torBinaryDir}/${name}.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum chksum
|
checksum chksum
|
||||||
onlyIf {
|
|
||||||
!alreadyVerified[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
torBinaries.every { name, checksum ->
|
torBinaries.every { key, value ->
|
||||||
preBuild.dependsOn.add(verifyBinary(name, checksum))
|
preBuild.dependsOn.add(verifyBinary(key, value))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
-keep class net.i2p.crypto.eddsa.** { *; }
|
-keep class net.i2p.crypto.eddsa.** { *; }
|
||||||
|
|
||||||
-keep class org.whispersystems.curve25519.** { *; }
|
|
||||||
|
|
||||||
-dontwarn sun.misc.Unsafe
|
-dontwarn sun.misc.Unsafe
|
||||||
-dontnote com.google.common.**
|
-dontnote com.google.common.**
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
|||||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.Scheduler;
|
import org.briarproject.bramble.plugin.droidtooth.DroidtoothPluginFactory;
|
||||||
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
|
||||||
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
||||||
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
|
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
|
||||||
|
|
||||||
@@ -23,7 +22,6 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
@@ -35,20 +33,18 @@ public class AndroidPluginModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
|
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
|
||||||
@Scheduler ScheduledExecutorService scheduler,
|
|
||||||
AndroidExecutor androidExecutor, SecureRandom random,
|
AndroidExecutor androidExecutor, SecureRandom random,
|
||||||
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
|
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
|
||||||
Application app, LocationUtils locationUtils, DevReporter reporter,
|
Application app, LocationUtils locationUtils, DevReporter reporter,
|
||||||
EventBus eventBus) {
|
EventBus eventBus) {
|
||||||
Context appContext = app.getApplicationContext();
|
Context appContext = app.getApplicationContext();
|
||||||
DuplexPluginFactory bluetooth =
|
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
|
||||||
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
androidExecutor, appContext, random, eventBus, backoffFactory);
|
||||||
appContext, random, eventBus, backoffFactory);
|
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
|
||||||
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
|
locationUtils, reporter, eventBus, torSocketFactory,
|
||||||
appContext, locationUtils, reporter, eventBus,
|
backoffFactory);
|
||||||
torSocketFactory, backoffFactory);
|
|
||||||
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
||||||
scheduler, backoffFactory, appContext);
|
backoffFactory, appContext);
|
||||||
Collection<DuplexPluginFactory> duplex =
|
Collection<DuplexPluginFactory> duplex =
|
||||||
Arrays.asList(bluetooth, tor, lan);
|
Arrays.asList(bluetooth, tor, lan);
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.droidtooth;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
|||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@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 MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
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 EventBus eventBus;
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
|
||||||
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
public DroidtoothPluginFactory(Executor ioExecutor,
|
||||||
AndroidExecutor androidExecutor, Context appContext,
|
AndroidExecutor androidExecutor, Context appContext,
|
||||||
SecureRandom secureRandom, EventBus eventBus,
|
SecureRandom secureRandom, EventBus eventBus,
|
||||||
BackoffFactory backoffFactory) {
|
BackoffFactory backoffFactory) {
|
||||||
@@ -59,13 +59,11 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
|
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
|
||||||
BluetoothConnectionLimiter connectionLimiter =
|
|
||||||
new BluetoothConnectionLimiterImpl();
|
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
DroidtoothPlugin plugin = new DroidtoothPlugin(ioExecutor,
|
||||||
connectionLimiter, ioExecutor, androidExecutor, appContext,
|
androidExecutor, appContext, secureRandom, backoff, callback,
|
||||||
secureRandom, backoff, callback, MAX_LATENCY);
|
MAX_LATENCY);
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.droidtooth;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothSocket;
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
|
||||||
@@ -11,17 +11,12 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AndroidBluetoothTransportConnection
|
class DroidtoothTransportConnection extends AbstractDuplexTransportConnection {
|
||||||
extends AbstractDuplexTransportConnection {
|
|
||||||
|
|
||||||
private final BluetoothConnectionLimiter connectionManager;
|
|
||||||
private final BluetoothSocket socket;
|
private final BluetoothSocket socket;
|
||||||
|
|
||||||
AndroidBluetoothTransportConnection(Plugin plugin,
|
DroidtoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
|
||||||
BluetoothConnectionLimiter connectionManager,
|
|
||||||
BluetoothSocket socket) {
|
|
||||||
super(plugin);
|
super(plugin);
|
||||||
this.connectionManager = connectionManager;
|
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,10 +32,6 @@ class AndroidBluetoothTransportConnection
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void closeConnection(boolean exception) throws IOException {
|
protected void closeConnection(boolean exception) throws IOException {
|
||||||
try {
|
socket.close();
|
||||||
socket.close();
|
|
||||||
} finally {
|
|
||||||
connectionManager.connectionClosed(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,84 +5,37 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.Network;
|
|
||||||
import android.net.NetworkInfo;
|
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.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
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.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.net.SocketFactory;
|
|
||||||
|
|
||||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
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.CONNECTIVITY_ACTION;
|
||||||
import static android.net.ConnectivityManager.TYPE_WIFI;
|
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
|
@NotNullByDefault
|
||||||
class AndroidLanTcpPlugin extends LanTcpPlugin {
|
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 =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(AndroidLanTcpPlugin.class.getName());
|
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 Context appContext;
|
||||||
private final ConnectivityManager connectivityManager;
|
|
||||||
@Nullable
|
|
||||||
private final WifiManager wifiManager;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile BroadcastReceiver networkStateReceiver = null;
|
private volatile BroadcastReceiver networkStateReceiver = null;
|
||||||
private volatile SocketFactory socketFactory;
|
|
||||||
|
|
||||||
AndroidLanTcpPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
|
AndroidLanTcpPlugin(Executor ioExecutor, Backoff backoff,
|
||||||
Backoff backoff, Context appContext, DuplexPluginCallback callback,
|
Context appContext, DuplexPluginCallback callback, int maxLatency,
|
||||||
int maxLatency, int maxIdleTime) {
|
int maxIdleTime) {
|
||||||
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
|
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.appContext = appContext;
|
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
|
@Override
|
||||||
@@ -91,9 +44,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
running = true;
|
running = true;
|
||||||
// Register to receive network status events
|
// Register to receive network status events
|
||||||
networkStateReceiver = new NetworkStateReceiver();
|
networkStateReceiver = new NetworkStateReceiver();
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
|
||||||
filter.addAction(CONNECTIVITY_ACTION);
|
|
||||||
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
|
|
||||||
appContext.registerReceiver(networkStateReceiver, filter);
|
appContext.registerReceiver(networkStateReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,92 +56,21 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
tryToClose(socket);
|
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 {
|
private class NetworkStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context ctx, Intent i) {
|
public void onReceive(Context ctx, Intent i) {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
if (isApEnabledEvent(i)) {
|
Object o = ctx.getSystemService(CONNECTIVITY_SERVICE);
|
||||||
// The state change may be broadcast before the AP address is
|
ConnectivityManager cm = (ConnectivityManager) o;
|
||||||
// visible, so delay handling the event
|
NetworkInfo net = cm.getActiveNetworkInfo();
|
||||||
scheduler.schedule(this::handleConnectivityChange, 1, SECONDS);
|
if (net != null && net.getType() == TYPE_WIFI && net.isConnected()) {
|
||||||
} else {
|
LOG.info("Connected to Wi-Fi");
|
||||||
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();
|
|
||||||
if (socket == null || socket.isClosed()) bind();
|
if (socket == null || socket.isClosed()) bind();
|
||||||
} else if (addrs.isEmpty()) {
|
} else {
|
||||||
LOG.info("Not connected to wifi");
|
LOG.info("Not connected to Wi-Fi");
|
||||||
socketFactory = SocketFactory.getDefault();
|
|
||||||
tryToClose(socket);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@@ -28,15 +27,12 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
private static final double BACKOFF_BASE = 1.2;
|
private static final double BACKOFF_BASE = 1.2;
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
|
|
||||||
public AndroidLanTcpPluginFactory(Executor ioExecutor,
|
public AndroidLanTcpPluginFactory(Executor ioExecutor,
|
||||||
ScheduledExecutorService scheduler, BackoffFactory backoffFactory,
|
BackoffFactory backoffFactory, Context appContext) {
|
||||||
Context appContext) {
|
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
}
|
}
|
||||||
@@ -55,7 +51,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
|
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
return new AndroidLanTcpPlugin(ioExecutor, scheduler, backoff,
|
return new AndroidLanTcpPlugin(ioExecutor, backoff, appContext,
|
||||||
appContext, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
callback, MAX_LATENCY, MAX_IDLE_TIME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import android.os.PowerManager;
|
|||||||
import net.freehaven.tor.control.EventHandler;
|
import net.freehaven.tor.control.EventHandler;
|
||||||
import net.freehaven.tor.control.TorControlConnection;
|
import net.freehaven.tor.control.TorControlConnection;
|
||||||
|
|
||||||
import org.briarproject.bramble.PoliteExecutor;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
@@ -60,10 +59,7 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
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.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
@@ -74,15 +70,10 @@ import javax.net.SocketFactory;
|
|||||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||||
import static android.content.Context.MODE_PRIVATE;
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
import static android.content.Context.POWER_SERVICE;
|
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.CONNECTIVITY_ACTION;
|
||||||
import static android.net.ConnectivityManager.TYPE_WIFI;
|
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 android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
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.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
||||||
@@ -110,8 +101,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(TorPlugin.class.getName());
|
Logger.getLogger(TorPlugin.class.getName());
|
||||||
|
|
||||||
private final Executor ioExecutor, connectionStatusExecutor;
|
private final Executor ioExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final LocationUtils locationUtils;
|
private final LocationUtils locationUtils;
|
||||||
private final DevReporter reporter;
|
private final DevReporter reporter;
|
||||||
@@ -124,8 +114,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private final File torDirectory, torFile, geoIpFile, configFile;
|
private final File torDirectory, torFile, geoIpFile, configFile;
|
||||||
private final File doneFile, cookieFile;
|
private final File doneFile, cookieFile;
|
||||||
private final PowerManager.WakeLock wakeLock;
|
private final PowerManager.WakeLock wakeLock;
|
||||||
private final AtomicReference<Future<?>> connectivityCheck =
|
|
||||||
new AtomicReference<>();
|
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
@@ -134,13 +122,12 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private volatile TorControlConnection controlConnection = null;
|
private volatile TorControlConnection controlConnection = null;
|
||||||
private volatile BroadcastReceiver networkStateReceiver = null;
|
private volatile BroadcastReceiver networkStateReceiver = null;
|
||||||
|
|
||||||
TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
|
TorPlugin(Executor ioExecutor, Context appContext,
|
||||||
Context appContext, LocationUtils locationUtils,
|
LocationUtils locationUtils, DevReporter reporter,
|
||||||
DevReporter reporter, SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory, Backoff backoff,
|
||||||
Backoff backoff, DuplexPluginCallback callback,
|
DuplexPluginCallback callback, String architecture, int maxLatency,
|
||||||
String architecture, int maxLatency, int maxIdleTime) {
|
int maxIdleTime) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.locationUtils = locationUtils;
|
this.locationUtils = locationUtils;
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
@@ -165,9 +152,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// This tag will prevent Huawei's powermanager from killing us.
|
// This tag will prevent Huawei's powermanager from killing us.
|
||||||
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService");
|
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService");
|
||||||
wakeLock.setReferenceCounted(false);
|
wakeLock.setReferenceCounted(false);
|
||||||
// Don't execute more than one connection status check at a time
|
|
||||||
connectionStatusExecutor = new PoliteExecutor("TorPlugin",
|
|
||||||
ioExecutor, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -220,11 +204,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
Scanner stderr = new Scanner(torProcess.getErrorStream());
|
Scanner stderr = new Scanner(torProcess.getErrorStream());
|
||||||
while (stdout.hasNextLine() || stderr.hasNextLine()) {
|
while (stdout.hasNextLine() || stderr.hasNextLine()){
|
||||||
if (stdout.hasNextLine()) {
|
if(stdout.hasNextLine()) {
|
||||||
LOG.info(stdout.nextLine());
|
LOG.info(stdout.nextLine());
|
||||||
}
|
}
|
||||||
if (stderr.hasNextLine()) {
|
if(stderr.hasNextLine()){
|
||||||
LOG.info(stderr.nextLine());
|
LOG.info(stderr.nextLine());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,11 +257,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
// Register to receive network status events
|
// Register to receive network status events
|
||||||
networkStateReceiver = new NetworkStateReceiver();
|
networkStateReceiver = new NetworkStateReceiver();
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
|
||||||
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);
|
|
||||||
appContext.registerReceiver(networkStateReceiver, filter);
|
appContext.registerReceiver(networkStateReceiver, filter);
|
||||||
// Bind a server socket to receive incoming hidden service connections
|
// Bind a server socket to receive incoming hidden service connections
|
||||||
bind();
|
bind();
|
||||||
@@ -614,7 +594,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createKeyAgreementConnection(
|
public DuplexTransportConnection createKeyAgreementConnection(
|
||||||
byte[] commitment, BdfList descriptor) {
|
byte[] commitment, BdfList descriptor, long timeout) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,8 +618,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void orConnStatus(String status, String orName) {
|
public void orConnStatus(String status, String orName) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
|
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
|
||||||
if (status.equals("CLOSED") || status.equals("FAILED"))
|
|
||||||
updateConnectionStatus(); // Check whether we've lost connectivity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -679,7 +657,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(int event, @Nullable String path) {
|
public void onEvent(int event, String path) {
|
||||||
stopWatching();
|
stopWatching();
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
@@ -697,8 +675,9 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus() {
|
private void updateConnectionStatus() {
|
||||||
connectionStatusExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
|
|
||||||
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
|
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
|
||||||
ConnectivityManager cm = (ConnectivityManager) o;
|
ConnectivityManager cm = (ConnectivityManager) o;
|
||||||
NetworkInfo net = cm.getActiveNetworkInfo();
|
NetworkInfo net = cm.getActiveNetworkInfo();
|
||||||
@@ -737,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 {
|
private class NetworkStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context ctx, Intent i) {
|
public void onReceive(Context ctx, Intent i) {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
String action = i.getAction();
|
if (CONNECTIVITY_ACTION.equals(i.getAction())) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Received broadcast " + action);
|
LOG.info("Detected connectivity change");
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
if (ACTION_SCREEN_ON.equals(action)
|
|
||||||
|| ACTION_SCREEN_OFF.equals(action)) {
|
|
||||||
scheduleConnectionStatusUpdate();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -778,7 +746,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
private synchronized void enableNetwork(boolean enable) {
|
private synchronized void enableNetwork(boolean enable) {
|
||||||
networkEnabled = enable;
|
networkEnabled = enable;
|
||||||
if (!enable) circuitBuilt = false;
|
circuitBuilt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized boolean isConnected() {
|
private synchronized boolean isConnected() {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
|
|||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
@@ -37,7 +36,6 @@ public class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
private static final double BACKOFF_BASE = 1.2;
|
private static final double BACKOFF_BASE = 1.2;
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final ScheduledExecutorService scheduler;
|
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final LocationUtils locationUtils;
|
private final LocationUtils locationUtils;
|
||||||
private final DevReporter reporter;
|
private final DevReporter reporter;
|
||||||
@@ -45,13 +43,11 @@ public class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
private final SocketFactory torSocketFactory;
|
private final SocketFactory torSocketFactory;
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
|
||||||
public TorPluginFactory(Executor ioExecutor,
|
public TorPluginFactory(Executor ioExecutor, Context appContext,
|
||||||
ScheduledExecutorService scheduler, Context appContext,
|
|
||||||
LocationUtils locationUtils, DevReporter reporter,
|
LocationUtils locationUtils, DevReporter reporter,
|
||||||
EventBus eventBus, SocketFactory torSocketFactory,
|
EventBus eventBus, SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory) {
|
BackoffFactory backoffFactory) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.locationUtils = locationUtils;
|
this.locationUtils = locationUtils;
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
@@ -93,9 +89,9 @@ public class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext,
|
TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils,
|
||||||
locationUtils, reporter, torSocketFactory, backoff, callback,
|
reporter, torSocketFactory, backoff, callback, architecture,
|
||||||
architecture, MAX_LATENCY, MAX_IDLE_TIME);
|
MAX_LATENCY, MAX_IDLE_TIME);
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,7 @@ import org.briarproject.bramble.api.data.BdfDictionary;
|
|||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
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.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.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
@@ -90,30 +87,16 @@ public interface ClientHelper {
|
|||||||
BdfDictionary toDictionary(byte[] b, int off, int len)
|
BdfDictionary toDictionary(byte[] b, int off, int len)
|
||||||
throws FormatException;
|
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, int off, int len) throws FormatException;
|
||||||
|
|
||||||
BdfList toList(byte[] b) throws FormatException;
|
BdfList toList(byte[] b) throws FormatException;
|
||||||
|
|
||||||
BdfList toList(Message m) throws FormatException;
|
BdfList toList(Message m) throws FormatException;
|
||||||
|
|
||||||
BdfList toList(Author a);
|
|
||||||
|
|
||||||
byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
||||||
throws FormatException, GeneralSecurityException;
|
throws FormatException, GeneralSecurityException;
|
||||||
|
|
||||||
void verifySignature(byte[] signature, String label, BdfList signed,
|
void verifySignature(String label, byte[] sig, byte[] publicKey,
|
||||||
byte[] publicKey) throws FormatException, GeneralSecurityException;
|
BdfList signed) throws FormatException, GeneralSecurityException;
|
||||||
|
|
||||||
Author parseAndValidateAuthor(BdfList author) throws FormatException;
|
|
||||||
|
|
||||||
TransportProperties parseAndValidateTransportProperties(
|
|
||||||
BdfDictionary properties) throws FormatException;
|
|
||||||
|
|
||||||
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
|
||||||
BdfDictionary properties) throws FormatException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ public interface ContactGroupFactory {
|
|||||||
/**
|
/**
|
||||||
* Creates a group that is not shared with any contacts.
|
* Creates a group that is not shared with any contacts.
|
||||||
*/
|
*/
|
||||||
Group createLocalGroup(ClientId clientId, int majorVersion);
|
Group createLocalGroup(ClientId clientId, int clientVersion);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group for the given client to share with the given contact.
|
* Creates a group for the given client to share with the given contact.
|
||||||
*/
|
*/
|
||||||
Group createContactGroup(ClientId clientId, int majorVersion,
|
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
Contact contact);
|
Contact contact);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group for the given client to share between the given authors
|
* Creates a group for the given client to share between the given authors
|
||||||
* identified by their AuthorIds.
|
* identified by their AuthorIds.
|
||||||
*/
|
*/
|
||||||
Group createContactGroup(ClientId clientId, int majorVersion,
|
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
AuthorId authorId1, AuthorId authorId2);
|
AuthorId authorId1, AuthorId authorId2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|||||||
public interface ContactExchangeTask {
|
public interface ContactExchangeTask {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current version of the contact exchange protocol.
|
* The current version of the contact exchange protocol
|
||||||
*/
|
*/
|
||||||
byte PROTOCOL_VERSION = 1;
|
int PROTOCOL_VERSION = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving Alice's header key from the master secret.
|
* Label for deriving Alice's header key from the master secret.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Type-safe wrapper for an integer that uniquely identifies a contact within
|
* 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
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -14,36 +13,26 @@ import java.util.Collection;
|
|||||||
public interface ContactManager {
|
public interface ContactManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a hook to be called whenever a contact is added or removed.
|
* Registers a hook to be called whenever a contact is added.
|
||||||
* This method should be called before
|
|
||||||
* {@link LifecycleManager#startServices(String)}.
|
|
||||||
*/
|
*/
|
||||||
void registerContactHook(ContactHook hook);
|
void registerAddContactHook(AddContactHook hook);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a contact associated with the given local and remote pseudonyms,
|
* Registers a hook to be called whenever a contact is removed.
|
||||||
* derives and stores transport keys for each transport, and returns an ID
|
*/
|
||||||
* for the contact.
|
void registerRemoveContactHook(RemoveContactHook hook);
|
||||||
*
|
|
||||||
* @param alice true if the local party is Alice
|
/**
|
||||||
|
* 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,
|
ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||||
SecretKey master, long timestamp, boolean alice, boolean verified,
|
SecretKey master, long timestamp, boolean alice, boolean verified,
|
||||||
boolean active) throws DbException;
|
boolean active) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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,
|
* Stores a contact associated with the given local and remote pseudonyms,
|
||||||
* derives and stores transport keys for each transport, and returns an ID
|
* and returns an ID for the contact.
|
||||||
* for the contact.
|
|
||||||
*
|
|
||||||
* @param alice true if the local party is Alice
|
|
||||||
*/
|
*/
|
||||||
ContactId addContact(Author remote, AuthorId local,
|
ContactId addContact(Author remote, AuthorId local,
|
||||||
SecretKey master, long timestamp, boolean alice, boolean verified,
|
SecretKey master, long timestamp, boolean alice, boolean verified,
|
||||||
@@ -105,10 +94,11 @@ public interface ContactManager {
|
|||||||
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
interface ContactHook {
|
interface AddContactHook {
|
||||||
|
|
||||||
void addingContact(Transaction txn, Contact c) throws DbException;
|
void addingContact(Transaction txn, Contact c) throws DbException;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemoveContactHook {
|
||||||
void removingContact(Transaction txn, Contact c) throws DbException;
|
void removingContact(Transaction txn, Contact c) throws DbException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.contact;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Record types for the contact exchange protocol.
|
|
||||||
*/
|
|
||||||
public interface RecordTypes {
|
|
||||||
|
|
||||||
byte CONTACT_INFO = 0;
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface CryptoComponent {
|
public interface CryptoComponent {
|
||||||
|
|
||||||
SecretKey generateSecretKey();
|
SecretKey generateSecretKey();
|
||||||
@@ -22,6 +17,10 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
KeyParser getSignatureKeyParser();
|
KeyParser getSignatureKeyParser();
|
||||||
|
|
||||||
|
KeyPair generateEdKeyPair();
|
||||||
|
|
||||||
|
KeyParser getEdKeyParser();
|
||||||
|
|
||||||
KeyParser getMessageKeyParser();
|
KeyParser getMessageKeyParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +48,7 @@ public interface CryptoComponent {
|
|||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the given byte[] with the given private key.
|
* Signs the given byte[] with the given ECDSA private key.
|
||||||
*
|
*
|
||||||
* @param label a namespaced label indicating the purpose of this
|
* @param label a namespaced label indicating the purpose of this
|
||||||
* signature, to prevent it from being repurposed or colliding with a
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
@@ -58,17 +57,37 @@ public interface CryptoComponent {
|
|||||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs the given byte[] with the given Ed25519 private key.
|
||||||
|
*
|
||||||
|
* @param label A label specific to this signature
|
||||||
|
* to ensure that the signature cannot be repurposed
|
||||||
|
*/
|
||||||
|
byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that the given signature is valid for the signed data
|
* Verifies that the given signature is valid for the signed data
|
||||||
* and the given public key.
|
* and the given ECDSA public key.
|
||||||
*
|
*
|
||||||
* @param label a namespaced label indicating the purpose of this
|
* @param label a namespaced label indicating the purpose of this
|
||||||
* signature, to prevent it from being repurposed or colliding with a
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
* signature created for another purpose
|
* signature created for another purpose
|
||||||
* @return true if the signature was valid, false otherwise.
|
* @return true if the signature was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean verifySignature(byte[] signature, String label, byte[] signed,
|
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
byte[] publicKey) throws GeneralSecurityException;
|
byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the given signature is valid for the signed data
|
||||||
|
* and the given Ed25519 public key.
|
||||||
|
*
|
||||||
|
* @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 verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash of the given inputs. The inputs are unambiguously
|
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||||
@@ -91,18 +110,6 @@ public interface CryptoComponent {
|
|||||||
*/
|
*/
|
||||||
byte[] mac(String label, SecretKey macKey, byte[]... inputs);
|
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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts and authenticates the given plaintext so it can be written to
|
* Encrypts and authenticates the given plaintext so it can be written to
|
||||||
* storage. The encryption and authentication keys are derived from the
|
* storage. The encryption and authentication keys are derived from the
|
||||||
@@ -117,7 +124,6 @@ public interface CryptoComponent {
|
|||||||
* given password. Returns null if the ciphertext cannot be decrypted and
|
* given password. Returns null if the ciphertext cannot be decrypted and
|
||||||
* authenticated (for example, if the password is wrong).
|
* authenticated (for example, if the password is wrong).
|
||||||
*/
|
*/
|
||||||
@Nullable
|
|
||||||
byte[] decryptWithPassword(byte[] ciphertext, String password);
|
byte[] decryptWithPassword(byte[] ciphertext, String password);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -14,10 +14,9 @@ public interface TransportCrypto {
|
|||||||
* rotation period from the given master secret.
|
* rotation period from the given master secret.
|
||||||
*
|
*
|
||||||
* @param alice whether the keys are for use by Alice or Bob.
|
* @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,
|
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
|
||||||
long rotationPeriod, boolean alice, boolean active);
|
long rotationPeriod, boolean alice);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates the given transport keys to the given rotation period. If the
|
* Rotates the given transport keys to the given rotation period. If the
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
* );
|
* );
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public static BdfDictionary of(Entry<String, ?>... entries) {
|
public static BdfDictionary of(Entry<String, Object>... entries) {
|
||||||
BdfDictionary d = new BdfDictionary();
|
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;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ public class BdfDictionary extends TreeMap<String, Object> {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary(Map<String, ?> m) {
|
public BdfDictionary(Map<String, Object> m) {
|
||||||
super(m);
|
super(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -18,8 +18,7 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.Offer;
|
import org.briarproject.bramble.api.sync.Offer;
|
||||||
import org.briarproject.bramble.api.sync.Request;
|
import org.briarproject.bramble.api.sync.Request;
|
||||||
import org.briarproject.bramble.api.transport.KeySet;
|
import org.briarproject.bramble.api.sync.ValidationManager;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -38,13 +37,8 @@ public interface DatabaseComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the database and returns true if the database already existed.
|
* 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.
|
* Waits for any open transactions to finish and closes the database.
|
||||||
@@ -104,17 +98,10 @@ public interface DatabaseComponent {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given transport keys, optionally binding them to the given
|
* Stores transport keys for a newly added contact.
|
||||||
* contact, and returns a key set ID.
|
|
||||||
*/
|
*/
|
||||||
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c,
|
void addTransportKeys(Transaction txn, ContactId c, TransportKeys k)
|
||||||
TransportKeys k) throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the given keys for the given transport to the given contact.
|
|
||||||
*/
|
|
||||||
void bindTransportKeys(Transaction txn, ContactId c, TransportId t,
|
|
||||||
KeySetId k) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact for the given
|
||||||
@@ -136,8 +123,8 @@ public interface DatabaseComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the message with the given ID. Unlike
|
* Deletes the message with the given ID. Unlike
|
||||||
* {@link #removeMessage(Transaction, MessageId)}, the message ID,
|
* {@link #removeMessage(Transaction, MessageId)}, the message ID and any
|
||||||
* dependencies, metadata, and any other associated state are not deleted.
|
* other associated data are not deleted.
|
||||||
*/
|
*/
|
||||||
void deleteMessage(Transaction txn, MessageId m) throws DbException;
|
void deleteMessage(Transaction txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
@@ -241,8 +228,7 @@ public interface DatabaseComponent {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<Group> getGroups(Transaction txn, ClientId c, int majorVersion)
|
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the given group's visibility to the given contact, or
|
* Returns the given group's visibility to the given contact, or
|
||||||
@@ -268,38 +254,31 @@ public interface DatabaseComponent {
|
|||||||
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
|
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/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
Collection<MessageId> getMessagesToValidate(Transaction txn, ClientId c)
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the IDs of any messages that need to be validated.
|
|
||||||
* <p/>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
Collection<MessageId> getMessagesToValidate(Transaction txn)
|
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that are pending delivery due to
|
* Returns the IDs of any messages that are valid but pending delivery due
|
||||||
* dependencies on other messages.
|
* to dependencies on other messages for the given client.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getPendingMessages(Transaction txn)
|
Collection<MessageId> getPendingMessages(Transaction txn, ClientId c)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that have shared dependents but have
|
* Returns the IDs of any messages from the given client
|
||||||
* not yet been shared themselves.
|
* that have a shared dependent, but are still not shared themselves.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessagesToShare(Transaction txn)
|
Collection<MessageId> getMessagesToShare(Transaction txn,
|
||||||
throws DbException;
|
ClientId c) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the message with the given ID, in serialised form, or null if
|
* Returns the message with the given ID, in serialised form, or null if
|
||||||
@@ -319,9 +298,9 @@ public interface DatabaseComponent {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata for any delivered messages in the given group with
|
* Returns the metadata for any messages in the given group with metadata
|
||||||
* metadata that matches all entries in the given query. If the query is
|
* that matches all entries in the given query. If the query is empty, the
|
||||||
* empty, the metadata for all delivered messages is returned.
|
* metadata for all messages is returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -337,8 +316,8 @@ public interface DatabaseComponent {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata for the given delivered or pending message.
|
* Returns the metadata for the given delivered and pending message.
|
||||||
* This is only meant to be used by the ValidationManager.
|
* This is meant to be only used by the ValidationManager
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -346,8 +325,8 @@ public interface DatabaseComponent {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of all delivered messages in the given group with
|
* Returns the status of all messages in the given group with respect to
|
||||||
* respect to the given contact.
|
* the given contact.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -356,8 +335,12 @@ public interface DatabaseComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs and states of all dependencies of the given message.
|
* Returns the IDs and states of all dependencies of the given message.
|
||||||
* For missing dependencies and dependencies in other groups, the state
|
* Missing dependencies have the state
|
||||||
* {@link State UNKNOWN} is returned.
|
* {@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/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -365,9 +348,9 @@ public interface DatabaseComponent {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs and states of all dependents of the given message.
|
* Returns all IDs of messages that depend on the given message.
|
||||||
* Dependents in other groups are not returned. If the given message is
|
* Messages in other groups that declare a dependency on the given message
|
||||||
* missing, no dependents are returned.
|
* will be returned even though such dependencies are invalid.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -382,24 +365,14 @@ public interface DatabaseComponent {
|
|||||||
State getMessageState(Transaction txn, MessageId m) throws DbException;
|
State getMessageState(Transaction txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of the given delivered message with respect to the
|
* Returns the status of the given message with respect to the given
|
||||||
* given contact.
|
* contact.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
||||||
throws DbException;
|
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.
|
* Returns all settings in the given namespace.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -412,14 +385,15 @@ public interface DatabaseComponent {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t)
|
Map<ContactId, TransportKeys> getTransportKeys(Transaction txn,
|
||||||
throws DbException;
|
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)
|
void incrementStreamCounter(Transaction txn, ContactId c, TransportId t,
|
||||||
throws DbException;
|
long rotationPeriod) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given metadata with the existing metadata for the given
|
* Merges the given metadata with the existing metadata for the given
|
||||||
@@ -489,12 +463,6 @@ public interface DatabaseComponent {
|
|||||||
*/
|
*/
|
||||||
void removeTransport(Transaction txn, TransportId t) throws DbException;
|
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.
|
* Marks the given contact as verified.
|
||||||
*/
|
*/
|
||||||
@@ -530,21 +498,15 @@ public interface DatabaseComponent {
|
|||||||
Collection<MessageId> dependencies) throws DbException;
|
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.
|
* 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;
|
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.
|
* Stores the given transport keys, deleting any keys they have replaced.
|
||||||
*/
|
*/
|
||||||
void updateTransportKeys(Transaction txn, Collection<KeySet> keys)
|
void updateTransportKeys(Transaction txn,
|
||||||
throws DbException;
|
Map<ContactId, TransportKeys> keys) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
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.
|
* A pseudonym for a user.
|
||||||
*/
|
*/
|
||||||
@@ -19,25 +17,20 @@ public class Author {
|
|||||||
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
|
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 AuthorId id;
|
||||||
private final int formatVersion;
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final byte[] publicKey;
|
private final byte[] publicKey;
|
||||||
|
|
||||||
public Author(AuthorId id, int formatVersion, String name,
|
public Author(AuthorId id, String name, byte[] publicKey) {
|
||||||
byte[] publicKey) {
|
int length;
|
||||||
int nameLength = StringUtils.toUtf8(name).length;
|
try {
|
||||||
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
|
length = name.getBytes("UTF-8").length;
|
||||||
throw new IllegalArgumentException();
|
} catch (UnsupportedEncodingException e) {
|
||||||
if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH)
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (length == 0 || length > AuthorConstants.MAX_AUTHOR_NAME_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.formatVersion = formatVersion;
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
}
|
}
|
||||||
@@ -49,13 +42,6 @@ public class Author {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the version of the author structure used to create the author.
|
|
||||||
*/
|
|
||||||
public int getFormatVersion() {
|
|
||||||
return formatVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the author's name.
|
* Returns the author's name.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
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 {
|
public interface AuthorConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,14 +8,26 @@ public interface AuthorConstants {
|
|||||||
int MAX_AUTHOR_NAME_LENGTH = 50;
|
int MAX_AUTHOR_NAME_LENGTH = 50;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum length of a public key in bytes. This applies to the
|
* The maximum length of a public key in bytes.
|
||||||
* signature algorithm used by the current {@link Author format version}.
|
* <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
|
* The maximum length of a signature in bytes.
|
||||||
* signature algorithm used by the current {@link Author format version}.
|
* <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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,27 +5,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface AuthorFactory {
|
public interface AuthorFactory {
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an author with the current format version and the given name and
|
|
||||||
* public key.
|
|
||||||
*/
|
|
||||||
Author createAuthor(String name, byte[] publicKey);
|
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,
|
LocalAuthor createLocalAuthor(String name, byte[] publicKey,
|
||||||
byte[] privateKey);
|
byte[] privateKey);
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a local author with the given format version, name and keys.
|
|
||||||
*/
|
|
||||||
LocalAuthor createLocalAuthor(int formatVersion, String name,
|
|
||||||
byte[] publicKey, byte[] privateKey);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public class LocalAuthor extends Author {
|
|||||||
private final byte[] privateKey;
|
private final byte[] privateKey;
|
||||||
private final long created;
|
private final long created;
|
||||||
|
|
||||||
public LocalAuthor(AuthorId id, int formatVersion, String name,
|
public LocalAuthor(AuthorId id, String name, byte[] publicKey,
|
||||||
byte[] publicKey, byte[] privateKey, long created) {
|
byte[] privateKey, long created) {
|
||||||
super(id, formatVersion, name, publicKey);
|
super(id, name, publicKey);
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
this.created = created;
|
this.created = created;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble.api.keyagreement;
|
|||||||
public interface KeyAgreementConstants {
|
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 = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the record header in bytes.
|
* The length of the record header in bytes.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.keyagreement;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
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.
|
* 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.
|
* Starts listening for incoming connections, and returns a Callable that
|
||||||
*
|
* will return a KeyAgreementConnection when an incoming connection is
|
||||||
* @throws IOException if an error occurs or {@link #close()} is called.
|
* received.
|
||||||
*/
|
*/
|
||||||
public abstract KeyAgreementConnection accept() throws IOException;
|
public abstract Callable<KeyAgreementConnection> listen();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the underlying server socket.
|
* Closes the underlying server socket.
|
||||||
|
|||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -21,42 +21,21 @@ public interface LifecycleManager {
|
|||||||
* The result of calling {@link #startServices(String)}.
|
* The result of calling {@link #startServices(String)}.
|
||||||
*/
|
*/
|
||||||
enum StartResult {
|
enum StartResult {
|
||||||
ALREADY_RUNNING,
|
ALREADY_RUNNING, DB_ERROR, SERVICE_ERROR, SUCCESS
|
||||||
DB_ERROR,
|
|
||||||
DATA_TOO_OLD_ERROR,
|
|
||||||
DATA_TOO_NEW_ERROR,
|
|
||||||
SERVICE_ERROR,
|
|
||||||
SUCCESS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The state the lifecycle can be in.
|
* Registers a {@link Service} to be started and stopped.
|
||||||
* 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)}.
|
|
||||||
*/
|
*/
|
||||||
void registerService(Service s);
|
void registerService(Service s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a {@link Client} to be started. This method should be called
|
* Registers a {@link Client} to be started.
|
||||||
* before {@link #startServices(String)}.
|
|
||||||
*/
|
*/
|
||||||
void registerClient(Client c);
|
void registerClient(Client c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an {@link ExecutorService} to be shut down. This method
|
* Registers an {@link ExecutorService} to be shut down.
|
||||||
* should be called before {@link #startServices(String)}.
|
|
||||||
*/
|
*/
|
||||||
void registerForShutdown(ExecutorService e);
|
void registerForShutdown(ExecutorService e);
|
||||||
|
|
||||||
@@ -92,10 +71,4 @@ public interface LifecycleManager {
|
|||||||
* the {@link DatabaseComponent} to be closed before returning.
|
* the {@link DatabaseComponent} to be closed before returning.
|
||||||
*/
|
*/
|
||||||
void waitForShutdown() throws InterruptedException;
|
void waitForShutdown() throws InterruptedException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current state of the lifecycle.
|
|
||||||
*/
|
|
||||||
LifecycleState getLifecycleState();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
|
}
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
package org.briarproject.bramble.api.plugin;
|
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
|
* Type-safe wrapper for a string that uniquely identifies a transport plugin.
|
||||||
* transport plugin.
|
|
||||||
*/
|
*/
|
||||||
public class TransportId {
|
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;
|
private final String id;
|
||||||
|
|
||||||
public TransportId(String id) {
|
public TransportId(String id) {
|
||||||
int length = StringUtils.toUtf8(id).length;
|
byte[] b = id.getBytes(Charset.forName("UTF-8"));
|
||||||
if (length == 0 || length > MAX_TRANSPORT_ID_LENGTH)
|
if (b.length == 0 || b.length > MAX_TRANSPORT_ID_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ public interface DuplexPlugin extends Plugin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to connect to the remote peer specified in the given descriptor.
|
* 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
|
@Nullable
|
||||||
DuplexTransportConnection createKeyAgreementConnection(
|
DuplexTransportConnection createKeyAgreementConnection(
|
||||||
byte[] remoteCommitment, BdfList descriptor);
|
byte[] remoteCommitment, BdfList descriptor, long timeout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
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
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -15,17 +15,12 @@ public interface TransportPropertyManager {
|
|||||||
/**
|
/**
|
||||||
* The unique ID of the transport property client.
|
* The unique ID of the transport property client.
|
||||||
*/
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.properties");
|
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current major version of the transport property client.
|
* The current version of the transport property client.
|
||||||
*/
|
*/
|
||||||
int MAJOR_VERSION = 0;
|
int CLIENT_VERSION = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* The current minor version of the transport property client.
|
|
||||||
*/
|
|
||||||
int MINOR_VERSION = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given properties received while adding a contact - they will
|
* Stores the given properties received while adding a contact - they will
|
||||||
@@ -42,8 +37,8 @@ public interface TransportPropertyManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the local transport properties for all transports.
|
* Returns the local transport properties for all transports.
|
||||||
* <p/>
|
* <br/>
|
||||||
* Read-only.
|
* TODO: Transaction can be read-only when code is simplified
|
||||||
*/
|
*/
|
||||||
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
|
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.record;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface RecordReaderFactory {
|
|
||||||
|
|
||||||
RecordReader createRecordReader(InputStream in);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.record;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public interface RecordWriterFactory {
|
|
||||||
|
|
||||||
RecordWriter createRecordWriter(OutputStream out);
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,19 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
package org.briarproject.bramble.api.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type-safe wrapper for a namespaced string that uniquely identifies a sync
|
* Wrapper for a name-spaced string that uniquely identifies a sync client.
|
||||||
* client.
|
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ClientId implements Comparable<ClientId> {
|
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;
|
private final String id;
|
||||||
|
|
||||||
public ClientId(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;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,43 +5,20 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
|
|||||||
public class Group {
|
public class Group {
|
||||||
|
|
||||||
public enum Visibility {
|
public enum Visibility {
|
||||||
|
INVISIBLE, // The group is not visible
|
||||||
INVISIBLE(0), // The group is not visible
|
VISIBLE, // The group is visible but messages are not shared
|
||||||
VISIBLE(1), // The group is visible, messages are accepted but not sent
|
SHARED // The group is visible and messages are shared
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The current version of the group format.
|
|
||||||
*/
|
|
||||||
public static final int FORMAT_VERSION = 1;
|
|
||||||
|
|
||||||
private final GroupId id;
|
private final GroupId id;
|
||||||
private final ClientId clientId;
|
private final ClientId clientId;
|
||||||
private final int majorVersion;
|
|
||||||
private final byte[] descriptor;
|
private final byte[] descriptor;
|
||||||
|
|
||||||
public Group(GroupId id, ClientId clientId, int majorVersion,
|
public Group(GroupId id, ClientId clientId, byte[] descriptor) {
|
||||||
byte[] descriptor) {
|
|
||||||
if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH)
|
if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.majorVersion = majorVersion;
|
|
||||||
this.descriptor = descriptor;
|
this.descriptor = descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +36,6 @@ public class Group {
|
|||||||
return clientId;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the major version of the client to which the group belongs.
|
|
||||||
*/
|
|
||||||
public int getMajorVersion() {
|
|
||||||
return majorVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the group's descriptor.
|
* Returns the group's descriptor.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
public interface GroupFactory {
|
public interface GroupFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group with the given client ID, major version and descriptor.
|
* Creates a group with the given client ID, client version and descriptor.
|
||||||
*/
|
*/
|
||||||
Group createGroup(ClientId c, int majorVersion, byte[] descriptor);
|
Group createGroup(ClientId c, int clientVersion, byte[] descriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LEN
|
|||||||
|
|
||||||
public class Message {
|
public class Message {
|
||||||
|
|
||||||
/**
|
|
||||||
* The current version of the message format.
|
|
||||||
*/
|
|
||||||
public static final int FORMAT_VERSION = 1;
|
|
||||||
|
|
||||||
private final MessageId id;
|
private final MessageId id;
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
|
|||||||
@@ -7,7 +7,5 @@ public interface MessageFactory {
|
|||||||
|
|
||||||
Message createMessage(GroupId g, long timestamp, byte[] body);
|
Message createMessage(GroupId g, long timestamp, byte[] body);
|
||||||
|
|
||||||
Message createMessage(byte[] raw);
|
|
||||||
|
|
||||||
Message createMessage(MessageId m, byte[] raw);
|
Message createMessage(MessageId m, byte[] raw);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,7 @@ public class MessageId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing messages to calculate their identifiers.
|
* Label for hashing messages to calculate their identifiers.
|
||||||
*/
|
*/
|
||||||
public static final String ID_LABEL = "org.briarproject.bramble/MESSAGE_ID";
|
public static final String LABEL = "org.briarproject.bramble/MESSAGE_ID";
|
||||||
|
|
||||||
/**
|
|
||||||
* Label for hashing blocks of messages.
|
|
||||||
*/
|
|
||||||
public static final String BLOCK_LABEL =
|
|
||||||
"org.briarproject.bramble/MESSAGE_BLOCK";
|
|
||||||
|
|
||||||
public MessageId(byte[] id) {
|
public MessageId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncRecordReader {
|
public interface RecordReader {
|
||||||
|
|
||||||
boolean eof() throws IOException;
|
boolean eof() throws IOException;
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncRecordReaderFactory {
|
public interface RecordReaderFactory {
|
||||||
|
|
||||||
SyncRecordReader createRecordReader(InputStream in);
|
RecordReader createRecordReader(InputStream in);
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncRecordWriter {
|
public interface RecordWriter {
|
||||||
|
|
||||||
void writeAck(Ack a) throws IOException;
|
void writeAck(Ack a) throws IOException;
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface SyncRecordWriterFactory {
|
public interface RecordWriterFactory {
|
||||||
|
|
||||||
SyncRecordWriter createRecordWriter(OutputStream out);
|
RecordWriter createRecordWriter(OutputStream out);
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,6 @@ package org.briarproject.bramble.api.sync;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
|
||||||
|
|
||||||
public interface SyncConstants {
|
public interface SyncConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,8 +10,16 @@ public interface SyncConstants {
|
|||||||
byte PROTOCOL_VERSION = 0;
|
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
|
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.
|
* 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.sync;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
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.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,20 +33,15 @@ public interface ValidationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the message validator for the given client. This method
|
* Sets the message validator for the given client.
|
||||||
* should be called before {@link LifecycleManager#startServices(String)}.
|
|
||||||
*/
|
*/
|
||||||
void registerMessageValidator(ClientId c, int majorVersion,
|
void registerMessageValidator(ClientId c, MessageValidator v);
|
||||||
MessageValidator v);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the incoming message hook for the given client. The hook will
|
* Sets the incoming message hook for the given client. The hook will be
|
||||||
* be called once for each incoming message that passes validation. This
|
* called once for each incoming message that passes validation.
|
||||||
* method should be called before
|
|
||||||
* {@link LifecycleManager#startServices(String)}.
|
|
||||||
*/
|
*/
|
||||||
void registerIncomingMessageHook(ClientId c, int majorVersion,
|
void registerIncomingMessageHook(ClientId c, IncomingMessageHook hook);
|
||||||
IncomingMessageHook hook);
|
|
||||||
|
|
||||||
interface MessageValidator {
|
interface MessageValidator {
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,55 +16,13 @@ public interface KeyManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Informs the key manager that a new contact has been added. Derives and
|
* 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
|
* stores transport keys for communicating with the contact.
|
||||||
* each transport.
|
|
||||||
* <p/>
|
|
||||||
* {@link StreamContext StreamContexts} for the contact can be created
|
* {@link StreamContext StreamContexts} for the contact can be created
|
||||||
* after this method has returned.
|
* after this method has returned.
|
||||||
*
|
|
||||||
* @param alice true if the local party is Alice
|
|
||||||
*/
|
*/
|
||||||
void addContact(Transaction txn, ContactId c, SecretKey master,
|
void addContact(Transaction txn, ContactId c, SecretKey master,
|
||||||
long timestamp, boolean alice) throws DbException;
|
long timestamp, boolean alice) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives and stores a set of unbound transport keys for each transport
|
|
||||||
* and returns the key set IDs.
|
|
||||||
* <p/>
|
|
||||||
* The keys must be bound before they can be used for incoming streams,
|
|
||||||
* and also activated before they can be used for outgoing streams.
|
|
||||||
*
|
|
||||||
* @param alice true if the local party is Alice
|
|
||||||
*/
|
|
||||||
Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master,
|
|
||||||
long timestamp, boolean alice) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the given transport keys to the given contact.
|
|
||||||
*/
|
|
||||||
void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the given transport keys as usable for outgoing streams. Keys must
|
|
||||||
* be bound before they are activated.
|
|
||||||
*/
|
|
||||||
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given transport keys, which must not have been bound, from
|
|
||||||
* the manager and the database.
|
|
||||||
*/
|
|
||||||
void removeKeys(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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link StreamContext} for sending a stream to the given
|
* Returns a {@link StreamContext} for sending a stream to the given
|
||||||
* contact over the given transport, or null if an error occurs or the
|
* contact over the given transport, or null if an error occurs or the
|
||||||
|
|||||||
@@ -1,51 +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.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of transport keys for communicating with a contact. If the keys have
|
|
||||||
* not yet been bound to a contact, {@link #getContactId()}} returns null.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class KeySet {
|
|
||||||
|
|
||||||
private final KeySetId keySetId;
|
|
||||||
@Nullable
|
|
||||||
private final ContactId contactId;
|
|
||||||
private final TransportKeys transportKeys;
|
|
||||||
|
|
||||||
public KeySet(KeySetId keySetId, @Nullable ContactId contactId,
|
|
||||||
TransportKeys transportKeys) {
|
|
||||||
this.keySetId = keySetId;
|
|
||||||
this.contactId = contactId;
|
|
||||||
this.transportKeys = transportKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeySetId getKeySetId() {
|
|
||||||
return keySetId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,20 +10,18 @@ public class OutgoingKeys {
|
|||||||
|
|
||||||
private final SecretKey tagKey, headerKey;
|
private final SecretKey tagKey, headerKey;
|
||||||
private final long rotationPeriod, streamCounter;
|
private final long rotationPeriod, streamCounter;
|
||||||
private final boolean active;
|
|
||||||
|
|
||||||
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
||||||
long rotationPeriod, boolean active) {
|
long rotationPeriod) {
|
||||||
this(tagKey, headerKey, rotationPeriod, 0, active);
|
this(tagKey, headerKey, rotationPeriod, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
||||||
long rotationPeriod, long streamCounter, boolean active) {
|
long rotationPeriod, long streamCounter) {
|
||||||
this.tagKey = tagKey;
|
this.tagKey = tagKey;
|
||||||
this.headerKey = headerKey;
|
this.headerKey = headerKey;
|
||||||
this.rotationPeriod = rotationPeriod;
|
this.rotationPeriod = rotationPeriod;
|
||||||
this.streamCounter = streamCounter;
|
this.streamCounter = streamCounter;
|
||||||
this.active = active;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKey getTagKey() {
|
public SecretKey getTagKey() {
|
||||||
@@ -41,8 +39,4 @@ public class OutgoingKeys {
|
|||||||
public long getStreamCounter() {
|
public long getStreamCounter() {
|
||||||
return streamCounter;
|
return streamCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
|
||||||
return active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -126,10 +126,6 @@ public class StringUtils {
|
|||||||
return toUtf8(s).length > maxLength;
|
return toUtf8(s).length > maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidMac(String mac) {
|
|
||||||
return MAC.matcher(mac).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] macToBytes(String mac) {
|
public static byte[] macToBytes(String mac) {
|
||||||
if (!MAC.matcher(mac).matches()) throw new IllegalArgumentException();
|
if (!MAC.matcher(mac).matches()) throw new IllegalArgumentException();
|
||||||
return fromHexString(mac.replaceAll(":", ""));
|
return fromHexString(mac.replaceAll(":", ""));
|
||||||
|
|||||||
@@ -2,39 +2,12 @@ package org.briarproject.bramble.test;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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 org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.File;
|
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.Random;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
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 {
|
public class TestUtils {
|
||||||
|
|
||||||
private static final AtomicInteger nextTestDir =
|
private static final AtomicInteger nextTestDir =
|
||||||
@@ -61,117 +34,8 @@ public class TestUtils {
|
|||||||
return getRandomBytes(UniqueId.LENGTH);
|
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() {
|
public static SecretKey getSecretKey() {
|
||||||
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ dependencies {
|
|||||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
implementation 'org.bitlet:weupnp:0.1.4'
|
implementation 'org.bitlet:weupnp:0.1.4'
|
||||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
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'
|
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
@@ -24,6 +23,7 @@ dependencies {
|
|||||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||||
|
testImplementation "org.whispersystems:curve25519-java:0.4.1"
|
||||||
|
|
||||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ dependencies {
|
|||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
'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-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-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.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
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.db.DatabaseExecutorModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
@@ -10,13 +10,12 @@ import org.briarproject.bramble.properties.PropertiesModule;
|
|||||||
import org.briarproject.bramble.sync.SyncModule;
|
import org.briarproject.bramble.sync.SyncModule;
|
||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
import org.briarproject.bramble.versioning.VersioningModule;
|
|
||||||
|
|
||||||
public interface BrambleCoreEagerSingletons {
|
public interface BrambleCoreEagerSingletons {
|
||||||
|
|
||||||
void inject(ContactModule.EagerSingletons init);
|
void inject(ContactModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(CryptoExecutorModule.EagerSingletons init);
|
void inject(CryptoModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(DatabaseExecutorModule.EagerSingletons init);
|
void inject(DatabaseExecutorModule.EagerSingletons init);
|
||||||
|
|
||||||
@@ -33,6 +32,4 @@ public interface BrambleCoreEagerSingletons {
|
|||||||
void inject(SystemModule.EagerSingletons init);
|
void inject(SystemModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(TransportModule.EagerSingletons init);
|
void inject(TransportModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(VersioningModule.EagerSingletons init);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble;
|
|||||||
|
|
||||||
import org.briarproject.bramble.client.ClientModule;
|
import org.briarproject.bramble.client.ClientModule;
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
|
||||||
import org.briarproject.bramble.crypto.CryptoModule;
|
import org.briarproject.bramble.crypto.CryptoModule;
|
||||||
import org.briarproject.bramble.data.DataModule;
|
import org.briarproject.bramble.data.DataModule;
|
||||||
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
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.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
|
||||||
import org.briarproject.bramble.reliability.ReliabilityModule;
|
import org.briarproject.bramble.reliability.ReliabilityModule;
|
||||||
import org.briarproject.bramble.reporting.ReportingModule;
|
import org.briarproject.bramble.reporting.ReportingModule;
|
||||||
import org.briarproject.bramble.settings.SettingsModule;
|
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.sync.SyncModule;
|
||||||
import org.briarproject.bramble.system.SystemModule;
|
import org.briarproject.bramble.system.SystemModule;
|
||||||
import org.briarproject.bramble.transport.TransportModule;
|
import org.briarproject.bramble.transport.TransportModule;
|
||||||
import org.briarproject.bramble.versioning.VersioningModule;
|
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
@@ -29,7 +26,6 @@ import dagger.Module;
|
|||||||
ClientModule.class,
|
ClientModule.class,
|
||||||
ContactModule.class,
|
ContactModule.class,
|
||||||
CryptoModule.class,
|
CryptoModule.class,
|
||||||
CryptoExecutorModule.class,
|
|
||||||
DataModule.class,
|
DataModule.class,
|
||||||
DatabaseModule.class,
|
DatabaseModule.class,
|
||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
@@ -39,21 +35,19 @@ import dagger.Module;
|
|||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
PropertiesModule.class,
|
PropertiesModule.class,
|
||||||
RecordModule.class,
|
|
||||||
ReliabilityModule.class,
|
ReliabilityModule.class,
|
||||||
ReportingModule.class,
|
ReportingModule.class,
|
||||||
SettingsModule.class,
|
SettingsModule.class,
|
||||||
SocksModule.class,
|
SocksModule.class,
|
||||||
SyncModule.class,
|
SyncModule.class,
|
||||||
SystemModule.class,
|
SystemModule.class,
|
||||||
TransportModule.class,
|
TransportModule.class
|
||||||
VersioningModule.class
|
|
||||||
})
|
})
|
||||||
public class BrambleCoreModule {
|
public class BrambleCoreModule {
|
||||||
|
|
||||||
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
|
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
|
||||||
c.inject(new ContactModule.EagerSingletons());
|
c.inject(new ContactModule.EagerSingletons());
|
||||||
c.inject(new CryptoExecutorModule.EagerSingletons());
|
c.inject(new CryptoModule.EagerSingletons());
|
||||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||||
c.inject(new IdentityModule.EagerSingletons());
|
c.inject(new IdentityModule.EagerSingletons());
|
||||||
c.inject(new LifecycleModule.EagerSingletons());
|
c.inject(new LifecycleModule.EagerSingletons());
|
||||||
@@ -62,6 +56,5 @@ public class BrambleCoreModule {
|
|||||||
c.inject(new SyncModule.EagerSingletons());
|
c.inject(new SyncModule.EagerSingletons());
|
||||||
c.inject(new SystemModule.EagerSingletons());
|
c.inject(new SystemModule.EagerSingletons());
|
||||||
c.inject(new TransportModule.EagerSingletons());
|
c.inject(new TransportModule.EagerSingletons());
|
||||||
c.inject(new VersioningModule.EagerSingletons());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
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.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.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
@@ -36,14 +32,7 @@ import java.util.Map.Entry;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
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.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -62,14 +51,12 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
private final MetadataParser metadataParser;
|
private final MetadataParser metadataParser;
|
||||||
private final MetadataEncoder metadataEncoder;
|
private final MetadataEncoder metadataEncoder;
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
private final AuthorFactory authorFactory;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ClientHelperImpl(DatabaseComponent db, MessageFactory messageFactory,
|
ClientHelperImpl(DatabaseComponent db, MessageFactory messageFactory,
|
||||||
BdfReaderFactory bdfReaderFactory,
|
BdfReaderFactory bdfReaderFactory,
|
||||||
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
|
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
|
||||||
MetadataEncoder metadataEncoder, CryptoComponent crypto,
|
MetadataEncoder metadataEncoder, CryptoComponent crypto) {
|
||||||
AuthorFactory authorFactory) {
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.messageFactory = messageFactory;
|
this.messageFactory = messageFactory;
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
this.bdfReaderFactory = bdfReaderFactory;
|
||||||
@@ -77,7 +64,6 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
this.metadataParser = metadataParser;
|
this.metadataParser = metadataParser;
|
||||||
this.metadataEncoder = metadataEncoder;
|
this.metadataEncoder = metadataEncoder;
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
this.authorFactory = authorFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
public BdfList toList(byte[] b, int off, int len) throws FormatException {
|
public BdfList toList(byte[] b, int off, int len) throws FormatException {
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
|
ByteArrayInputStream in = new ByteArrayInputStream(b, off, len);
|
||||||
@@ -369,11 +341,6 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
raw.length - MESSAGE_HEADER_LENGTH);
|
raw.length - MESSAGE_HEADER_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BdfList toList(Author a) {
|
|
||||||
return BdfList.of(a.getFormatVersion(), a.getName(), a.getPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
public byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
||||||
throws FormatException, GeneralSecurityException {
|
throws FormatException, GeneralSecurityException {
|
||||||
@@ -381,53 +348,11 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verifySignature(byte[] signature, String label, BdfList signed,
|
public void verifySignature(String label, byte[] sig, byte[] publicKey,
|
||||||
byte[] publicKey) throws FormatException, GeneralSecurityException {
|
BdfList signed) throws FormatException, GeneralSecurityException {
|
||||||
if (!crypto.verifySignature(signature, label, toByteArray(signed),
|
if (!crypto.verify(label, toByteArray(signed), publicKey, sig)) {
|
||||||
publicKey)) {
|
|
||||||
throw new GeneralSecurityException("Invalid signature");
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ package org.briarproject.bramble.client;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
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.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -10,14 +18,19 @@ import dagger.Provides;
|
|||||||
public class ClientModule {
|
public class ClientModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
ClientHelper provideClientHelper(ClientHelperImpl clientHelper) {
|
ClientHelper provideClientHelper(DatabaseComponent db,
|
||||||
return clientHelper;
|
MessageFactory messageFactory, BdfReaderFactory bdfReaderFactory,
|
||||||
|
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
|
||||||
|
MetadataEncoder metadataEncoder, CryptoComponent cryptoComponent) {
|
||||||
|
return new ClientHelperImpl(db, messageFactory, bdfReaderFactory,
|
||||||
|
bdfWriterFactory, metadataParser, metadataEncoder,
|
||||||
|
cryptoComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
ContactGroupFactory provideContactGroupFactory(
|
ContactGroupFactory provideContactGroupFactory(GroupFactory groupFactory,
|
||||||
ContactGroupFactoryImpl contactGroupFactory) {
|
ClientHelper clientHelper) {
|
||||||
return contactGroupFactory;
|
return new ContactGroupFactoryImpl(groupFactory, clientHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,25 +32,25 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createLocalGroup(ClientId clientId, int majorVersion) {
|
public Group createLocalGroup(ClientId clientId, int clientVersion) {
|
||||||
return groupFactory.createGroup(clientId, majorVersion,
|
return groupFactory.createGroup(clientId, clientVersion,
|
||||||
LOCAL_GROUP_DESCRIPTOR);
|
LOCAL_GROUP_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, int majorVersion,
|
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
Contact contact) {
|
Contact contact) {
|
||||||
AuthorId local = contact.getLocalAuthorId();
|
AuthorId local = contact.getLocalAuthorId();
|
||||||
AuthorId remote = contact.getAuthor().getId();
|
AuthorId remote = contact.getAuthor().getId();
|
||||||
byte[] descriptor = createGroupDescriptor(local, remote);
|
byte[] descriptor = createGroupDescriptor(local, remote);
|
||||||
return groupFactory.createGroup(clientId, majorVersion, descriptor);
|
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, int majorVersion,
|
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
AuthorId authorId1, AuthorId authorId2) {
|
AuthorId authorId1, AuthorId authorId2) {
|
||||||
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
||||||
return groupFactory.createGroup(clientId, majorVersion, descriptor);
|
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
package org.briarproject.bramble.contact;
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
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.ContactExchangeListener;
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
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.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.ContactExistsException;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
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.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
@@ -23,30 +26,29 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
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.system.Clock;
|
||||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
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.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
||||||
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -59,9 +61,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
"org.briarproject.briar.contact/EXCHANGE";
|
"org.briarproject.briar.contact/EXCHANGE";
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final ClientHelper clientHelper;
|
private final AuthorFactory authorFactory;
|
||||||
private final RecordReaderFactory recordReaderFactory;
|
private final BdfReaderFactory bdfReaderFactory;
|
||||||
private final RecordWriterFactory recordWriterFactory;
|
private final BdfWriterFactory bdfWriterFactory;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final ConnectionManager connectionManager;
|
private final ConnectionManager connectionManager;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
@@ -78,17 +80,17 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
private volatile boolean alice;
|
private volatile boolean alice;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper,
|
ContactExchangeTaskImpl(DatabaseComponent db,
|
||||||
RecordReaderFactory recordReaderFactory,
|
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
|
||||||
RecordWriterFactory recordWriterFactory, Clock clock,
|
BdfWriterFactory bdfWriterFactory, Clock clock,
|
||||||
ConnectionManager connectionManager, ContactManager contactManager,
|
ConnectionManager connectionManager, ContactManager contactManager,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
|
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
|
||||||
StreamWriterFactory streamWriterFactory) {
|
StreamWriterFactory streamWriterFactory) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.clientHelper = clientHelper;
|
this.authorFactory = authorFactory;
|
||||||
this.recordReaderFactory = recordReaderFactory;
|
this.bdfReaderFactory = bdfReaderFactory;
|
||||||
this.recordWriterFactory = recordWriterFactory;
|
this.bdfWriterFactory = bdfWriterFactory;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
@@ -123,18 +125,18 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
listener.contactExchangeFailed();
|
listener.contactExchangeFailed();
|
||||||
tryToClose(conn);
|
tryToClose(conn, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the local transport properties
|
// Get the local transport properties
|
||||||
Map<TransportId, TransportProperties> localProperties;
|
Map<TransportId, TransportProperties> localProperties, remoteProperties;
|
||||||
try {
|
try {
|
||||||
localProperties = transportPropertyManager.getLocalProperties();
|
localProperties = transportPropertyManager.getLocalProperties();
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
listener.contactExchangeFailed();
|
listener.contactExchangeFailed();
|
||||||
tryToClose(conn);
|
tryToClose(conn, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,138 +150,152 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
InputStream streamReader =
|
InputStream streamReader =
|
||||||
streamReaderFactory.createContactExchangeStreamReader(in,
|
streamReaderFactory.createContactExchangeStreamReader(in,
|
||||||
alice ? bobHeaderKey : aliceHeaderKey);
|
alice ? bobHeaderKey : aliceHeaderKey);
|
||||||
RecordReader recordReader =
|
BdfReader r = bdfReaderFactory.createReader(streamReader);
|
||||||
recordReaderFactory.createRecordReader(streamReader);
|
|
||||||
|
|
||||||
// Create the writers
|
// Create the writers
|
||||||
OutputStream streamWriter =
|
OutputStream streamWriter =
|
||||||
streamWriterFactory.createContactExchangeStreamWriter(out,
|
streamWriterFactory.createContactExchangeStreamWriter(out,
|
||||||
alice ? aliceHeaderKey : bobHeaderKey);
|
alice ? aliceHeaderKey : bobHeaderKey);
|
||||||
RecordWriter recordWriter =
|
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||||
recordWriterFactory.createRecordWriter(streamWriter);
|
|
||||||
|
|
||||||
// Derive the nonces to be signed
|
// Derive the nonces to be signed
|
||||||
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
||||||
new byte[] {PROTOCOL_VERSION});
|
new byte[] {PROTOCOL_VERSION});
|
||||||
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
||||||
new byte[] {PROTOCOL_VERSION});
|
new byte[] {PROTOCOL_VERSION});
|
||||||
byte[] localNonce = alice ? aliceNonce : bobNonce;
|
|
||||||
byte[] remoteNonce = alice ? bobNonce : aliceNonce;
|
|
||||||
|
|
||||||
// Sign the nonce
|
// Exchange pseudonyms, signed nonces, and timestamps
|
||||||
byte[] localSignature = sign(localAuthor, localNonce);
|
|
||||||
|
|
||||||
// Exchange contact info
|
|
||||||
long localTimestamp = clock.currentTimeMillis();
|
long localTimestamp = clock.currentTimeMillis();
|
||||||
ContactInfo remoteInfo;
|
Author remoteAuthor;
|
||||||
|
long remoteTimestamp;
|
||||||
try {
|
try {
|
||||||
if (alice) {
|
if (alice) {
|
||||||
sendContactInfo(recordWriter, localAuthor, localProperties,
|
sendPseudonym(w, aliceNonce);
|
||||||
localSignature, localTimestamp);
|
sendTimestamp(w, localTimestamp);
|
||||||
recordWriter.flush();
|
sendTransportProperties(w, localProperties);
|
||||||
remoteInfo = receiveContactInfo(recordReader);
|
w.flush();
|
||||||
|
remoteAuthor = receivePseudonym(r, bobNonce);
|
||||||
|
remoteTimestamp = receiveTimestamp(r);
|
||||||
|
remoteProperties = receiveTransportProperties(r);
|
||||||
} else {
|
} else {
|
||||||
remoteInfo = receiveContactInfo(recordReader);
|
remoteAuthor = receivePseudonym(r, aliceNonce);
|
||||||
sendContactInfo(recordWriter, localAuthor, localProperties,
|
remoteTimestamp = receiveTimestamp(r);
|
||||||
localSignature, localTimestamp);
|
remoteProperties = receiveTransportProperties(r);
|
||||||
recordWriter.flush();
|
sendPseudonym(w, bobNonce);
|
||||||
|
sendTimestamp(w, localTimestamp);
|
||||||
|
sendTransportProperties(w, localProperties);
|
||||||
|
w.flush();
|
||||||
}
|
}
|
||||||
// Close the outgoing stream
|
// Close the outgoing stream and expect EOF on the incoming stream
|
||||||
recordWriter.close();
|
w.close();
|
||||||
// Skip any remaining records from the incoming stream
|
if (!r.eof()) LOG.warning("Unexpected data at end of connection");
|
||||||
try {
|
} catch (GeneralSecurityException | IOException e) {
|
||||||
while (true) recordReader.readRecord();
|
|
||||||
} catch (EOFException expected) {
|
|
||||||
LOG.info("End of stream");
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
listener.contactExchangeFailed();
|
listener.contactExchangeFailed();
|
||||||
tryToClose(conn);
|
tryToClose(conn, true);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the contact's signature
|
|
||||||
if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) {
|
|
||||||
LOG.warning("Invalid signature");
|
|
||||||
listener.contactExchangeFailed();
|
|
||||||
tryToClose(conn);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The agreed timestamp is the minimum of the peers' timestamps
|
// The agreed timestamp is the minimum of the peers' timestamps
|
||||||
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
long timestamp = Math.min(localTimestamp, remoteTimestamp);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Add the contact
|
// Add the contact
|
||||||
ContactId contactId = addContact(remoteInfo.author, timestamp,
|
ContactId contactId = addContact(remoteAuthor, timestamp,
|
||||||
remoteInfo.properties);
|
remoteProperties);
|
||||||
// Reuse the connection as a transport connection
|
// Reuse the connection as a transport connection
|
||||||
connectionManager.manageOutgoingConnection(contactId, transportId,
|
connectionManager.manageOutgoingConnection(contactId, transportId,
|
||||||
conn);
|
conn);
|
||||||
// Pseudonym exchange succeeded
|
// Pseudonym exchange succeeded
|
||||||
LOG.info("Pseudonym exchange succeeded");
|
LOG.info("Pseudonym exchange succeeded");
|
||||||
listener.contactExchangeSucceeded(remoteInfo.author);
|
listener.contactExchangeSucceeded(remoteAuthor);
|
||||||
} catch (ContactExistsException e) {
|
} catch (ContactExistsException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
tryToClose(conn);
|
tryToClose(conn, true);
|
||||||
listener.duplicateContact(remoteInfo.author);
|
listener.duplicateContact(remoteAuthor);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
tryToClose(conn);
|
tryToClose(conn, true);
|
||||||
listener.contactExchangeFailed();
|
listener.contactExchangeFailed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] sign(LocalAuthor author, byte[] nonce) {
|
private void sendPseudonym(BdfWriter w, byte[] nonce)
|
||||||
try {
|
throws GeneralSecurityException, IOException {
|
||||||
return crypto.sign(SIGNING_LABEL_EXCHANGE, nonce,
|
// Sign the nonce
|
||||||
author.getPrivateKey());
|
byte[] privateKey = localAuthor.getPrivateKey();
|
||||||
} catch (GeneralSecurityException e) {
|
byte[] sig = crypto.sign(SIGNING_LABEL_EXCHANGE, nonce, privateKey);
|
||||||
throw new AssertionError();
|
|
||||||
|
// 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) {
|
private void sendTimestamp(BdfWriter w, long timestamp)
|
||||||
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)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Record record;
|
w.writeLong(timestamp);
|
||||||
do {
|
LOG.info("Sent timestamp");
|
||||||
record = recordReader.readRecord();
|
}
|
||||||
if (record.getProtocolVersion() != PROTOCOL_VERSION)
|
|
||||||
throw new FormatException();
|
private long receiveTimestamp(BdfReader r) throws IOException {
|
||||||
} while (record.getRecordType() != CONTACT_INFO);
|
long timestamp = r.readLong();
|
||||||
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();
|
if (timestamp < 0) throw new FormatException();
|
||||||
return new ContactInfo(author, properties, signature, timestamp);
|
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, long timestamp,
|
private ContactId addContact(Author remoteAuthor, long timestamp,
|
||||||
@@ -300,30 +316,13 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(DuplexTransportConnection conn) {
|
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
|
||||||
try {
|
try {
|
||||||
LOG.info("Closing connection");
|
LOG.info("Closing connection");
|
||||||
conn.getReader().dispose(true, true);
|
conn.getReader().dispose(exception, true);
|
||||||
conn.getWriter().dispose(true);
|
conn.getWriter().dispose(exception);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,37 +27,36 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final KeyManager keyManager;
|
private final KeyManager keyManager;
|
||||||
private final List<ContactHook> hooks;
|
private final List<AddContactHook> addHooks;
|
||||||
|
private final List<RemoveContactHook> removeHooks;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
|
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.keyManager = keyManager;
|
this.keyManager = keyManager;
|
||||||
hooks = new CopyOnWriteArrayList<>();
|
addHooks = new CopyOnWriteArrayList<>();
|
||||||
|
removeHooks = new CopyOnWriteArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerContactHook(ContactHook hook) {
|
public void registerAddContactHook(AddContactHook hook) {
|
||||||
hooks.add(hook);
|
addHooks.add(hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerRemoveContactHook(RemoveContactHook hook) {
|
||||||
|
removeHooks.add(hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||||
SecretKey master, long timestamp, boolean alice, boolean verified,
|
SecretKey master,long timestamp, boolean alice, boolean verified,
|
||||||
boolean active) throws DbException {
|
boolean active) throws DbException {
|
||||||
ContactId c = db.addContact(txn, remote, local, verified, active);
|
ContactId c = db.addContact(txn, remote, local, verified, active);
|
||||||
keyManager.addContact(txn, c, master, timestamp, alice);
|
keyManager.addContact(txn, c, master, timestamp, alice);
|
||||||
Contact contact = db.getContact(txn, c);
|
Contact contact = db.getContact(txn, c);
|
||||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
for (AddContactHook hook : addHooks)
|
||||||
return c;
|
hook.addingContact(txn, contact);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
|
||||||
boolean verified, boolean active) throws DbException {
|
|
||||||
ContactId c = db.addContact(txn, remote, local, verified, active);
|
|
||||||
Contact contact = db.getContact(txn, c);
|
|
||||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +156,7 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
@Override
|
@Override
|
||||||
public boolean contactExists(AuthorId remoteAuthorId,
|
public boolean contactExists(AuthorId remoteAuthorId,
|
||||||
AuthorId localAuthorId) throws DbException {
|
AuthorId localAuthorId) throws DbException {
|
||||||
boolean exists;
|
boolean exists = false;
|
||||||
Transaction txn = db.startTransaction(true);
|
Transaction txn = db.startTransaction(true);
|
||||||
try {
|
try {
|
||||||
exists = contactExists(txn, remoteAuthorId, localAuthorId);
|
exists = contactExists(txn, remoteAuthorId, localAuthorId);
|
||||||
@@ -172,7 +171,8 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
public void removeContact(Transaction txn, ContactId c)
|
public void removeContact(Transaction txn, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Contact contact = db.getContact(txn, c);
|
Contact contact = db.getContact(txn, c);
|
||||||
for (ContactHook hook : hooks) hook.removingContact(txn, contact);
|
for (RemoveContactHook hook : removeHooks)
|
||||||
|
hook.removingContact(txn, contact);
|
||||||
db.removeContact(txn, c);
|
db.removeContact(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,547 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The BLAKE2 cryptographic hash function was designed by Jean-
|
||||||
|
Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
|
||||||
|
Winnerlein.
|
||||||
|
|
||||||
|
Reference Implementation and Description can be found at: https://blake2.net/
|
||||||
|
RFC: https://tools.ietf.org/html/rfc7693
|
||||||
|
|
||||||
|
This implementation does not support the Tree Hashing Mode.
|
||||||
|
|
||||||
|
For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based
|
||||||
|
message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2.
|
||||||
|
|
||||||
|
Algorithm | Target | Collision | Hash | Hash ASN.1 |
|
||||||
|
Identifier | Arch | Security | nn | OID Suffix |
|
||||||
|
---------------+--------+-----------+------+------------+
|
||||||
|
id-blake2s128 | 32-bit | 2**64 | 16 | x.2.4 |
|
||||||
|
id-blake2s160 | 32-bit | 2**80 | 20 | x.2.5 |
|
||||||
|
id-blake2s224 | 32-bit | 2**112 | 28 | x.2.7 |
|
||||||
|
id-blake2s256 | 32-bit | 2**128 | 32 | x.2.8 |
|
||||||
|
---------------+--------+-----------+------+------------+
|
||||||
|
|
||||||
|
Based on the BouncyCastle implementation of BLAKE2b. License:
|
||||||
|
|
||||||
|
Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc.
|
||||||
|
(http://www.bouncycastle.org)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.spongycastle.crypto.ExtendedDigest;
|
||||||
|
import org.spongycastle.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the cryptographic hash function BLAKE2s.
|
||||||
|
* <p/>
|
||||||
|
* BLAKE2s offers a built-in keying mechanism to be used directly
|
||||||
|
* for authentication ("Prefix-MAC") rather than a HMAC construction.
|
||||||
|
* <p/>
|
||||||
|
* BLAKE2s offers a built-in support for a salt for randomized hashing
|
||||||
|
* and a personal string for defining a unique hash function for each application.
|
||||||
|
* <p/>
|
||||||
|
* BLAKE2s is optimized for 32-bit platforms and produces digests of any size
|
||||||
|
* between 1 and 32 bytes.
|
||||||
|
*/
|
||||||
|
public class Blake2sDigest implements ExtendedDigest {
|
||||||
|
/** BLAKE2s Initialization Vector **/
|
||||||
|
private static final int blake2s_IV[] =
|
||||||
|
// Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19.
|
||||||
|
// The same as SHA-256 IV.
|
||||||
|
{
|
||||||
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372,
|
||||||
|
0xa54ff53a, 0x510e527f, 0x9b05688c,
|
||||||
|
0x1f83d9ab, 0x5be0cd19
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Message word permutations **/
|
||||||
|
private static final byte[][] blake2s_sigma =
|
||||||
|
{
|
||||||
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||||
|
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||||
|
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
||||||
|
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
||||||
|
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
||||||
|
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
||||||
|
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
||||||
|
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
||||||
|
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
||||||
|
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int ROUNDS = 10; // to use for Catenas H'
|
||||||
|
private static final int BLOCK_LENGTH_BYTES = 64;// bytes
|
||||||
|
|
||||||
|
// General parameters:
|
||||||
|
private int digestLength = 32; // 1- 32 bytes
|
||||||
|
private int keyLength = 0; // 0 - 32 bytes for keyed hashing for MAC
|
||||||
|
private byte[] salt = null;
|
||||||
|
private byte[] personalization = null;
|
||||||
|
private byte[] key = null;
|
||||||
|
|
||||||
|
// Tree hashing parameters:
|
||||||
|
// Because this class does not implement the Tree Hashing Mode,
|
||||||
|
// these parameters can be treated as constants (see init() function)
|
||||||
|
/*
|
||||||
|
* private int fanout = 1; // 0-255
|
||||||
|
* private int depth = 1; // 1 - 255
|
||||||
|
* private int leafLength= 0;
|
||||||
|
* private long nodeOffset = 0L;
|
||||||
|
* private int nodeDepth = 0;
|
||||||
|
* private int innerHashLength = 0;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever this buffer overflows, it will be processed in the compress()
|
||||||
|
* function. For performance issues, long messages will not use this buffer.
|
||||||
|
*/
|
||||||
|
private byte[] buffer = null;
|
||||||
|
/** Position of last inserted byte **/
|
||||||
|
private int bufferPos = 0;// a value from 0 up to BLOCK_LENGTH_BYTES
|
||||||
|
|
||||||
|
/** Internal state, in the BLAKE2 paper it is called v **/
|
||||||
|
private int[] internalState = new int[16];
|
||||||
|
/** State vector, in the BLAKE2 paper it is called h **/
|
||||||
|
private int[] chainValue = null;
|
||||||
|
|
||||||
|
// counter (counts bytes): Length up to 2^64 are supported
|
||||||
|
/** holds least significant bits of counter **/
|
||||||
|
private int t0 = 0;
|
||||||
|
/** holds most significant bits of counter **/
|
||||||
|
private int t1 = 0;
|
||||||
|
/** finalization flag, for last block: ~0 **/
|
||||||
|
private int f0 = 0;
|
||||||
|
|
||||||
|
// For Tree Hashing Mode, not used here:
|
||||||
|
// private long f1 = 0L; // finalization flag, for last node: ~0L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BLAKE2s-256 for hashing.
|
||||||
|
*/
|
||||||
|
public Blake2sDigest() {
|
||||||
|
this(256);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Blake2sDigest(Blake2sDigest digest) {
|
||||||
|
this.bufferPos = digest.bufferPos;
|
||||||
|
this.buffer = Arrays.clone(digest.buffer);
|
||||||
|
this.keyLength = digest.keyLength;
|
||||||
|
this.key = Arrays.clone(digest.key);
|
||||||
|
this.digestLength = digest.digestLength;
|
||||||
|
this.chainValue = Arrays.clone(digest.chainValue);
|
||||||
|
this.personalization = Arrays.clone(digest.personalization);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BLAKE2s for hashing.
|
||||||
|
*
|
||||||
|
* @param digestBits the desired digest length in bits. Must be one of
|
||||||
|
* [128, 160, 224, 256].
|
||||||
|
*/
|
||||||
|
public Blake2sDigest(int digestBits) {
|
||||||
|
if (digestBits != 128 && digestBits != 160 &&
|
||||||
|
digestBits != 224 && digestBits != 256) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"BLAKE2s digest restricted to one of [128, 160, 224, 256]");
|
||||||
|
}
|
||||||
|
buffer = new byte[BLOCK_LENGTH_BYTES];
|
||||||
|
keyLength = 0;
|
||||||
|
digestLength = digestBits / 8;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BLAKE2s for authentication ("Prefix-MAC mode").
|
||||||
|
* <p/>
|
||||||
|
* After calling the doFinal() method, the key will remain to be used for
|
||||||
|
* further computations of this instance. The key can be overwritten using
|
||||||
|
* the clearKey() method.
|
||||||
|
*
|
||||||
|
* @param key a key up to 32 bytes or null
|
||||||
|
*/
|
||||||
|
public Blake2sDigest(byte[] key) {
|
||||||
|
buffer = new byte[BLOCK_LENGTH_BYTES];
|
||||||
|
if (key != null) {
|
||||||
|
if (key.length > 32) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Keys > 32 are not supported");
|
||||||
|
}
|
||||||
|
this.key = new byte[key.length];
|
||||||
|
System.arraycopy(key, 0, this.key, 0, key.length);
|
||||||
|
|
||||||
|
keyLength = key.length;
|
||||||
|
System.arraycopy(key, 0, buffer, 0, key.length);
|
||||||
|
bufferPos = BLOCK_LENGTH_BYTES; // zero padding
|
||||||
|
}
|
||||||
|
digestLength = 32;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BLAKE2s with key, required digest length, salt and personalization.
|
||||||
|
* <p/>
|
||||||
|
* After calling the doFinal() method, the key, the salt and the personal
|
||||||
|
* string will remain and might be used for further computations with this
|
||||||
|
* instance. The key can be overwritten using the clearKey() method, the
|
||||||
|
* salt (pepper) can be overwritten using the clearSalt() method.
|
||||||
|
*
|
||||||
|
* @param key a key up to 32 bytes or null
|
||||||
|
* @param digestBytes from 1 up to 32 bytes
|
||||||
|
* @param salt 8 bytes or null
|
||||||
|
* @param personalization 8 bytes or null
|
||||||
|
*/
|
||||||
|
public Blake2sDigest(byte[] key, int digestBytes, byte[] salt,
|
||||||
|
byte[] personalization) {
|
||||||
|
buffer = new byte[BLOCK_LENGTH_BYTES];
|
||||||
|
if (digestBytes < 1 || digestBytes > 32) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid digest length (required: 1 - 32)");
|
||||||
|
}
|
||||||
|
digestLength = digestBytes;
|
||||||
|
if (salt != null) {
|
||||||
|
if (salt.length != 8) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Salt length must be exactly 8 bytes");
|
||||||
|
}
|
||||||
|
this.salt = new byte[8];
|
||||||
|
System.arraycopy(salt, 0, this.salt, 0, salt.length);
|
||||||
|
}
|
||||||
|
if (personalization != null) {
|
||||||
|
if (personalization.length != 8) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Personalization length must be exactly 8 bytes");
|
||||||
|
}
|
||||||
|
this.personalization = new byte[8];
|
||||||
|
System.arraycopy(personalization, 0, this.personalization, 0,
|
||||||
|
personalization.length);
|
||||||
|
}
|
||||||
|
if (key != null) {
|
||||||
|
if (key.length > 32) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Keys > 32 bytes are not supported");
|
||||||
|
}
|
||||||
|
this.key = new byte[key.length];
|
||||||
|
System.arraycopy(key, 0, this.key, 0, key.length);
|
||||||
|
|
||||||
|
keyLength = key.length;
|
||||||
|
System.arraycopy(key, 0, buffer, 0, key.length);
|
||||||
|
bufferPos = BLOCK_LENGTH_BYTES; // zero padding
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize chainValue
|
||||||
|
private void init() {
|
||||||
|
if (chainValue == null) {
|
||||||
|
chainValue = new int[8];
|
||||||
|
|
||||||
|
chainValue[0] = blake2s_IV[0]
|
||||||
|
^ (digestLength | (keyLength << 8) | 0x1010000);
|
||||||
|
// 0x1010000 = ((fanout << 16) | (depth << 24));
|
||||||
|
// with fanout = 1; depth = 0;
|
||||||
|
chainValue[1] = blake2s_IV[1];// ^ leafLength; with leafLength = 0;
|
||||||
|
chainValue[2] = blake2s_IV[2];// ^ nodeOffset; with nodeOffset = 0;
|
||||||
|
chainValue[3] = blake2s_IV[3];// ^ ( (nodeOffset << 32) |
|
||||||
|
// (nodeDepth << 16) | (innerHashLength << 24) );
|
||||||
|
// with nodeDepth = 0; innerHashLength = 0;
|
||||||
|
|
||||||
|
chainValue[4] = blake2s_IV[4];
|
||||||
|
chainValue[5] = blake2s_IV[5];
|
||||||
|
if (salt != null) {
|
||||||
|
chainValue[4] ^= (bytes2int(salt, 0));
|
||||||
|
chainValue[5] ^= (bytes2int(salt, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
chainValue[6] = blake2s_IV[6];
|
||||||
|
chainValue[7] = blake2s_IV[7];
|
||||||
|
if (personalization != null) {
|
||||||
|
chainValue[6] ^= (bytes2int(personalization, 0));
|
||||||
|
chainValue[7] ^= (bytes2int(personalization, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeInternalState() {
|
||||||
|
// initialize v:
|
||||||
|
System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
|
||||||
|
System.arraycopy(blake2s_IV, 0, internalState, chainValue.length, 4);
|
||||||
|
internalState[12] = t0 ^ blake2s_IV[4];
|
||||||
|
internalState[13] = t1 ^ blake2s_IV[5];
|
||||||
|
internalState[14] = f0 ^ blake2s_IV[6];
|
||||||
|
internalState[15] = blake2s_IV[7];// ^ f1 with f1 = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the message digest with a single byte.
|
||||||
|
*
|
||||||
|
* @param b the input byte to be entered.
|
||||||
|
*/
|
||||||
|
public void update(byte b) {
|
||||||
|
int remainingLength; // left bytes of buffer
|
||||||
|
|
||||||
|
// process the buffer if full else add to buffer:
|
||||||
|
remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
|
||||||
|
if (remainingLength == 0) { // full buffer
|
||||||
|
t0 += BLOCK_LENGTH_BYTES;
|
||||||
|
if (t0 == 0) { // if message > 2^32
|
||||||
|
t1++;
|
||||||
|
}
|
||||||
|
compress(buffer, 0);
|
||||||
|
Arrays.fill(buffer, (byte)0);// clear buffer
|
||||||
|
buffer[0] = b;
|
||||||
|
bufferPos = 1;
|
||||||
|
} else {
|
||||||
|
buffer[bufferPos] = b;
|
||||||
|
bufferPos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the message digest with a block of bytes.
|
||||||
|
*
|
||||||
|
* @param message the byte array containing the data.
|
||||||
|
* @param offset the offset into the byte array where the data starts.
|
||||||
|
* @param len the length of the data.
|
||||||
|
*/
|
||||||
|
public void update(byte[] message, int offset, int len) {
|
||||||
|
if (message == null || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int remainingLength = 0; // left bytes of buffer
|
||||||
|
|
||||||
|
if (bufferPos != 0) { // commenced, incomplete buffer
|
||||||
|
|
||||||
|
// complete the buffer:
|
||||||
|
remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
|
||||||
|
if (remainingLength < len) { // full buffer + at least 1 byte
|
||||||
|
System.arraycopy(message, offset, buffer, bufferPos,
|
||||||
|
remainingLength);
|
||||||
|
t0 += BLOCK_LENGTH_BYTES;
|
||||||
|
if (t0 == 0) { // if message > 2^32
|
||||||
|
t1++;
|
||||||
|
}
|
||||||
|
compress(buffer, 0);
|
||||||
|
bufferPos = 0;
|
||||||
|
Arrays.fill(buffer, (byte) 0);// clear buffer
|
||||||
|
} else {
|
||||||
|
System.arraycopy(message, offset, buffer, bufferPos, len);
|
||||||
|
bufferPos += len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process blocks except last block (also if last block is full)
|
||||||
|
int messagePos;
|
||||||
|
int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES;
|
||||||
|
for (messagePos = offset + remainingLength;
|
||||||
|
messagePos < blockWiseLastPos;
|
||||||
|
messagePos += BLOCK_LENGTH_BYTES) { // block wise 64 bytes
|
||||||
|
// without buffer:
|
||||||
|
t0 += BLOCK_LENGTH_BYTES;
|
||||||
|
if (t0 == 0) {
|
||||||
|
t1++;
|
||||||
|
}
|
||||||
|
compress(message, messagePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the buffer with left bytes, this might be a full block
|
||||||
|
System.arraycopy(message, messagePos, buffer, 0, offset + len
|
||||||
|
- messagePos);
|
||||||
|
bufferPos += offset + len - messagePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the digest, producing the final digest value. The doFinal() call
|
||||||
|
* leaves the digest reset. Key, salt and personal string remain.
|
||||||
|
*
|
||||||
|
* @param out the array the digest is to be copied into.
|
||||||
|
* @param outOffset the offset into the out array the digest is to start at.
|
||||||
|
*/
|
||||||
|
public int doFinal(byte[] out, int outOffset) {
|
||||||
|
f0 = 0xFFFFFFFF;
|
||||||
|
t0 += bufferPos;
|
||||||
|
// bufferPos may be < 64, so (t0 == 0) does not work
|
||||||
|
// for 2^32 < message length > 2^32 - 63
|
||||||
|
if ((t0 < 0) && (bufferPos > -t0)) {
|
||||||
|
t1++;
|
||||||
|
}
|
||||||
|
compress(buffer, 0);
|
||||||
|
Arrays.fill(buffer, (byte) 0);// Holds eventually the key if input is null
|
||||||
|
Arrays.fill(internalState, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < chainValue.length && (i * 4 < digestLength); i++) {
|
||||||
|
byte[] bytes = int2bytes(chainValue[i]);
|
||||||
|
|
||||||
|
if (i * 4 < digestLength - 4) {
|
||||||
|
System.arraycopy(bytes, 0, out, outOffset + i * 4, 4);
|
||||||
|
} else {
|
||||||
|
System.arraycopy(bytes, 0, out, outOffset + i * 4,
|
||||||
|
digestLength - (i * 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.fill(chainValue, 0);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
return digestLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the digest back to its initial state. The key, the salt and the
|
||||||
|
* personal string will remain for further computations.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
bufferPos = 0;
|
||||||
|
f0 = 0;
|
||||||
|
t0 = 0;
|
||||||
|
t1 = 0;
|
||||||
|
chainValue = null;
|
||||||
|
if (key != null) {
|
||||||
|
Arrays.fill(buffer, (byte) 0);
|
||||||
|
System.arraycopy(key, 0, buffer, 0, key.length);
|
||||||
|
bufferPos = BLOCK_LENGTH_BYTES; // zero padding
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compress(byte[] message, int messagePos) {
|
||||||
|
initializeInternalState();
|
||||||
|
|
||||||
|
int[] m = new int[16];
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
m[j] = bytes2int(message, messagePos + j * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int round = 0; round < ROUNDS; round++) {
|
||||||
|
|
||||||
|
// G apply to columns of internalState:m[blake2s_sigma[round][2 *
|
||||||
|
// blockPos]] /+1
|
||||||
|
G(m[blake2s_sigma[round][0]], m[blake2s_sigma[round][1]], 0, 4, 8,
|
||||||
|
12);
|
||||||
|
G(m[blake2s_sigma[round][2]], m[blake2s_sigma[round][3]], 1, 5, 9,
|
||||||
|
13);
|
||||||
|
G(m[blake2s_sigma[round][4]], m[blake2s_sigma[round][5]], 2, 6, 10,
|
||||||
|
14);
|
||||||
|
G(m[blake2s_sigma[round][6]], m[blake2s_sigma[round][7]], 3, 7, 11,
|
||||||
|
15);
|
||||||
|
// G apply to diagonals of internalState:
|
||||||
|
G(m[blake2s_sigma[round][8]], m[blake2s_sigma[round][9]], 0, 5, 10,
|
||||||
|
15);
|
||||||
|
G(m[blake2s_sigma[round][10]], m[blake2s_sigma[round][11]], 1, 6,
|
||||||
|
11, 12);
|
||||||
|
G(m[blake2s_sigma[round][12]], m[blake2s_sigma[round][13]], 2, 7,
|
||||||
|
8, 13);
|
||||||
|
G(m[blake2s_sigma[round][14]], m[blake2s_sigma[round][15]], 3, 4,
|
||||||
|
9, 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update chain values:
|
||||||
|
for (int offset = 0; offset < chainValue.length; offset++) {
|
||||||
|
chainValue[offset] = chainValue[offset] ^ internalState[offset]
|
||||||
|
^ internalState[offset + 8];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void G(int m1, int m2, int posA, int posB, int posC, int posD) {
|
||||||
|
internalState[posA] = internalState[posA] + internalState[posB] + m1;
|
||||||
|
internalState[posD] = rotr32(internalState[posD] ^ internalState[posA],
|
||||||
|
16);
|
||||||
|
internalState[posC] = internalState[posC] + internalState[posD];
|
||||||
|
internalState[posB] = rotr32(internalState[posB] ^ internalState[posC],
|
||||||
|
12);
|
||||||
|
internalState[posA] = internalState[posA] + internalState[posB] + m2;
|
||||||
|
internalState[posD] = rotr32(internalState[posD] ^ internalState[posA],
|
||||||
|
8);
|
||||||
|
internalState[posC] = internalState[posC] + internalState[posD];
|
||||||
|
internalState[posB] = rotr32(internalState[posB] ^ internalState[posC],
|
||||||
|
7);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int rotr32(int x, int rot) {
|
||||||
|
return x >>> rot | (x << (32 - rot));
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert one int value in byte array
|
||||||
|
// little-endian byte order!
|
||||||
|
private byte[] int2bytes(int intValue) {
|
||||||
|
return new byte[] {
|
||||||
|
(byte) intValue, (byte) (intValue >> 8),
|
||||||
|
(byte) (intValue >> 16), (byte) (intValue >> 24)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// little-endian byte order!
|
||||||
|
private int bytes2int(byte[] byteArray, int offset) {
|
||||||
|
return (((int) byteArray[offset] & 0xFF)
|
||||||
|
| (((int) byteArray[offset + 1] & 0xFF) << 8)
|
||||||
|
| (((int) byteArray[offset + 2] & 0xFF) << 16)
|
||||||
|
| (((int) byteArray[offset + 3] & 0xFF) << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the algorithm name.
|
||||||
|
*
|
||||||
|
* @return the algorithm name
|
||||||
|
*/
|
||||||
|
public String getAlgorithmName() {
|
||||||
|
return "BLAKE2s";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size in bytes of the digest produced by this message digest.
|
||||||
|
*
|
||||||
|
* @return the size in bytes of the digest produced by this message digest.
|
||||||
|
*/
|
||||||
|
public int getDigestSize() {
|
||||||
|
return digestLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size in bytes of the internal buffer the digest applies its
|
||||||
|
* compression function to.
|
||||||
|
*
|
||||||
|
* @return byte length of the digest's internal buffer.
|
||||||
|
*/
|
||||||
|
public int getByteLength() {
|
||||||
|
return BLOCK_LENGTH_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite the key if it is no longer used (zeroization).
|
||||||
|
*/
|
||||||
|
public void clearKey() {
|
||||||
|
if (key != null) {
|
||||||
|
Arrays.fill(key, (byte) 0);
|
||||||
|
Arrays.fill(buffer, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite the salt (pepper) if it is secret and no longer used
|
||||||
|
* (zeroization).
|
||||||
|
*/
|
||||||
|
public void clearSalt() {
|
||||||
|
if (salt != null) {
|
||||||
|
Arrays.fill(salt, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,50 +10,61 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
|||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
|
import org.spongycastle.crypto.CipherParameters;
|
||||||
import org.spongycastle.crypto.CryptoException;
|
import org.spongycastle.crypto.CryptoException;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||||
import org.whispersystems.curve25519.Curve25519;
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||||
|
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
|
||||||
|
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||||
|
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(CryptoComponentImpl.class.getName());
|
Logger.getLogger(CryptoComponentImpl.class.getName());
|
||||||
|
|
||||||
|
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
||||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||||
|
private static final int ED_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||||
private static final int PBKDF_FORMAT_SCRYPT = 0;
|
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||||
|
private static final int PBKDF_SAMPLES = 30;
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final PasswordBasedKdf passwordBasedKdf;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
private final Curve25519 curve25519;
|
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||||
private final KeyPairGenerator signatureKeyPairGenerator;
|
|
||||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
|
private final KeyPairGenerator edKeyPairGenerator;
|
||||||
|
private final KeyParser edKeyParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider,
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||||
PasswordBasedKdf passwordBasedKdf) {
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
SecureRandom defaultSecureRandom = new SecureRandom();
|
SecureRandom defaultSecureRandom = new SecureRandom();
|
||||||
String name = defaultSecureRandom.getProvider().getName();
|
String name = defaultSecureRandom.getProvider().getName();
|
||||||
@@ -73,14 +84,20 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
secureRandom = new SecureRandom();
|
secureRandom = new SecureRandom();
|
||||||
this.passwordBasedKdf = passwordBasedKdf;
|
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||||
curve25519 = Curve25519.getInstance("java");
|
PARAMETERS, secureRandom);
|
||||||
signatureKeyPairGenerator = new KeyPairGenerator();
|
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
|
agreementKeyPairGenerator.init(params);
|
||||||
secureRandom);
|
signatureKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
agreementKeyParser = new Curve25519KeyParser();
|
signatureKeyPairGenerator.init(params);
|
||||||
signatureKeyParser = new EdKeyParser();
|
agreementKeyParser = new Sec1KeyParser(PARAMETERS,
|
||||||
|
AGREEMENT_KEY_PAIR_BITS);
|
||||||
|
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
||||||
|
SIGNATURE_KEY_PAIR_BITS);
|
||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
|
edKeyPairGenerator = new KeyPairGenerator();
|
||||||
|
edKeyPairGenerator.initialize(ED_KEY_PAIR_BITS, secureRandom);
|
||||||
|
edKeyParser = new EdKeyParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
@@ -123,29 +140,51 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// Package access for testing
|
// Package access for testing
|
||||||
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
if (!(priv instanceof Curve25519PrivateKey))
|
if (!(priv instanceof Sec1PrivateKey))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (!(pub instanceof Curve25519PublicKey))
|
if (!(pub instanceof Sec1PublicKey))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
ECPrivateKeyParameters ecPriv = ((Sec1PrivateKey) priv).getKey();
|
||||||
|
ECPublicKeyParameters ecPub = ((Sec1PublicKey) pub).getKey();
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
|
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
|
||||||
priv.getEncoded());
|
agreement.init(ecPriv);
|
||||||
// If the shared secret is all zeroes, the public key is invalid
|
byte[] secret = agreement.calculateAgreement(ecPub).toByteArray();
|
||||||
byte allZero = 0;
|
|
||||||
for (byte b : secret) allZero |= b;
|
|
||||||
if (allZero == 0) throw new GeneralSecurityException();
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Deriving shared secret took " + duration + " ms");
|
LOG.info("Deriving shared secret took " + duration + " ms");
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair generateEdKeyPair() {
|
||||||
|
java.security.KeyPair keyPair = edKeyPairGenerator.generateKeyPair();
|
||||||
|
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||||
|
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
||||||
|
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||||
|
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
||||||
|
return new KeyPair(publicKey, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyParser getEdKeyParser() {
|
||||||
|
return edKeyParser;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyPair generateAgreementKeyPair() {
|
public KeyPair generateAgreementKeyPair() {
|
||||||
Curve25519KeyPair keyPair = curve25519.generateKeyPair();
|
AsymmetricCipherKeyPair keyPair =
|
||||||
PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey());
|
agreementKeyPairGenerator.generateKeyPair();
|
||||||
PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey());
|
// Return a wrapper that uses the SEC 1 encoding
|
||||||
return new KeyPair(pub, priv);
|
ECPublicKeyParameters ecPublicKey =
|
||||||
|
(ECPublicKeyParameters) keyPair.getPublic();
|
||||||
|
PublicKey publicKey = new Sec1PublicKey(ecPublicKey
|
||||||
|
);
|
||||||
|
ECPrivateKeyParameters ecPrivateKey =
|
||||||
|
(ECPrivateKeyParameters) keyPair.getPrivate();
|
||||||
|
PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey,
|
||||||
|
AGREEMENT_KEY_PAIR_BITS);
|
||||||
|
return new KeyPair(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -155,12 +194,17 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyPair generateSignatureKeyPair() {
|
public KeyPair generateSignatureKeyPair() {
|
||||||
java.security.KeyPair keyPair =
|
AsymmetricCipherKeyPair keyPair =
|
||||||
signatureKeyPairGenerator.generateKeyPair();
|
signatureKeyPairGenerator.generateKeyPair();
|
||||||
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
// Return a wrapper that uses the SEC 1 encoding
|
||||||
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
ECPublicKeyParameters ecPublicKey =
|
||||||
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
(ECPublicKeyParameters) keyPair.getPublic();
|
||||||
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
PublicKey publicKey = new Sec1PublicKey(ecPublicKey
|
||||||
|
);
|
||||||
|
ECPrivateKeyParameters ecPrivateKey =
|
||||||
|
(ECPrivateKeyParameters) keyPair.getPrivate();
|
||||||
|
PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey,
|
||||||
|
SIGNATURE_KEY_PAIR_BITS);
|
||||||
return new KeyPair(publicKey, privateKey);
|
return new KeyPair(publicKey, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,20 +241,44 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
PrivateKey key = signatureKeyParser.parsePrivateKey(privateKey);
|
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label,
|
||||||
Signature sig = new EdSignature();
|
toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return sign(new EdSignature(), edKeyParser, label, toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] sign(Signature sig, KeyParser keyParser, String label,
|
||||||
|
byte[] toSign, byte[] privateKey) throws GeneralSecurityException {
|
||||||
|
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
||||||
sig.initSign(key);
|
sig.initSign(key);
|
||||||
updateSignature(sig, label, toSign);
|
updateSignature(sig, label, toSign);
|
||||||
return sig.sign();
|
return sig.sign();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verifySignature(byte[] signature, String label,
|
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
byte[] signed, byte[] publicKey) throws GeneralSecurityException {
|
byte[] signature) throws GeneralSecurityException {
|
||||||
PublicKey key = signatureKeyParser.parsePublicKey(publicKey);
|
return verify(new SignatureImpl(secureRandom), signatureKeyParser,
|
||||||
Signature sig = new EdSignature();
|
label, signedData, publicKey, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException {
|
||||||
|
return verify(new EdSignature(), edKeyParser, label, signedData,
|
||||||
|
publicKey, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verify(Signature sig, KeyParser keyParser, String label,
|
||||||
|
byte[] signedData, byte[] publicKey, byte[] signature)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
PublicKey key = keyParser.parsePublicKey(publicKey);
|
||||||
sig.initVerify(key);
|
sig.initVerify(key);
|
||||||
updateSignature(sig, label, signed);
|
updateSignature(sig, label, signedData);
|
||||||
return sig.verify(signature);
|
return sig.verify(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +297,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] hash(String label, byte[]... inputs) {
|
public byte[] hash(String label, byte[]... inputs) {
|
||||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
Digest digest = new Blake2bDigest(256);
|
Digest digest = new Blake2sDigest();
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
byte[] length = new byte[INT_32_BYTES];
|
||||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||||
digest.update(length, 0, length.length);
|
digest.update(length, 0, length.length);
|
||||||
@@ -247,7 +315,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
||||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
Digest mac = new Blake2bDigest(macKey.getBytes(), 32, null, null);
|
Digest mac = new Blake2sDigest(macKey.getBytes());
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
byte[] length = new byte[INT_32_BYTES];
|
||||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||||
mac.update(length, 0, length.length);
|
mac.update(length, 0, length.length);
|
||||||
@@ -262,17 +330,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean verifyMac(byte[] mac, String label, SecretKey macKey,
|
|
||||||
byte[]... inputs) {
|
|
||||||
byte[] expected = mac(label, macKey, inputs);
|
|
||||||
if (mac.length != expected.length) return false;
|
|
||||||
// Constant-time comparison
|
|
||||||
int cmp = 0;
|
|
||||||
for (int i = 0; i < mac.length; i++) cmp |= mac[i] ^ expected[i];
|
|
||||||
return cmp == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encryptWithPassword(byte[] input, String password) {
|
public byte[] encryptWithPassword(byte[] input, String password) {
|
||||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||||
@@ -281,33 +338,23 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
secureRandom.nextBytes(salt);
|
secureRandom.nextBytes(salt);
|
||||||
// Calibrate the KDF
|
// Calibrate the KDF
|
||||||
int cost = passwordBasedKdf.chooseCostParameter();
|
int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS);
|
||||||
// Derive the key from the password
|
// Derive the key from the password
|
||||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost);
|
SecretKey key = new SecretKey(pbkdf2(password, salt, iterations));
|
||||||
// Generate a random IV
|
// Generate a random IV
|
||||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||||
secureRandom.nextBytes(iv);
|
secureRandom.nextBytes(iv);
|
||||||
// The output contains the format version, salt, cost parameter, IV,
|
// The output contains the salt, iterations, IV, ciphertext and MAC
|
||||||
// ciphertext and MAC
|
int outputLen = salt.length + INT_32_BYTES + iv.length + input.length
|
||||||
int outputLen = 1 + salt.length + INT_32_BYTES + iv.length
|
+ macBytes;
|
||||||
+ input.length + macBytes;
|
|
||||||
byte[] output = new byte[outputLen];
|
byte[] output = new byte[outputLen];
|
||||||
int outputOff = 0;
|
System.arraycopy(salt, 0, output, 0, salt.length);
|
||||||
// Format version
|
ByteUtils.writeUint32(iterations, output, salt.length);
|
||||||
output[outputOff] = PBKDF_FORMAT_SCRYPT;
|
System.arraycopy(iv, 0, output, salt.length + INT_32_BYTES, iv.length);
|
||||||
outputOff++;
|
|
||||||
// Salt
|
|
||||||
System.arraycopy(salt, 0, output, outputOff, salt.length);
|
|
||||||
outputOff += salt.length;
|
|
||||||
// Cost parameter
|
|
||||||
ByteUtils.writeUint32(cost, output, outputOff);
|
|
||||||
outputOff += INT_32_BYTES;
|
|
||||||
// IV
|
|
||||||
System.arraycopy(iv, 0, output, outputOff, iv.length);
|
|
||||||
outputOff += iv.length;
|
|
||||||
// Initialise the cipher and encrypt the plaintext
|
// Initialise the cipher and encrypt the plaintext
|
||||||
try {
|
try {
|
||||||
cipher.init(true, key, iv);
|
cipher.init(true, key, iv);
|
||||||
|
int outputOff = salt.length + INT_32_BYTES + iv.length;
|
||||||
cipher.process(input, 0, input.length, output, outputOff);
|
cipher.process(input, 0, input.length, output, outputOff);
|
||||||
return output;
|
return output;
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
@@ -316,36 +363,22 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
|
||||||
public byte[] decryptWithPassword(byte[] input, String password) {
|
public byte[] decryptWithPassword(byte[] input, String password) {
|
||||||
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
|
||||||
int macBytes = cipher.getMacBytes();
|
int macBytes = cipher.getMacBytes();
|
||||||
// The input contains the format version, salt, cost parameter, IV,
|
// The input contains the salt, iterations, IV, ciphertext and MAC
|
||||||
// ciphertext and MAC
|
if (input.length < PBKDF_SALT_BYTES + INT_32_BYTES + STORAGE_IV_BYTES
|
||||||
if (input.length < 1 + PBKDF_SALT_BYTES + INT_32_BYTES
|
+ macBytes)
|
||||||
+ STORAGE_IV_BYTES + macBytes)
|
|
||||||
return null; // Invalid input
|
return null; // Invalid input
|
||||||
int inputOff = 0;
|
|
||||||
// Format version
|
|
||||||
byte formatVersion = input[inputOff];
|
|
||||||
inputOff++;
|
|
||||||
if (formatVersion != PBKDF_FORMAT_SCRYPT)
|
|
||||||
return null; // Unknown format
|
|
||||||
// Salt
|
|
||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
System.arraycopy(input, inputOff, salt, 0, salt.length);
|
System.arraycopy(input, 0, salt, 0, salt.length);
|
||||||
inputOff += salt.length;
|
long iterations = ByteUtils.readUint32(input, salt.length);
|
||||||
// Cost parameter
|
if (iterations < 0 || iterations > Integer.MAX_VALUE)
|
||||||
long cost = ByteUtils.readUint32(input, inputOff);
|
return null; // Invalid iteration count
|
||||||
inputOff += INT_32_BYTES;
|
|
||||||
if (cost < 2 || cost > Integer.MAX_VALUE)
|
|
||||||
return null; // Invalid cost parameter
|
|
||||||
// IV
|
|
||||||
byte[] iv = new byte[STORAGE_IV_BYTES];
|
byte[] iv = new byte[STORAGE_IV_BYTES];
|
||||||
System.arraycopy(input, inputOff, iv, 0, iv.length);
|
System.arraycopy(input, salt.length + INT_32_BYTES, iv, 0, iv.length);
|
||||||
inputOff += iv.length;
|
|
||||||
// Derive the key from the password
|
// Derive the key from the password
|
||||||
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
|
SecretKey key = new SecretKey(pbkdf2(password, salt, (int) iterations));
|
||||||
// Initialise the cipher
|
// Initialise the cipher
|
||||||
try {
|
try {
|
||||||
cipher.init(false, key, iv);
|
cipher.init(false, key, iv);
|
||||||
@@ -354,6 +387,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
// Try to decrypt the ciphertext (may be invalid)
|
// Try to decrypt the ciphertext (may be invalid)
|
||||||
try {
|
try {
|
||||||
|
int inputOff = salt.length + INT_32_BYTES + iv.length;
|
||||||
int inputLen = input.length - inputOff;
|
int inputLen = input.length - inputOff;
|
||||||
byte[] output = new byte[inputLen - macBytes];
|
byte[] output = new byte[inputLen - macBytes];
|
||||||
cipher.process(input, inputOff, inputLen, output, 0);
|
cipher.process(input, inputOff, inputLen, output, 0);
|
||||||
@@ -376,4 +410,64 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public String asciiArmour(byte[] b, int lineLength) {
|
public String asciiArmour(byte[] b, int lineLength) {
|
||||||
return AsciiArmour.wrap(b, lineLength);
|
return AsciiArmour.wrap(b, lineLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||||
|
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||||
|
byte[] utf8 = StringUtils.toUtf8(password);
|
||||||
|
Digest digest = new SHA256Digest();
|
||||||
|
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
|
||||||
|
gen.init(utf8, salt, iterations);
|
||||||
|
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||||
|
CipherParameters p = gen.generateDerivedParameters(keyLengthInBits);
|
||||||
|
return ((KeyParameter) p).getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package access for testing
|
||||||
|
int chooseIterationCount(int targetMillis) {
|
||||||
|
List<Long> quickSamples = new ArrayList<>(PBKDF_SAMPLES);
|
||||||
|
List<Long> slowSamples = new ArrayList<>(PBKDF_SAMPLES);
|
||||||
|
long iterationNanos = 0, initNanos = 0;
|
||||||
|
while (iterationNanos <= 0 || initNanos <= 0) {
|
||||||
|
// Sample the running time with one iteration and two iterations
|
||||||
|
for (int i = 0; i < PBKDF_SAMPLES; i++) {
|
||||||
|
quickSamples.add(sampleRunningTime(1));
|
||||||
|
slowSamples.add(sampleRunningTime(2));
|
||||||
|
}
|
||||||
|
// Calculate the iteration time and the initialisation time
|
||||||
|
long quickMedian = median(quickSamples);
|
||||||
|
long slowMedian = median(slowSamples);
|
||||||
|
iterationNanos = slowMedian - quickMedian;
|
||||||
|
initNanos = quickMedian - iterationNanos;
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Init: " + initNanos + ", iteration: "
|
||||||
|
+ iterationNanos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long targetNanos = targetMillis * 1000L * 1000L;
|
||||||
|
long iterations = (targetNanos - initNanos) / iterationNanos;
|
||||||
|
if (LOG.isLoggable(INFO)) LOG.info("Target iterations: " + iterations);
|
||||||
|
if (iterations < 1) return 1;
|
||||||
|
if (iterations > Integer.MAX_VALUE) return Integer.MAX_VALUE;
|
||||||
|
return (int) iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long sampleRunningTime(int iterations) {
|
||||||
|
byte[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||||
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
|
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||||
|
long start = System.nanoTime();
|
||||||
|
Digest digest = new SHA256Digest();
|
||||||
|
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
|
||||||
|
gen.init(password, salt, iterations);
|
||||||
|
gen.generateDerivedParameters(keyLengthInBits);
|
||||||
|
return System.nanoTime() - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long median(List<Long> list) {
|
||||||
|
int size = list.size();
|
||||||
|
if (size == 0) throw new IllegalArgumentException();
|
||||||
|
Collections.sort(list);
|
||||||
|
if (size % 2 == 1) return list.get(size / 2);
|
||||||
|
return list.get(size / 2 - 1) + list.get(size / 2) / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.TimeLoggingExecutor;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
|
||||||
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.RejectedExecutionHandler;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class CryptoExecutorModule {
|
|
||||||
|
|
||||||
public static class EagerSingletons {
|
|
||||||
@Inject
|
|
||||||
@CryptoExecutor
|
|
||||||
ExecutorService cryptoExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of executor threads.
|
|
||||||
* <p>
|
|
||||||
* The number of available processors can change during the lifetime of the
|
|
||||||
* JVM, so this is just a reasonable guess.
|
|
||||||
*/
|
|
||||||
private static final int MAX_EXECUTOR_THREADS =
|
|
||||||
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
|
||||||
|
|
||||||
private final ExecutorService cryptoExecutor;
|
|
||||||
|
|
||||||
public CryptoExecutorModule() {
|
|
||||||
// Use an unbounded queue
|
|
||||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
|
||||||
// Discard tasks that are submitted during shutdown
|
|
||||||
RejectedExecutionHandler policy =
|
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
|
||||||
// Create a limited # of threads and keep them in the pool for 60 secs
|
|
||||||
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
|
|
||||||
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
@CryptoExecutor
|
|
||||||
ExecutorService provideCryptoExecutorService(
|
|
||||||
LifecycleManager lifecycleManager) {
|
|
||||||
lifecycleManager.registerForShutdown(cryptoExecutor);
|
|
||||||
return cryptoExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@CryptoExecutor
|
|
||||||
Executor provideCryptoExecutor() {
|
|
||||||
return cryptoExecutor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,64 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.RejectedExecutionHandler;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class CryptoModule {
|
public class CryptoModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
@CryptoExecutor
|
||||||
|
ExecutorService cryptoExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of executor threads.
|
||||||
|
* <p>
|
||||||
|
* The number of available processors can change during the lifetime of the
|
||||||
|
* JVM, so this is just a reasonable guess.
|
||||||
|
*/
|
||||||
|
private static final int MAX_EXECUTOR_THREADS =
|
||||||
|
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||||
|
|
||||||
|
private final ExecutorService cryptoExecutor;
|
||||||
|
|
||||||
|
public CryptoModule() {
|
||||||
|
// Use an unbounded queue
|
||||||
|
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||||
|
// Discard tasks that are submitted during shutdown
|
||||||
|
RejectedExecutionHandler policy =
|
||||||
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
|
// Create a limited # of threads and keep them in the pool for 60 secs
|
||||||
|
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
|
||||||
|
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
AuthenticatedCipher provideAuthenticatedCipher() {
|
AuthenticatedCipher provideAuthenticatedCipher() {
|
||||||
return new XSalsa20Poly1305AuthenticatedCipher();
|
return new XSalsa20Poly1305AuthenticatedCipher();
|
||||||
@@ -27,9 +67,8 @@ public class CryptoModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
CryptoComponent provideCryptoComponent(
|
CryptoComponent provideCryptoComponent(
|
||||||
SecureRandomProvider secureRandomProvider,
|
SecureRandomProvider secureRandomProvider) {
|
||||||
ScryptKdf passwordBasedKdf) {
|
return new CryptoComponentImpl(secureRandomProvider);
|
||||||
return new CryptoComponentImpl(secureRandomProvider, passwordBasedKdf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -63,6 +102,21 @@ public class CryptoModule {
|
|||||||
return keyAgreementCrypto;
|
return keyAgreementCrypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@CryptoExecutor
|
||||||
|
ExecutorService getCryptoExecutorService(
|
||||||
|
LifecycleManager lifecycleManager) {
|
||||||
|
lifecycleManager.registerForShutdown(cryptoExecutor);
|
||||||
|
return cryptoExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@CryptoExecutor
|
||||||
|
Executor getCryptoExecutor() {
|
||||||
|
return cryptoExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
SecureRandom getSecureRandom(CryptoComponent crypto) {
|
SecureRandom getSecureRandom(CryptoComponent crypto) {
|
||||||
return crypto.getSecureRandom();
|
return crypto.getSecureRandom();
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class Curve25519KeyParser implements KeyParser {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
|
||||||
throws GeneralSecurityException {
|
|
||||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
|
||||||
return new Curve25519PublicKey(encodedKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
|
||||||
throws GeneralSecurityException {
|
|
||||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
|
||||||
return new Curve25519PrivateKey(clamp(encodedKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] clamp(byte[] b) {
|
|
||||||
byte[] clamped = new byte[32];
|
|
||||||
System.arraycopy(b, 0, clamped, 0, 32);
|
|
||||||
clamped[0] &= 248;
|
|
||||||
clamped[31] &= 127;
|
|
||||||
clamped[31] |= 64;
|
|
||||||
return clamped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class Curve25519PrivateKey extends Bytes implements PrivateKey {
|
|
||||||
|
|
||||||
Curve25519PrivateKey(byte[] bytes) {
|
|
||||||
super(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getEncoded() {
|
|
||||||
return getBytes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class Curve25519PublicKey extends Bytes implements PublicKey {
|
|
||||||
|
|
||||||
Curve25519PublicKey(byte[] bytes) {
|
|
||||||
super(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getEncoded() {
|
|
||||||
return getBytes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
|
||||||
|
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||||
|
import org.spongycastle.crypto.params.ECDomainParameters;
|
||||||
|
import org.spongycastle.math.ec.ECCurve;
|
||||||
|
import org.spongycastle.math.ec.ECMultiplier;
|
||||||
|
import org.spongycastle.math.ec.ECPoint;
|
||||||
|
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters for curve brainpoolp256r1 - see RFC 5639.
|
||||||
|
*/
|
||||||
|
class EllipticCurveConstants {
|
||||||
|
|
||||||
|
static final ECDomainParameters PARAMETERS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Start with the default implementation of the curve
|
||||||
|
X9ECParameters x9 = TeleTrusTNamedCurves.getByName("brainpoolp256r1");
|
||||||
|
// Use a constant-time multiplier
|
||||||
|
ECMultiplier monty = new MontgomeryLadderMultiplier();
|
||||||
|
ECCurve curve = x9.getCurve().configure().setMultiplier(monty).create();
|
||||||
|
BigInteger gX = x9.getG().getAffineXCoord().toBigInteger();
|
||||||
|
BigInteger gY = x9.getG().getAffineYCoord().toBigInteger();
|
||||||
|
ECPoint g = curve.createPoint(gX, gY);
|
||||||
|
// Convert to ECDomainParameters using the new multiplier
|
||||||
|
PARAMETERS = new ECDomainParameters(curve, g, x9.getN(), x9.getH());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
|
|
||||||
interface PasswordBasedKdf {
|
|
||||||
|
|
||||||
int chooseCostParameter();
|
|
||||||
|
|
||||||
SecretKey deriveKey(String password, byte[] salt, int cost);
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
import org.spongycastle.crypto.generators.SCrypt;
|
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
|
|
||||||
class ScryptKdf implements PasswordBasedKdf {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ScryptKdf.class.getName());
|
|
||||||
|
|
||||||
private static final int MIN_COST = 256; // Min parameter N
|
|
||||||
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
|
||||||
private static final int BLOCK_SIZE = 8; // Parameter r
|
|
||||||
private static final int PARALLELIZATION = 1; // Parameter p
|
|
||||||
private static final int TARGET_MS = 1000;
|
|
||||||
|
|
||||||
private final Clock clock;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ScryptKdf(Clock clock) {
|
|
||||||
this.clock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int chooseCostParameter() {
|
|
||||||
// Increase the cost from min to max while measuring performance
|
|
||||||
int cost = MIN_COST;
|
|
||||||
while (cost * 2 <= MAX_COST && measureDuration(cost) * 2 <= TARGET_MS)
|
|
||||||
cost *= 2;
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("KDF cost parameter " + cost);
|
|
||||||
return cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long measureDuration(int cost) {
|
|
||||||
byte[] password = new byte[16], salt = new byte[32];
|
|
||||||
long start = clock.currentTimeMillis();
|
|
||||||
SCrypt.generate(password, salt, cost, BLOCK_SIZE, PARALLELIZATION,
|
|
||||||
SecretKey.LENGTH);
|
|
||||||
return clock.currentTimeMillis() - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveKey(String password, byte[] salt, int cost) {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
byte[] passwordBytes = StringUtils.toUtf8(password);
|
|
||||||
SecretKey k = new SecretKey(SCrypt.generate(passwordBytes, salt, cost,
|
|
||||||
BLOCK_SIZE, PARALLELIZATION, SecretKey.LENGTH));
|
|
||||||
long duration = System.currentTimeMillis() - start;
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Deriving key from password took " + duration + " ms");
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.spongycastle.crypto.Digest;
|
||||||
|
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||||
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
|
import org.spongycastle.crypto.params.ParametersWithRandom;
|
||||||
|
import org.spongycastle.crypto.signers.DSADigestSigner;
|
||||||
|
import org.spongycastle.crypto.signers.DSAKCalculator;
|
||||||
|
import org.spongycastle.crypto.signers.ECDSASigner;
|
||||||
|
import org.spongycastle.crypto.signers.HMacDSAKCalculator;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class SignatureImpl implements Signature {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(SignatureImpl.class.getName());
|
||||||
|
|
||||||
|
private final SecureRandom secureRandom;
|
||||||
|
private final DSADigestSigner signer;
|
||||||
|
|
||||||
|
SignatureImpl(SecureRandom secureRandom) {
|
||||||
|
this.secureRandom = secureRandom;
|
||||||
|
Digest digest = new Blake2sDigest();
|
||||||
|
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
|
||||||
|
signer = new DSADigestSigner(new ECDSASigner(calculator), digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
||||||
|
if (!(k instanceof Sec1PrivateKey))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
ECPrivateKeyParameters priv = ((Sec1PrivateKey) k).getKey();
|
||||||
|
signer.init(true, new ParametersWithRandom(priv, secureRandom));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
||||||
|
if (!(k instanceof Sec1PublicKey))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
ECPublicKeyParameters pub = ((Sec1PublicKey) k).getKey();
|
||||||
|
signer.init(false, pub);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte b) {
|
||||||
|
signer.update(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] b) {
|
||||||
|
update(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] b, int off, int len) {
|
||||||
|
signer.update(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] sign() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
byte[] signature = signer.generateSignature();
|
||||||
|
long duration = System.currentTimeMillis() - now;
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Generating signature took " + duration + " ms");
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(byte[] signature) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
boolean valid = signer.verifySignature(signature);
|
||||||
|
long duration = System.currentTimeMillis() - now;
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Verifying signature took " + duration + " ms");
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.transport.TransportKeys;
|
|||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
import org.spongycastle.crypto.digests.Blake2bDigest;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -36,8 +35,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportKeys deriveTransportKeys(TransportId t,
|
public TransportKeys deriveTransportKeys(TransportId t,
|
||||||
SecretKey master, long rotationPeriod, boolean alice,
|
SecretKey master, long rotationPeriod, boolean alice) {
|
||||||
boolean active) {
|
|
||||||
// Keys for the previous period are derived from the master secret
|
// Keys for the previous period are derived from the master secret
|
||||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
||||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
||||||
@@ -58,7 +56,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||||
rotationPeriod + 1);
|
rotationPeriod + 1);
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||||
rotationPeriod, active);
|
rotationPeriod);
|
||||||
// Collect and return the keys
|
// Collect and return the keys
|
||||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
@@ -72,7 +70,6 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
long startPeriod = outCurr.getRotationPeriod();
|
long startPeriod = outCurr.getRotationPeriod();
|
||||||
boolean active = outCurr.isActive();
|
|
||||||
// Rotate the keys
|
// Rotate the keys
|
||||||
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
|
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
|
||||||
inPrev = inCurr;
|
inPrev = inCurr;
|
||||||
@@ -82,7 +79,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
||||||
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
||||||
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
||||||
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
|
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p);
|
||||||
}
|
}
|
||||||
// Collect and return the keys
|
// Collect and return the keys
|
||||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||||
@@ -118,7 +115,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
// Initialise the PRF
|
// Initialise the PRF
|
||||||
Digest prf = new Blake2bDigest(tagKey.getBytes(), 32, null, null);
|
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||||
// The output of the PRF must be long enough to use as a tag
|
// The output of the PRF must be long enough to use as a tag
|
||||||
int macLength = prf.getDigestSize();
|
int macLength = prf.getDigestSize();
|
||||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package org.briarproject.bramble.db;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
|
||||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
@@ -21,8 +18,6 @@ import org.briarproject.bramble.api.sync.Message;
|
|||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.ValidationManager.State;
|
import org.briarproject.bramble.api.sync.ValidationManager.State;
|
||||||
import org.briarproject.bramble.api.transport.KeySet;
|
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -42,13 +37,8 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the database and returns true if the database already existed.
|
* 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents new transactions from starting, waits for all current
|
* Prevents new transactions from starting, waits for all current
|
||||||
@@ -99,25 +89,31 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a message.
|
* Stores a message.
|
||||||
*
|
|
||||||
* @param sender the contact from whom the message was received, or null
|
|
||||||
* if the message was created locally.
|
|
||||||
*/
|
*/
|
||||||
void addMessage(T txn, Message m, State state, boolean shared,
|
void addMessage(T txn, Message m, State state, boolean shared)
|
||||||
@Nullable ContactId sender) throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a dependency between two messages, where the dependent message is
|
* Adds a dependency between two messages in the given group.
|
||||||
* in the given state.
|
|
||||||
*/
|
*/
|
||||||
void addMessageDependency(T txn, Message dependent, MessageId dependency,
|
void addMessageDependency(T txn, GroupId g, MessageId dependent,
|
||||||
State dependentState) throws DbException;
|
MessageId dependency) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records that a message has been offered by the given contact.
|
* Records that a message has been offered by the given contact.
|
||||||
*/
|
*/
|
||||||
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
|
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the status of the given message with respect to the given
|
||||||
|
* contact.
|
||||||
|
*
|
||||||
|
* @param ack whether the message needs to be acknowledged.
|
||||||
|
* @param seen whether the contact has seen the message.
|
||||||
|
*/
|
||||||
|
void addStatus(T txn, ContactId c, MessageId m, boolean ack, boolean seen)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a transport.
|
* Stores a transport.
|
||||||
*/
|
*/
|
||||||
@@ -125,16 +121,9 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given transport keys, optionally binding them to the given
|
* Stores transport keys for a newly added contact.
|
||||||
* contact, and returns a key set ID.
|
|
||||||
*/
|
*/
|
||||||
KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k)
|
void addTransportKeys(T txn, ContactId c, TransportKeys k)
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the given keys for the given transport to the given contact.
|
|
||||||
*/
|
|
||||||
void bindTransportKeys(T txn, ContactId c, TransportId t, KeySetId k)
|
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,8 +255,7 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<Group> getGroups(T txn, ClientId c, int majorVersion)
|
Collection<Group> getGroups(T txn, ClientId c) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the given group's visibility to the given contact, or
|
* Returns the given group's visibility to the given contact, or
|
||||||
@@ -284,7 +272,7 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
|
Collection<ContactId> getGroupVisibility(T txn, GroupId g)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -303,8 +291,10 @@ interface Database<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs and states of all dependencies of the given message.
|
* Returns the IDs and states of all dependencies of the given message.
|
||||||
* For missing dependencies and dependencies in other groups, the state
|
* Missing dependencies have the state {@link State UNKNOWN}.
|
||||||
* {@link State UNKNOWN} is returned.
|
* Dependencies in other groups have the state {@link 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/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -312,9 +302,9 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs and states of all dependents of the given message.
|
* Returns all IDs and states of all dependents of the given message.
|
||||||
* Dependents in other groups are not returned. If the given message is
|
* Messages in other groups that declare a dependency on the given message
|
||||||
* missing, no dependents are returned.
|
* will be returned even though such dependencies are invalid.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -322,16 +312,16 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of all delivered messages in the given group.
|
* Returns the IDs of all messages in the given group.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
|
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any delivered messages in the given group with
|
* Returns the IDs of any messages in the given group with metadata
|
||||||
* metadata that matches all entries in the given query. If the query is
|
* matching all entries in the given query. If the query is empty, the IDs
|
||||||
* empty, the IDs of all delivered messages are returned.
|
* of all messages are returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -347,9 +337,9 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata for any delivered messages in the given group with
|
* Returns the metadata for any messages in the given group with metadata
|
||||||
* metadata that matches all entries in the given query. If the query is
|
* matching all entries in the given query. If the query is empty, the
|
||||||
* empty, the metadata for all delivered messages is returned.
|
* metadata for all messages is returned.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -357,8 +347,7 @@ interface Database<T> {
|
|||||||
Metadata query) throws DbException;
|
Metadata query) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata for the given delivered or pending message.
|
* Returns the metadata for the given delivered message.
|
||||||
* This is only meant to be used by the ValidationManager.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -366,7 +355,7 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata for the given delivered message.
|
* Returns the metadata for the given message.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -380,8 +369,8 @@ interface Database<T> {
|
|||||||
State getMessageState(T txn, MessageId m) throws DbException;
|
State getMessageState(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of all delivered messages in the given group with
|
* Returns the status of all messages in the given group with respect
|
||||||
* respect to the given contact.
|
* to the given contact.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@@ -389,13 +378,11 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of the given delivered message with respect to the
|
* Returns the status of the given message with respect to the given
|
||||||
* given contact, or null if the message's group is invisible to the
|
|
||||||
* contact.
|
* contact.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
|
||||||
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
|
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
@@ -436,37 +423,31 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that need to be validated.
|
* Returns the IDs of any messages that need to be validated by the given
|
||||||
|
* client.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessagesToValidate(T txn) throws DbException;
|
Collection<MessageId> getMessagesToValidate(T txn, ClientId c)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that are pending delivery due to
|
* Returns the IDs of any messages that are still pending due to
|
||||||
* dependencies on other messages.
|
* dependencies to other messages for the given client.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getPendingMessages(T txn) throws DbException;
|
Collection<MessageId> getPendingMessages(T txn, ClientId c)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that have a shared dependent but have
|
* Returns the IDs of any messages from the given client
|
||||||
* not yet been shared themselves.
|
* that have a shared dependent, but are still not shared themselves.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessagesToShare(T txn) throws DbException;
|
Collection<MessageId> getMessagesToShare(T txn, ClientId c)
|
||||||
|
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(T txn, ContactId c) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the message with the given ID, in serialised form, or null if
|
* Returns the message with the given ID, in serialised form, or null if
|
||||||
@@ -499,14 +480,15 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<KeySet> getTransportKeys(T txn, TransportId t)
|
Map<ContactId, TransportKeys> getTransportKeys(T txn, TransportId t)
|
||||||
throws DbException;
|
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(T txn, TransportId t, KeySetId k)
|
void incrementStreamCounter(T txn, ContactId c, TransportId t,
|
||||||
throws DbException;
|
long rotationPeriod) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given messages as not needing to be acknowledged to the
|
* Marks the given messages as not needing to be acknowledged to the
|
||||||
@@ -584,6 +566,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
void removeMessage(T txn, MessageId m) throws DbException;
|
void removeMessage(T txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an offered message that was offered by the given contact, or
|
||||||
|
* returns false if there is no such message.
|
||||||
|
*/
|
||||||
|
boolean removeOfferedMessage(T txn, ContactId c, MessageId m)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given offered messages that were offered by the given
|
* Removes the given offered messages that were offered by the given
|
||||||
* contact.
|
* contact.
|
||||||
@@ -591,17 +580,17 @@ interface Database<T> {
|
|||||||
void removeOfferedMessages(T txn, ContactId c,
|
void removeOfferedMessages(T txn, ContactId c,
|
||||||
Collection<MessageId> requested) throws DbException;
|
Collection<MessageId> requested) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the status of the given message with respect to the given
|
||||||
|
* contact.
|
||||||
|
*/
|
||||||
|
void removeStatus(T txn, ContactId c, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a transport (and all associated state) from the database.
|
* Removes a transport (and all associated state) from the database.
|
||||||
*/
|
*/
|
||||||
void removeTransport(T txn, TransportId t) throws DbException;
|
void removeTransport(T txn, TransportId t) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given transport keys from the database.
|
|
||||||
*/
|
|
||||||
void removeTransportKeys(T txn, TransportId t, KeySetId k)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the transmission count and expiry time of the given message with
|
* Resets the transmission count and expiry time of the given message with
|
||||||
* respect to the given contact.
|
* respect to the given contact.
|
||||||
@@ -637,18 +626,12 @@ interface Database<T> {
|
|||||||
void setMessageState(T txn, MessageId m, State state) throws DbException;
|
void setMessageState(T txn, MessageId m, State state) 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.
|
* given rotation period.
|
||||||
*/
|
*/
|
||||||
void setReorderingWindow(T txn, KeySetId k, TransportId t,
|
void setReorderingWindow(T txn, ContactId c, TransportId t,
|
||||||
long rotationPeriod, long base, byte[] bitmap) throws DbException;
|
long rotationPeriod, long base, byte[] bitmap) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the given transport keys as usable for outgoing streams.
|
|
||||||
*/
|
|
||||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the transmission count and expiry time of the given message
|
* Updates the transmission count and expiry time of the given message
|
||||||
* with respect to the given contact, using the latency of the transport
|
* with respect to the given contact, using the latency of the transport
|
||||||
@@ -658,7 +641,8 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the given transport keys following key rotation.
|
* Stores the given transport keys, deleting any keys they have replaced.
|
||||||
*/
|
*/
|
||||||
void updateTransportKeys(T txn, KeySet ks) throws DbException;
|
void updateTransportKeys(T txn, Map<ContactId, TransportKeys> keys)
|
||||||
|
throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.db.ContactExistsException;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
||||||
@@ -51,15 +50,15 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
|
|||||||
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||||
import org.briarproject.bramble.api.transport.KeySet;
|
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -101,9 +100,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open(@Nullable MigrationListener listener)
|
public boolean open() throws DbException {
|
||||||
throws DbException {
|
boolean reopened = db.open();
|
||||||
boolean reopened = db.open(listener);
|
|
||||||
shutdown.addShutdownHook(() -> {
|
shutdown.addShutdownHook(() -> {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
@@ -215,7 +213,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
if (!db.containsGroup(txn, m.getGroupId()))
|
if (!db.containsGroup(txn, m.getGroupId()))
|
||||||
throw new NoSuchGroupException();
|
throw new NoSuchGroupException();
|
||||||
if (!db.containsMessage(txn, m.getId())) {
|
if (!db.containsMessage(txn, m.getId())) {
|
||||||
db.addMessage(txn, m, DELIVERED, shared, null);
|
addMessage(txn, m, DELIVERED, shared, null);
|
||||||
transaction.attach(new MessageAddedEvent(m, null));
|
transaction.attach(new MessageAddedEvent(m, null));
|
||||||
transaction.attach(new MessageStateChangedEvent(m.getId(), true,
|
transaction.attach(new MessageStateChangedEvent(m.getId(), true,
|
||||||
DELIVERED));
|
DELIVERED));
|
||||||
@@ -224,6 +222,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.mergeMessageMetadata(txn, m.getId(), meta);
|
db.mergeMessageMetadata(txn, m.getId(), meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addMessage(T txn, Message m, State state, boolean shared,
|
||||||
|
@Nullable ContactId sender) throws DbException {
|
||||||
|
db.addMessage(txn, m, state, shared);
|
||||||
|
for (ContactId c : db.getGroupVisibility(txn, m.getGroupId())) {
|
||||||
|
boolean offered = db.removeOfferedMessage(txn, c, m.getId());
|
||||||
|
boolean seen = offered || (sender != null && c.equals(sender));
|
||||||
|
db.addStatus(txn, c, m.getId(), seen, seen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTransport(Transaction transaction, TransportId t,
|
public void addTransport(Transaction transaction, TransportId t,
|
||||||
int maxLatency) throws DbException {
|
int maxLatency) throws DbException {
|
||||||
@@ -234,27 +242,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeySetId addTransportKeys(Transaction transaction,
|
public void addTransportKeys(Transaction transaction, ContactId c,
|
||||||
@Nullable ContactId c, TransportKeys k) throws DbException {
|
TransportKeys k) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (c != null && !db.containsContact(txn, c))
|
|
||||||
throw new NoSuchContactException();
|
|
||||||
if (!db.containsTransport(txn, k.getTransportId()))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
return db.addTransportKeys(txn, c, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindTransportKeys(Transaction transaction, ContactId c,
|
|
||||||
TransportId t, KeySetId k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, k.getTransportId()))
|
||||||
throw new NoSuchTransportException();
|
throw new NoSuchTransportException();
|
||||||
db.bindTransportKeys(txn, c, t, k);
|
db.addTransportKeys(txn, c, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -435,10 +431,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Group> getGroups(Transaction transaction, ClientId c,
|
public Collection<Group> getGroups(Transaction transaction, ClientId c)
|
||||||
int majorVersion) throws DbException {
|
throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
return db.getGroups(txn, c, majorVersion);
|
return db.getGroups(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -467,33 +463,24 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getMessageIds(Transaction transaction,
|
public Collection<MessageId> getMessagesToValidate(Transaction transaction,
|
||||||
GroupId g) throws DbException {
|
ClientId c) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsGroup(txn, g))
|
return db.getMessagesToValidate(txn, c);
|
||||||
throw new NoSuchGroupException();
|
|
||||||
return db.getMessageIds(txn, g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
public Collection<MessageId> getPendingMessages(Transaction transaction,
|
||||||
throws DbException {
|
ClientId c) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
return db.getMessagesToValidate(txn);
|
return db.getPendingMessages(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<MessageId> getPendingMessages(Transaction transaction)
|
public Collection<MessageId> getMessagesToShare(
|
||||||
throws DbException {
|
Transaction transaction, ClientId c) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
return db.getPendingMessages(txn);
|
return db.getMessagesToShare(txn, c);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<MessageId> getMessagesToShare(Transaction transaction)
|
|
||||||
throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
return db.getMessagesToShare(txn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -560,13 +547,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
if (!db.containsGroup(txn, g))
|
if (!db.containsGroup(txn, g))
|
||||||
throw new NoSuchGroupException();
|
throw new NoSuchGroupException();
|
||||||
if (db.getGroupVisibility(txn, c, g) == INVISIBLE) {
|
|
||||||
// No status rows exist - return default statuses
|
|
||||||
Collection<MessageStatus> statuses = new ArrayList<>();
|
|
||||||
for (MessageId m : db.getMessageIds(txn, g))
|
|
||||||
statuses.add(new MessageStatus(m, c, false, false));
|
|
||||||
return statuses;
|
|
||||||
}
|
|
||||||
return db.getMessageStatus(txn, c, g);
|
return db.getMessageStatus(txn, c, g);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,9 +558,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
if (!db.containsMessage(txn, m))
|
if (!db.containsMessage(txn, m))
|
||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
MessageStatus status = db.getMessageStatus(txn, c, m);
|
return db.getMessageStatus(txn, c, m);
|
||||||
if (status == null) return new MessageStatus(m, c, false, false);
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -601,13 +579,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return db.getMessageDependents(txn, m);
|
return db.getMessageDependents(txn, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getNextSendTime(Transaction transaction, ContactId c)
|
|
||||||
throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
return db.getNextSendTime(txn, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Settings getSettings(Transaction transaction, String namespace)
|
public Settings getSettings(Transaction transaction, String namespace)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -616,8 +587,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<KeySet> getTransportKeys(Transaction transaction,
|
public Map<ContactId, TransportKeys> getTransportKeys(
|
||||||
TransportId t) throws DbException {
|
Transaction transaction, TransportId t) throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
throw new NoSuchTransportException();
|
throw new NoSuchTransportException();
|
||||||
@@ -625,13 +596,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
public void incrementStreamCounter(Transaction transaction, ContactId c,
|
||||||
KeySetId k) throws DbException {
|
TransportId t, long rotationPeriod) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsContact(txn, c))
|
||||||
|
throw new NoSuchContactException();
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
throw new NoSuchTransportException();
|
throw new NoSuchTransportException();
|
||||||
db.incrementStreamCounter(txn, t, k);
|
db.incrementStreamCounter(txn, c, t, rotationPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -700,7 +673,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.raiseSeenFlag(txn, c, m.getId());
|
db.raiseSeenFlag(txn, c, m.getId());
|
||||||
db.raiseAckFlag(txn, c, m.getId());
|
db.raiseAckFlag(txn, c, m.getId());
|
||||||
} else {
|
} else {
|
||||||
db.addMessage(txn, m, UNKNOWN, false, c);
|
addMessage(txn, m, UNKNOWN, false, c);
|
||||||
transaction.attach(new MessageAddedEvent(m, c));
|
transaction.attach(new MessageAddedEvent(m, c));
|
||||||
}
|
}
|
||||||
transaction.attach(new MessageToAckEvent(c));
|
transaction.attach(new MessageToAckEvent(c));
|
||||||
@@ -768,8 +741,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
GroupId id = g.getId();
|
GroupId id = g.getId();
|
||||||
if (!db.containsGroup(txn, id))
|
if (!db.containsGroup(txn, id))
|
||||||
throw new NoSuchGroupException();
|
throw new NoSuchGroupException();
|
||||||
Collection<ContactId> affected =
|
Collection<ContactId> affected = db.getGroupVisibility(txn, id);
|
||||||
db.getGroupVisibility(txn, id).keySet();
|
|
||||||
db.removeGroup(txn, id);
|
db.removeGroup(txn, id);
|
||||||
transaction.attach(new GroupRemovedEvent(g));
|
transaction.attach(new GroupRemovedEvent(g));
|
||||||
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
||||||
@@ -793,7 +765,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, m))
|
if (!db.containsMessage(txn, m))
|
||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
// TODO: Don't allow messages with dependents to be removed
|
|
||||||
db.removeMessage(txn, m);
|
db.removeMessage(txn, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,16 +778,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
db.removeTransport(txn, t);
|
db.removeTransport(txn, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeTransportKeys(Transaction transaction,
|
|
||||||
TransportId t, KeySetId k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsTransport(txn, t))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
db.removeTransportKeys(txn, t, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContactVerified(Transaction transaction, ContactId c)
|
public void setContactVerified(Transaction transaction, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -850,9 +811,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
throw new NoSuchGroupException();
|
throw new NoSuchGroupException();
|
||||||
Visibility old = db.getGroupVisibility(txn, c, g);
|
Visibility old = db.getGroupVisibility(txn, c, g);
|
||||||
if (old == v) return;
|
if (old == v) return;
|
||||||
if (old == INVISIBLE) db.addGroupVisibility(txn, c, g, v == SHARED);
|
if (old == INVISIBLE) {
|
||||||
else if (v == INVISIBLE) db.removeGroupVisibility(txn, c, g);
|
db.addGroupVisibility(txn, c, g, v == SHARED);
|
||||||
else db.setGroupVisibility(txn, c, g, v == SHARED);
|
for (MessageId m : db.getMessageIds(txn, g)) {
|
||||||
|
boolean seen = db.removeOfferedMessage(txn, c, m);
|
||||||
|
db.addStatus(txn, c, m, seen, seen);
|
||||||
|
}
|
||||||
|
} else if (v == INVISIBLE) {
|
||||||
|
db.removeGroupVisibility(txn, c, g);
|
||||||
|
for (MessageId m : db.getMessageIds(txn, g))
|
||||||
|
db.removeStatus(txn, c, m);
|
||||||
|
} else {
|
||||||
|
db.setGroupVisibility(txn, c, g, v == SHARED);
|
||||||
|
}
|
||||||
List<ContactId> affected = Collections.singletonList(c);
|
List<ContactId> affected = Collections.singletonList(c);
|
||||||
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
transaction.attach(new GroupVisibilityUpdatedEvent(affected));
|
||||||
}
|
}
|
||||||
@@ -889,41 +860,39 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsMessage(txn, dependent.getId()))
|
if (!db.containsMessage(txn, dependent.getId()))
|
||||||
throw new NoSuchMessageException();
|
throw new NoSuchMessageException();
|
||||||
State dependentState = db.getMessageState(txn, dependent.getId());
|
|
||||||
for (MessageId dependency : dependencies) {
|
for (MessageId dependency : dependencies) {
|
||||||
db.addMessageDependency(txn, dependent, dependency, dependentState);
|
db.addMessageDependency(txn, dependent.getGroupId(),
|
||||||
|
dependent.getId(), dependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setReorderingWindow(Transaction transaction, KeySetId k,
|
public void setReorderingWindow(Transaction transaction, ContactId c,
|
||||||
TransportId t, long rotationPeriod, long base, byte[] bitmap)
|
TransportId t, long rotationPeriod, long base, byte[] bitmap)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsContact(txn, c))
|
||||||
|
throw new NoSuchContactException();
|
||||||
if (!db.containsTransport(txn, t))
|
if (!db.containsTransport(txn, t))
|
||||||
throw new NoSuchTransportException();
|
throw new NoSuchTransportException();
|
||||||
db.setReorderingWindow(txn, k, t, rotationPeriod, base, bitmap);
|
db.setReorderingWindow(txn, c, t, rotationPeriod, base, bitmap);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTransportKeysActive(Transaction transaction, TransportId t,
|
|
||||||
KeySetId k) throws DbException {
|
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsTransport(txn, t))
|
|
||||||
throw new NoSuchTransportException();
|
|
||||||
db.setTransportKeysActive(txn, t, k);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateTransportKeys(Transaction transaction,
|
public void updateTransportKeys(Transaction transaction,
|
||||||
Collection<KeySet> keys) throws DbException {
|
Map<ContactId, TransportKeys> keys) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
for (KeySet ks : keys) {
|
Map<ContactId, TransportKeys> filtered = new HashMap<>();
|
||||||
TransportId t = ks.getTransportKeys().getTransportId();
|
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
||||||
if (db.containsTransport(txn, t)) db.updateTransportKeys(txn, ks);
|
ContactId c = e.getKey();
|
||||||
|
TransportKeys k = e.getValue();
|
||||||
|
if (db.containsContact(txn, c)
|
||||||
|
&& db.containsTransport(txn, k.getTransportId())) {
|
||||||
|
filtered.put(c, k);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
db.updateTransportKeys(txn, filtered);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,10 @@ interface DatabaseConstants {
|
|||||||
*/
|
*/
|
||||||
String SCHEMA_VERSION_KEY = "schemaVersion";
|
String SCHEMA_VERSION_KEY = "schemaVersion";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Settings} key under which the minimum supported database
|
||||||
|
* schema version is stored.
|
||||||
|
*/
|
||||||
|
String MIN_SCHEMA_VERSION_KEY = "minSchemaVersion";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
@@ -14,7 +13,6 @@ import java.sql.DriverManager;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,11 +42,10 @@ class H2Database extends JdbcDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open(@Nullable MigrationListener listener)
|
public boolean open() throws DbException {
|
||||||
throws DbException {
|
|
||||||
boolean reopen = config.databaseExists();
|
boolean reopen = config.databaseExists();
|
||||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||||
super.open("org.h2.Driver", reopen, listener);
|
super.open("org.h2.Driver", reopen);
|
||||||
return reopen;
|
return reopen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,10 +92,6 @@ class H2Database extends JdbcDatabase {
|
|||||||
// Separate the file password from the user password with a space
|
// Separate the file password from the user password with a space
|
||||||
String hex = StringUtils.toHexString(key.getBytes());
|
String hex = StringUtils.toHexString(key.getBytes());
|
||||||
props.put("password", hex + " password");
|
props.put("password", hex + " password");
|
||||||
return DriverManager.getConnection(getUrl(), props);
|
return DriverManager.getConnection(url, props);
|
||||||
}
|
|
||||||
|
|
||||||
String getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
@@ -14,7 +13,6 @@ import java.sql.DriverManager;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,10 +44,10 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open(@Nullable MigrationListener listener) throws DbException {
|
public boolean open() throws DbException {
|
||||||
boolean reopen = config.databaseExists();
|
boolean reopen = config.databaseExists();
|
||||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, listener);
|
super.open("org.hsqldb.jdbc.JDBCDriver", reopen);
|
||||||
return reopen;
|
return reopen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user