mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Compare commits
268 Commits
beta-2017-
...
beta-0.16.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef2286ab53 | ||
|
|
47b25f3221 | ||
|
|
c30bfa12ce | ||
|
|
d0fc04251d | ||
|
|
dcbb41eb7a | ||
|
|
999bdf8866 | ||
|
|
911c0c0fd9 | ||
|
|
99d8cc64a6 | ||
|
|
ba727d7568 | ||
|
|
ed01048f9f | ||
|
|
043ee3c58e | ||
|
|
6e0af7deda | ||
|
|
9591db2097 | ||
|
|
329a4c64f6 | ||
|
|
79015bc5ae | ||
|
|
27422ab9f9 | ||
|
|
abcb682498 | ||
|
|
5044127c46 | ||
|
|
0e4b8ca62e | ||
|
|
822017c69c | ||
|
|
eb6561b93d | ||
|
|
d24b1884a2 | ||
|
|
078534889e | ||
|
|
e92713006a | ||
|
|
18f43f3bc1 | ||
|
|
a4118b40e1 | ||
|
|
de29fbc324 | ||
|
|
3197dcf9b5 | ||
|
|
35aad409fd | ||
|
|
08ce6a7331 | ||
|
|
33a0099065 | ||
|
|
34d20fafda | ||
|
|
aafddcd0f0 | ||
|
|
0d6983b4ef | ||
|
|
69bfb72171 | ||
|
|
1aa33ec9b2 | ||
|
|
6702df1e22 | ||
|
|
c1748c9a86 | ||
|
|
9df624c62a | ||
|
|
0ee6197d7f | ||
|
|
b03a7dce3e | ||
|
|
6c59d7dd5f | ||
|
|
050191f0ef | ||
|
|
4b5a19ce5d | ||
|
|
7c4dd991b9 | ||
|
|
8455569e88 | ||
|
|
d25676559c | ||
|
|
a9437f7985 | ||
|
|
8141a97fc9 | ||
|
|
db842bd7e4 | ||
|
|
6dbec3a864 | ||
|
|
29f658cf4d | ||
|
|
ca83744a84 | ||
|
|
d91a9e2be4 | ||
|
|
8408c3f467 | ||
|
|
544c83a64c | ||
|
|
3800cd5e4f | ||
|
|
259f2cd419 | ||
|
|
20eb022c36 | ||
|
|
531e555b52 | ||
|
|
a9024aa34b | ||
|
|
d4e3b7842c | ||
|
|
167fddfbcc | ||
|
|
a48d642648 | ||
|
|
9a70f054c7 | ||
|
|
ca43d13bd6 | ||
|
|
5b71004179 | ||
|
|
63befccdbf | ||
|
|
4ecf7c02d0 | ||
|
|
f25badc18c | ||
|
|
6e931e9ba5 | ||
|
|
7e749124bf | ||
|
|
5822eb7808 | ||
|
|
7a7e086541 | ||
|
|
abab3167c2 | ||
|
|
8d08570568 | ||
|
|
2007078f13 | ||
|
|
dfb71a7978 | ||
|
|
480b0e3a03 | ||
|
|
8f8751f4ac | ||
|
|
de2ea112ee | ||
|
|
6f99a53fd9 | ||
|
|
a8a9b9032d | ||
|
|
6b15fb89de | ||
|
|
a711d6b8a1 | ||
|
|
5678f8aaa4 | ||
|
|
2fe37f6c26 | ||
|
|
a879747968 | ||
|
|
95e8fd7ee0 | ||
|
|
4416aaaa4c | ||
|
|
500d5f0efe | ||
|
|
fc8978fd90 | ||
|
|
73df126bd4 | ||
|
|
9146488c7d | ||
|
|
613a7fe376 | ||
|
|
ecb62f00d4 | ||
|
|
c4540a03cd | ||
|
|
3e31da99b5 | ||
|
|
098c1d0b1e | ||
|
|
178e908c86 | ||
|
|
ecf7cf14ae | ||
|
|
09e2a15a73 | ||
|
|
ab387860a6 | ||
|
|
f63fc94f2b | ||
|
|
41e5928cca | ||
|
|
8303175494 | ||
|
|
151eb6935b | ||
|
|
6a419c0c7b | ||
|
|
1795b32121 | ||
|
|
01971768ce | ||
|
|
ef7483ab01 | ||
|
|
527d11473d | ||
|
|
775dadc9a0 | ||
|
|
800b10a988 | ||
|
|
c977bf047d | ||
|
|
660a25f21d | ||
|
|
e7fd6d23af | ||
|
|
46982897f0 | ||
|
|
d24de68d64 | ||
|
|
7514c46a3f | ||
|
|
6632c0f8e3 | ||
|
|
79aafcda69 | ||
|
|
05af21e8dc | ||
|
|
0dc62cbbdc | ||
|
|
f3a084cfd2 | ||
|
|
8b32f82566 | ||
|
|
d598b6ed44 | ||
|
|
f5dc6f24b9 | ||
|
|
37454392da | ||
|
|
de7f9111d3 | ||
|
|
96d2889a6c | ||
|
|
f6412d1e9a | ||
|
|
b377cd6b1c | ||
|
|
f6cdbda5bb | ||
|
|
855c600a3e | ||
|
|
ea6e8303b0 | ||
|
|
d4934040d9 | ||
|
|
3449677b24 | ||
|
|
1ad3a6646e | ||
|
|
2d10f6b2bd | ||
|
|
5b05424d83 | ||
|
|
0826022d82 | ||
|
|
a901bfb9cb | ||
|
|
03cdce122a | ||
|
|
f2e0e16969 | ||
|
|
0c441e2ff3 | ||
|
|
21302304a5 | ||
|
|
6839d8b844 | ||
|
|
aee65a716c | ||
|
|
6a07d8f2c9 | ||
|
|
3c1ea81cd0 | ||
|
|
025f417bc7 | ||
|
|
c9dcd906c9 | ||
|
|
7024e04d15 | ||
|
|
0b8ac947db | ||
|
|
948410a064 | ||
|
|
2841339cac | ||
|
|
e8e82bd805 | ||
|
|
6876f40a0e | ||
|
|
5f4e1ecdfd | ||
|
|
044719432a | ||
|
|
d1a929da85 | ||
|
|
2a8978a60d | ||
|
|
c0afad7a26 | ||
|
|
37281c6c23 | ||
|
|
6de539a62d | ||
|
|
34704ec04d | ||
|
|
9fd6d46583 | ||
|
|
76a5e25656 | ||
|
|
3575b74837 | ||
|
|
f1c7996960 | ||
|
|
920f3581fa | ||
|
|
45e7af31fe | ||
|
|
67d5d8cdf1 | ||
|
|
9d8cadb7a9 | ||
|
|
6425c49d04 | ||
|
|
68d98b50f2 | ||
|
|
84986d393f | ||
|
|
115d488bc3 | ||
|
|
2eeb2213e3 | ||
|
|
1b48d661e8 | ||
|
|
49ba66dee9 | ||
|
|
46920f3bce | ||
|
|
4b955809f7 | ||
|
|
57d4d6546a | ||
|
|
9bfb58a764 | ||
|
|
0256ec0b8c | ||
|
|
b0b4a85d15 | ||
|
|
d40a058ef5 | ||
|
|
58b9efb24c | ||
|
|
17de785c12 | ||
|
|
c7ff1ba974 | ||
|
|
d17669f131 | ||
|
|
9755cd9ab4 | ||
|
|
6d2b18facc | ||
|
|
f8cf7034db | ||
|
|
a1e65c9fa7 | ||
|
|
499d2fe677 | ||
|
|
fe963edd9d | ||
|
|
96f006068f | ||
|
|
74f1fa5690 | ||
|
|
85c17b4cb0 | ||
|
|
6b3a1fd6d4 | ||
|
|
bcabcfce8c | ||
|
|
db0a3bf380 | ||
|
|
d5d9436e28 | ||
|
|
0827b067ec | ||
|
|
9d0dbe9210 | ||
|
|
1f7d1bf515 | ||
|
|
fb85ecf07b | ||
|
|
a931e6b316 | ||
|
|
3aa4644339 | ||
|
|
9a638c804a | ||
|
|
df3254c634 | ||
|
|
ba353b9f2b | ||
|
|
04c4e70dd1 | ||
|
|
d381e25e86 | ||
|
|
0c085f139a | ||
|
|
4123f4a5ce | ||
|
|
7bc269fda4 | ||
|
|
a22931bae6 | ||
|
|
403f886110 | ||
|
|
b7866be38d | ||
|
|
a1b415330e | ||
|
|
58318bb79f | ||
|
|
10bb30e190 | ||
|
|
199a2ffc46 | ||
|
|
f6ad2992f2 | ||
|
|
f039bd1239 | ||
|
|
da22d91ef3 | ||
|
|
cd360ec877 | ||
|
|
8e1ada4cdc | ||
|
|
ac063b4c79 | ||
|
|
10e6163e94 | ||
|
|
ebc3402307 | ||
|
|
d9c63bbcfe | ||
|
|
9c89e83c20 | ||
|
|
adc9bdeb68 | ||
|
|
ff7f0bdc63 | ||
|
|
c5f6980c69 | ||
|
|
2574354997 | ||
|
|
c4e42949cf | ||
|
|
1c5897f1cc | ||
|
|
510f99c7da | ||
|
|
1918346ae8 | ||
|
|
2a59515c72 | ||
|
|
7161152b41 | ||
|
|
b42660edab | ||
|
|
b405bbf98e | ||
|
|
c167938b61 | ||
|
|
24b531e6b2 | ||
|
|
9cffff715a | ||
|
|
804e912e19 | ||
|
|
d67e3900e3 | ||
|
|
e682f31898 | ||
|
|
a9053808b4 | ||
|
|
d9a62a0431 | ||
|
|
15ba73276d | ||
|
|
720dda784e | ||
|
|
0ae55404f5 | ||
|
|
9c41437870 | ||
|
|
da9cde083f | ||
|
|
ce3156c9fe | ||
|
|
be3752bf2f | ||
|
|
ef74db65aa | ||
|
|
867a233b6f | ||
|
|
59af25b2cd | ||
|
|
79c78518fb |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -9,17 +9,18 @@ Thumbs.db
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Eclipse project files
|
# Eclipse project files
|
||||||
#.classpath
|
.classpath
|
||||||
#.project
|
.project
|
||||||
|
.settings
|
||||||
|
|
||||||
# Local configuration file (sdk path, etc)
|
# Local configuration file (sdk path, etc)
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
# Android Studio
|
# Android Studio
|
||||||
.idea/*
|
.idea/*
|
||||||
|
!.idea/runConfigurations/
|
||||||
!.idea/codeStyleSettings.xml
|
!.idea/codeStyleSettings.xml
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
build/
|
||||||
*.iml
|
*.iml
|
||||||
.gitignore
|
projectFilesBackup/
|
||||||
src/test/
|
|
||||||
20
.gitlab-ci.yml
Normal file
20
.gitlab-ci.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
image: registry.gitlab.com/fdroid/ci-images-base:latest
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .gradle/wrapper
|
||||||
|
- .gradle/caches
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
# - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
||||||
|
# - echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK}
|
||||||
|
|
||||||
|
test:
|
||||||
|
script:
|
||||||
|
- ./gradlew test
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
# this file changes every time but should not be cached
|
||||||
|
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||||
|
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||||
28
.idea/runConfigurations/All_tests.xml
generated
Normal file
28
.idea/runConfigurations/All_tests.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="briar-android" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method>
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-j2se" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
23
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="bramble-api" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
||||||
|
<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>
|
||||||
23
.idea/runConfigurations/All_tests_in_bramble_core.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_bramble_core.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in bramble-core" 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="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
||||||
|
<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>
|
||||||
23
.idea/runConfigurations/All_tests_in_bramble_j2se.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_bramble_j2se.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in bramble-j2se" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="bramble-j2se" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-j2se" />
|
||||||
|
<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>
|
||||||
23
.idea/runConfigurations/All_tests_in_briar_android.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_briar_android.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="briar-android" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||||
|
<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>
|
||||||
23
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="briar-core" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
||||||
|
<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>
|
||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 22
|
targetSdkVersion 22
|
||||||
versionCode 1
|
versionCode 1610
|
||||||
versionName "1.0"
|
versionName "0.16.10"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,38 +25,38 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-core')
|
compile project(':bramble-core')
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
provided 'javax.annotation:jsr250-api:1.0'
|
provided 'javax.annotation:jsr250-api:1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
def torBinaryDir = 'src/main/res/raw'
|
def torBinaryDir = 'src/main/res/raw'
|
||||||
|
|
||||||
task downloadTorGeoIp(type: Download) {
|
task downloadTorGeoIp(type: Download) {
|
||||||
src 'https://briarproject.org/build/geoip-2015-12-01.zip'
|
src 'https://briarproject.org/build/geoip-2017-09-06.zip'
|
||||||
dest "$torBinaryDir/geoip.zip"
|
dest "$torBinaryDir/geoip.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryArm(type: Download) {
|
task downloadTorBinaryArm(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-arm.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.12-arm.zip'
|
||||||
dest "$torBinaryDir/tor_arm.zip"
|
dest "$torBinaryDir/tor_arm.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryArmPie(type: Download) {
|
task downloadTorBinaryArmPie(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-arm-pie.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.12-arm-pie.zip'
|
||||||
dest "$torBinaryDir/tor_arm_pie.zip"
|
dest "$torBinaryDir/tor_arm_pie.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryX86(type: Download) {
|
task downloadTorBinaryX86(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-x86.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.12-x86.zip'
|
||||||
dest "$torBinaryDir/tor_x86.zip"
|
dest "$torBinaryDir/tor_x86.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryX86Pie(type: Download) {
|
task downloadTorBinaryX86Pie(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-x86-pie.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.12-x86-pie.zip'
|
||||||
dest "$torBinaryDir/tor_x86_pie.zip"
|
dest "$torBinaryDir/tor_x86_pie.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
@@ -64,31 +64,31 @@ task downloadTorBinaryX86Pie(type: Download) {
|
|||||||
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
|
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
|
||||||
src "$torBinaryDir/geoip.zip"
|
src "$torBinaryDir/geoip.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
|
checksum 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
|
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
|
||||||
src "$torBinaryDir/tor_arm.zip"
|
src "$torBinaryDir/tor_arm.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
|
checksum '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
|
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
|
||||||
src "$torBinaryDir/tor_arm_pie.zip"
|
src "$torBinaryDir/tor_arm_pie.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
|
checksum '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
|
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
|
||||||
src "$torBinaryDir/tor_x86.zip"
|
src "$torBinaryDir/tor_x86.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
|
checksum '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
|
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
|
||||||
src "$torBinaryDir/tor_x86_pie.zip"
|
src "$torBinaryDir/tor_x86_pie.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
|
checksum '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288'
|
||||||
}
|
}
|
||||||
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
|
|||||||
Binary file not shown.
@@ -11,7 +11,6 @@ import android.content.IntentFilter;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||||
@@ -30,23 +29,14 @@ import org.briarproject.bramble.util.StringUtils;
|
|||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CompletionService;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -61,12 +51,11 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
|
|||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||||
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
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 org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
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.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_ADDRESS;
|
||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
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.api.plugin.BluetoothConstants.UUID_BYTES;
|
||||||
@@ -78,10 +67,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(DroidtoothPlugin.class.getName());
|
Logger.getLogger(DroidtoothPlugin.class.getName());
|
||||||
private static final String FOUND =
|
|
||||||
"android.bluetooth.device.action.FOUND";
|
|
||||||
private static final String DISCOVERY_FINISHED =
|
|
||||||
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
|
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
@@ -164,7 +149,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
bind();
|
bind();
|
||||||
} else {
|
} else {
|
||||||
// Enable Bluetooth if settings allow
|
// Enable Bluetooth if settings allow
|
||||||
if (callback.getSettings().getBoolean("enable", false)) {
|
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
|
||||||
wasEnabledByUs = true;
|
wasEnabledByUs = true;
|
||||||
if (adapter.enable()) LOG.info("Enabling Bluetooth");
|
if (adapter.enable()) LOG.info("Enabling Bluetooth");
|
||||||
else LOG.info("Could not enable Bluetooth");
|
else LOG.info("Could not enable Bluetooth");
|
||||||
@@ -373,90 +358,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return new DroidtoothTransportConnection(this, s);
|
return new DroidtoothTransportConnection(this, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice) {
|
|
||||||
if (!isRunning()) return null;
|
|
||||||
// Use the invitation codes to generate the UUID
|
|
||||||
byte[] b = r.nextBytes(UUID_BYTES);
|
|
||||||
UUID uuid = UUID.nameUUIDFromBytes(b);
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Invitation UUID " + uuid);
|
|
||||||
// Bind a server socket for receiving invitation connections
|
|
||||||
BluetoothServerSocket ss;
|
|
||||||
try {
|
|
||||||
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
|
||||||
"RFCOMM", uuid);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Create the background tasks
|
|
||||||
CompletionService<BluetoothSocket> complete =
|
|
||||||
new ExecutorCompletionService<>(ioExecutor);
|
|
||||||
List<Future<BluetoothSocket>> futures = new ArrayList<>();
|
|
||||||
if (alice) {
|
|
||||||
// Return the first connected socket
|
|
||||||
futures.add(complete.submit(new ListeningTask(ss)));
|
|
||||||
futures.add(complete.submit(new DiscoveryTask(uuid.toString())));
|
|
||||||
} else {
|
|
||||||
// Return the first socket with readable data
|
|
||||||
futures.add(complete.submit(new ReadableTask(
|
|
||||||
new ListeningTask(ss))));
|
|
||||||
futures.add(complete.submit(new ReadableTask(
|
|
||||||
new DiscoveryTask(uuid.toString()))));
|
|
||||||
}
|
|
||||||
BluetoothSocket chosen = null;
|
|
||||||
try {
|
|
||||||
Future<BluetoothSocket> f = complete.poll(timeout, MILLISECONDS);
|
|
||||||
if (f == null) return null; // No task completed within the timeout
|
|
||||||
chosen = f.get();
|
|
||||||
return new DroidtoothTransportConnection(this, chosen);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while exchanging invitations");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return null;
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
// Closing the socket will terminate the listener task
|
|
||||||
tryToClose(ss);
|
|
||||||
closeSockets(futures, chosen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeSockets(final List<Future<BluetoothSocket>> futures,
|
|
||||||
@Nullable final BluetoothSocket chosen) {
|
|
||||||
ioExecutor.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (Future<BluetoothSocket> f : futures) {
|
|
||||||
try {
|
|
||||||
if (f.cancel(true)) {
|
|
||||||
LOG.info("Cancelled task");
|
|
||||||
} else {
|
|
||||||
BluetoothSocket s = f.get();
|
|
||||||
if (s != null && s != chosen) {
|
|
||||||
LOG.info("Closing unwanted socket");
|
|
||||||
s.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while closing sockets");
|
|
||||||
return;
|
|
||||||
} catch (ExecutionException | IOException e) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsKeyAgreement() {
|
public boolean supportsKeyAgreement() {
|
||||||
return true;
|
return true;
|
||||||
@@ -471,7 +372,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
// No truncation necessary because COMMIT_LENGTH = 16
|
// No truncation necessary because COMMIT_LENGTH = 16
|
||||||
UUID uuid = UUID.nameUUIDFromBytes(commitment);
|
UUID uuid = UUID.nameUUIDFromBytes(commitment);
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
||||||
// Bind a server socket for receiving invitation connections
|
// Bind a server socket for receiving key agreement connections
|
||||||
BluetoothServerSocket ss;
|
BluetoothServerSocket ss;
|
||||||
try {
|
try {
|
||||||
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
||||||
@@ -535,115 +436,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DiscoveryTask implements Callable<BluetoothSocket> {
|
|
||||||
|
|
||||||
private final String uuid;
|
|
||||||
|
|
||||||
private DiscoveryTask(String uuid) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BluetoothSocket call() throws Exception {
|
|
||||||
// Repeat discovery until we connect or get interrupted
|
|
||||||
while (true) {
|
|
||||||
// Discover nearby devices
|
|
||||||
LOG.info("Discovering nearby devices");
|
|
||||||
List<String> addresses = discoverDevices();
|
|
||||||
if (addresses.isEmpty()) {
|
|
||||||
LOG.info("No devices discovered");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Connect to any device with the right UUID
|
|
||||||
for (String address : addresses) {
|
|
||||||
BluetoothSocket s = connect(address, uuid);
|
|
||||||
if (s != null) {
|
|
||||||
LOG.info("Outgoing connection");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> discoverDevices() throws InterruptedException {
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
filter.addAction(FOUND);
|
|
||||||
filter.addAction(DISCOVERY_FINISHED);
|
|
||||||
DiscoveryReceiver disco = new DiscoveryReceiver();
|
|
||||||
appContext.registerReceiver(disco, filter);
|
|
||||||
LOG.info("Starting discovery");
|
|
||||||
adapter.startDiscovery();
|
|
||||||
return disco.waitForAddresses();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DiscoveryReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private final CountDownLatch finished = new CountDownLatch(1);
|
|
||||||
private final List<String> addresses = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context ctx, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (action.equals(DISCOVERY_FINISHED)) {
|
|
||||||
LOG.info("Discovery finished");
|
|
||||||
ctx.unregisterReceiver(this);
|
|
||||||
finished.countDown();
|
|
||||||
} else if (action.equals(FOUND)) {
|
|
||||||
BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE);
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Discovered device: " +
|
|
||||||
scrubMacAddress(d.getAddress()));
|
|
||||||
}
|
|
||||||
addresses.add(d.getAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> waitForAddresses() throws InterruptedException {
|
|
||||||
finished.await();
|
|
||||||
List<String> shuffled = new ArrayList<>(addresses);
|
|
||||||
Collections.shuffle(shuffled);
|
|
||||||
return shuffled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ListeningTask implements Callable<BluetoothSocket> {
|
|
||||||
|
|
||||||
private final BluetoothServerSocket serverSocket;
|
|
||||||
|
|
||||||
private ListeningTask(BluetoothServerSocket serverSocket) {
|
|
||||||
this.serverSocket = serverSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BluetoothSocket call() throws IOException {
|
|
||||||
BluetoothSocket s = serverSocket.accept();
|
|
||||||
LOG.info("Incoming connection");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ReadableTask implements Callable<BluetoothSocket> {
|
|
||||||
|
|
||||||
private final Callable<BluetoothSocket> connectionTask;
|
|
||||||
|
|
||||||
private ReadableTask(Callable<BluetoothSocket> connectionTask) {
|
|
||||||
this.connectionTask = connectionTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BluetoothSocket call() throws Exception {
|
|
||||||
BluetoothSocket s = connectionTask.call();
|
|
||||||
InputStream in = s.getInputStream();
|
|
||||||
while (in.available() == 0) {
|
|
||||||
LOG.info("Waiting for data");
|
|
||||||
Thread.sleep(1000);
|
|
||||||
}
|
|
||||||
LOG.info("Data available");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
|
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
|
||||||
|
|
||||||
private final BluetoothServerSocket ss;
|
private final BluetoothServerSocket ss;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import net.freehaven.tor.control.EventHandler;
|
|||||||
import net.freehaven.tor.control.TorControlConnection;
|
import net.freehaven.tor.control.TorControlConnection;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
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;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -79,6 +78,12 @@ 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;
|
||||||
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_ALWAYS;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -182,19 +187,31 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
String torPath = torFile.getAbsolutePath();
|
String torPath = torFile.getAbsolutePath();
|
||||||
String configPath = configFile.getAbsolutePath();
|
String configPath = configFile.getAbsolutePath();
|
||||||
String pid = String.valueOf(android.os.Process.myPid());
|
String pid = String.valueOf(android.os.Process.myPid());
|
||||||
String[] cmd = {torPath, "-f", configPath, OWNER, pid};
|
|
||||||
String[] env = {"HOME=" + torDirectory.getAbsolutePath()};
|
|
||||||
Process torProcess;
|
Process torProcess;
|
||||||
|
ProcessBuilder pb =
|
||||||
|
new ProcessBuilder(torPath, "-f", configPath, OWNER, pid);
|
||||||
|
Map<String, String> env = pb.environment();
|
||||||
|
env.put("HOME", torDirectory.getAbsolutePath());
|
||||||
|
pb.directory(torDirectory);
|
||||||
try {
|
try {
|
||||||
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
|
torProcess = pb.start();
|
||||||
} catch (SecurityException | IOException e) {
|
} catch (SecurityException | IOException e) {
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
// Log the process's standard output until it detaches
|
// Log the process's standard output until it detaches
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
while (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
Scanner stderr = new Scanner(torProcess.getErrorStream());
|
||||||
|
while (stdout.hasNextLine() || stderr.hasNextLine()){
|
||||||
|
if(stdout.hasNextLine()) {
|
||||||
|
LOG.info(stdout.nextLine());
|
||||||
|
}
|
||||||
|
if(stderr.hasNextLine()){
|
||||||
|
LOG.info(stderr.nextLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
stdout.close();
|
stdout.close();
|
||||||
|
stderr.close();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Wait for the process to detach or exit
|
// Wait for the process to detach or exit
|
||||||
@@ -366,7 +383,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
String portString = callback.getSettings().get("port");
|
String portString = callback.getSettings().get(PREF_TOR_PORT);
|
||||||
int port;
|
int port;
|
||||||
if (StringUtils.isNullOrEmpty(portString)) port = 0;
|
if (StringUtils.isNullOrEmpty(portString)) port = 0;
|
||||||
else port = Integer.parseInt(portString);
|
else port = Integer.parseInt(portString);
|
||||||
@@ -389,7 +406,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// Store the port number
|
// Store the port number
|
||||||
final String localPort = String.valueOf(ss.getLocalPort());
|
final String localPort = String.valueOf(ss.getLocalPort());
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.put("port", localPort);
|
s.put(PREF_TOR_PORT, localPort);
|
||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
// Create a hidden service if necessary
|
// Create a hidden service if necessary
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(new Runnable() {
|
||||||
@@ -571,17 +588,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsKeyAgreement() {
|
public boolean supportsKeyAgreement() {
|
||||||
return false;
|
return false;
|
||||||
@@ -666,7 +672,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof SettingsUpdatedEvent) {
|
if (e instanceof SettingsUpdatedEvent) {
|
||||||
if (((SettingsUpdatedEvent) e).getNamespace().equals("tor")) {
|
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||||
|
if (s.getNamespace().equals(ID.getString())) {
|
||||||
LOG.info("Tor settings updated");
|
LOG.info("Tor settings updated");
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
}
|
}
|
||||||
@@ -688,7 +695,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
|
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
|
||||||
country);
|
country);
|
||||||
Settings s = callback.getSettings();
|
Settings s = callback.getSettings();
|
||||||
boolean useMobileData = s.getBoolean("torOverMobile", true);
|
int network = s.getInt(PREF_TOR_NETWORK,
|
||||||
|
PREF_TOR_NETWORK_ALWAYS);
|
||||||
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Online: " + online + ", wifi: " + wifi);
|
LOG.info("Online: " + online + ", wifi: " + wifi);
|
||||||
@@ -703,7 +711,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
} else if (blocked) {
|
} else if (blocked) {
|
||||||
LOG.info("Disabling network, country is blocked");
|
LOG.info("Disabling network, country is blocked");
|
||||||
enableNetwork(false);
|
enableNetwork(false);
|
||||||
} else if (!wifi && !useMobileData) {
|
} else if (network == PREF_TOR_NETWORK_NEVER
|
||||||
|
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
|
||||||
LOG.info("Disabling network due to data setting");
|
LOG.info("Disabling network due to data setting");
|
||||||
enableNetwork(false);
|
enableNetwork(false);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -21,12 +22,12 @@ class TorTransportConnection extends AbstractDuplexTransportConnection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getInputStream() throws IOException {
|
protected InputStream getInputStream() throws IOException {
|
||||||
return socket.getInputStream();
|
return IoUtils.getInputStream(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OutputStream getOutputStream() throws IOException {
|
protected OutputStream getOutputStream() throws IOException {
|
||||||
return socket.getOutputStream();
|
return IoUtils.getOutputStream(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.wifi.WifiConfiguration;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
|
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
||||||
|
|
||||||
|
private static final int SEED_LENGTH = 32;
|
||||||
|
|
||||||
|
private final Context appContext;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AndroidSecureRandomProvider(Application app) {
|
||||||
|
appContext = app.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||||
|
super.writeToEntropyPool(out);
|
||||||
|
out.writeInt(android.os.Process.myPid());
|
||||||
|
out.writeInt(android.os.Process.myTid());
|
||||||
|
out.writeInt(android.os.Process.myUid());
|
||||||
|
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
||||||
|
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
||||||
|
ContentResolver contentResolver = appContext.getContentResolver();
|
||||||
|
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||||
|
if (id != null) out.writeUTF(id);
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
WifiManager wm =
|
||||||
|
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
|
||||||
|
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
||||||
|
if (configs != null) {
|
||||||
|
for (WifiConfiguration config : configs)
|
||||||
|
parcel.writeParcelable(config, 0);
|
||||||
|
}
|
||||||
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (bt != null) {
|
||||||
|
for (BluetoothDevice device : bt.getBondedDevices())
|
||||||
|
parcel.writeParcelable(device, 0);
|
||||||
|
}
|
||||||
|
out.write(parcel.marshall());
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeSeed() {
|
||||||
|
super.writeSeed();
|
||||||
|
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
|
||||||
|
applyOpenSslFix();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private void applyOpenSslFix() {
|
||||||
|
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
|
||||||
|
SEED_LENGTH);
|
||||||
|
try {
|
||||||
|
// Seed the OpenSSL PRNG
|
||||||
|
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_seed", byte[].class)
|
||||||
|
.invoke(null, seed);
|
||||||
|
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
||||||
|
int bytesRead = (Integer) Class.forName(
|
||||||
|
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_load_file", String.class, long.class)
|
||||||
|
.invoke(null, "/dev/urandom", 1024);
|
||||||
|
if (bytesRead != 1024) throw new IOException();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.provider.Settings;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class AndroidSeedProvider extends LinuxSeedProvider {
|
|
||||||
|
|
||||||
private final Context appContext;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AndroidSeedProvider(Application app) {
|
|
||||||
appContext = app.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void writeToEntropyPool(DataOutputStream out) throws IOException {
|
|
||||||
out.writeInt(android.os.Process.myPid());
|
|
||||||
out.writeInt(android.os.Process.myTid());
|
|
||||||
out.writeInt(android.os.Process.myUid());
|
|
||||||
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
|
||||||
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
|
||||||
ContentResolver contentResolver = appContext.getContentResolver();
|
|
||||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
|
||||||
if (id != null) out.writeUTF(id);
|
|
||||||
super.writeToEntropyPool(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import android.app.Application;
|
|||||||
|
|
||||||
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.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ public class AndroidSystemModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SeedProvider provideSeedProvider(Application app) {
|
SecureRandomProvider provideSecureRandomProvider(Application app) {
|
||||||
return new AndroidSeedProvider(app);
|
return new AndroidSecureRandomProvider(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ apply plugin: 'witness'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile "com.google.dagger:dagger:2.0.2"
|
compile "com.google.dagger:dagger:2.0.2"
|
||||||
compile 'com.google.dagger:dagger-compiler:2.0.2'
|
compile 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.1'
|
compile 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile "org.jmock:jmock:2.8.1"
|
testCompile "org.jmock:jmock:2.8.2"
|
||||||
testCompile "org.jmock:jmock-junit4:2.8.1"
|
testCompile "org.jmock:jmock-junit4:2.8.2"
|
||||||
testCompile "org.jmock:jmock-legacy:2.8.1"
|
testCompile "org.jmock:jmock-legacy:2.8.2"
|
||||||
testCompile "org.hamcrest:hamcrest-library:1.3"
|
testCompile "org.hamcrest:hamcrest-library:1.3"
|
||||||
testCompile "org.hamcrest:hamcrest-core:1.3"
|
testCompile "org.hamcrest:hamcrest-core:1.3"
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ dependencyVerification {
|
|||||||
verify = [
|
verify = [
|
||||||
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||||
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
|
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||||
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api;
|
package org.briarproject.bramble.api;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -53,6 +54,12 @@ public class Bytes implements Comparable<Bytes> {
|
|||||||
return aBytes.length - bBytes.length;
|
return aBytes.length - bBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() +
|
||||||
|
"(" + StringUtils.toHexString(getBytes()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
public static class BytesComparator implements Comparator<Bytes> {
|
public static class BytesComparator implements Comparator<Bytes> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
SecretKey generateSecretKey();
|
SecretKey generateSecretKey();
|
||||||
|
|
||||||
PseudoRandom getPseudoRandom(int seed1, int seed2);
|
|
||||||
|
|
||||||
SecureRandom getSecureRandom();
|
SecureRandom getSecureRandom();
|
||||||
|
|
||||||
KeyPair generateAgreementKeyPair();
|
KeyPair generateAgreementKeyPair();
|
||||||
@@ -24,15 +22,6 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
KeyParser getMessageKeyParser();
|
KeyParser getMessageKeyParser();
|
||||||
|
|
||||||
/** Generates a random invitation code. */
|
|
||||||
int generateBTInvitationCode();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a confirmation code from the given master secret.
|
|
||||||
* @param alice whether the code is for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
int deriveBTConfirmationCode(SecretKey master, boolean alice);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a stream header key from the given master secret.
|
* Derives a stream header key from the given master secret.
|
||||||
* @param alice whether the key is for use by Alice or Bob.
|
* @param alice whether the key is for use by Alice or Bob.
|
||||||
@@ -137,7 +126,8 @@ public interface CryptoComponent {
|
|||||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
||||||
|
|
||||||
/** Encodes the pseudo-random tag that is used to recognise a stream. */
|
/** Encodes the pseudo-random tag that is used to recognise a stream. */
|
||||||
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
|
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
|
long streamNumber);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the given byte[] with the given PrivateKey.
|
* Signs the given byte[] with the given PrivateKey.
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
public interface PasswordStrengthEstimator {
|
public interface PasswordStrengthEstimator {
|
||||||
|
|
||||||
float NONE = 0;
|
float NONE = 0;
|
||||||
float WEAK = 0.4f;
|
float WEAK = 0.25f;
|
||||||
float QUITE_WEAK = 0.6f;
|
float QUITE_WEAK = 0.5f;
|
||||||
float QUITE_STRONG = 0.8f;
|
float QUITE_STRONG = 0.75f;
|
||||||
float STRONG = 1;
|
float STRONG = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A deterministic pseudo-random number generator.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface PseudoRandom {
|
|
||||||
|
|
||||||
byte[] nextBytes(int bytes);
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,9 @@ public interface StreamDecrypterFactory {
|
|||||||
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
|
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StreamDecrypter} for decrypting an invitation stream.
|
* Creates a {@link StreamDecrypter} for decrypting a contact exchange
|
||||||
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamDecrypter createInvitationStreamDecrypter(InputStream in,
|
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ public interface StreamEncrypterFactory {
|
|||||||
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
|
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StreamEncrypter} for encrypting an invitation stream.
|
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
||||||
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class Author {
|
public class Author {
|
||||||
|
|
||||||
public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES}
|
public enum Status {
|
||||||
|
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
|
||||||
|
}
|
||||||
|
|
||||||
private final AuthorId id;
|
private final AuthorId id;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
public interface InvitationConstants {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection timeout in milliseconds.
|
|
||||||
*/
|
|
||||||
long CONNECTION_TIMEOUT = 60 * 1000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The confirmation timeout in milliseconds.
|
|
||||||
*/
|
|
||||||
long CONFIRMATION_TIMEOUT = 60 * 1000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of bits in an invitation or confirmation code. Codes must fit
|
|
||||||
* into six decimal digits.
|
|
||||||
*/
|
|
||||||
int CODE_BITS = 19;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for receiving updates about the state of an
|
|
||||||
* {@link InvitationTask}.
|
|
||||||
*/
|
|
||||||
public interface InvitationListener {
|
|
||||||
|
|
||||||
/** Called if a connection to the remote peer is established. */
|
|
||||||
void connectionSucceeded();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if a connection to the remote peer cannot be established. This
|
|
||||||
* indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void connectionFailed();
|
|
||||||
|
|
||||||
/** Called if key agreement with the remote peer succeeds. */
|
|
||||||
void keyAgreementSucceeded(int localCode, int remoteCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if key agreement with the remote peer fails or the connection is
|
|
||||||
* lost. This indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void keyAgreementFailed();
|
|
||||||
|
|
||||||
/** Called if the remote peer's confirmation check succeeds. */
|
|
||||||
void remoteConfirmationSucceeded();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if remote peer's confirmation check fails or the connection is
|
|
||||||
* lost. This indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void remoteConfirmationFailed();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if the exchange of pseudonyms succeeds. This indicates that the
|
|
||||||
* protocol has ended successfully.
|
|
||||||
*/
|
|
||||||
void pseudonymExchangeSucceeded(String remoteName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if the exchange of pseudonyms fails or the connection is lost.
|
|
||||||
* This indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void pseudonymExchangeFailed();
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A snapshot of the state of an {@link InvitationTask}.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class InvitationState {
|
|
||||||
|
|
||||||
private final int localInvitationCode, remoteInvitationCode;
|
|
||||||
private final int localConfirmationCode, remoteConfirmationCode;
|
|
||||||
private final boolean connected, connectionFailed;
|
|
||||||
private final boolean localCompared, remoteCompared;
|
|
||||||
private final boolean localMatched, remoteMatched;
|
|
||||||
@Nullable
|
|
||||||
private final String contactName;
|
|
||||||
|
|
||||||
public InvitationState(int localInvitationCode, int remoteInvitationCode,
|
|
||||||
int localConfirmationCode, int remoteConfirmationCode,
|
|
||||||
boolean connected, boolean connectionFailed, boolean localCompared,
|
|
||||||
boolean remoteCompared, boolean localMatched,
|
|
||||||
boolean remoteMatched, @Nullable String contactName) {
|
|
||||||
this.localInvitationCode = localInvitationCode;
|
|
||||||
this.remoteInvitationCode = remoteInvitationCode;
|
|
||||||
this.localConfirmationCode = localConfirmationCode;
|
|
||||||
this.remoteConfirmationCode = remoteConfirmationCode;
|
|
||||||
this.connected = connected;
|
|
||||||
this.connectionFailed = connectionFailed;
|
|
||||||
this.localCompared = localCompared;
|
|
||||||
this.remoteCompared = remoteCompared;
|
|
||||||
this.localMatched = localMatched;
|
|
||||||
this.remoteMatched = remoteMatched;
|
|
||||||
this.contactName = contactName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalInvitationCode() {
|
|
||||||
return localInvitationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemoteInvitationCode() {
|
|
||||||
return remoteInvitationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalConfirmationCode() {
|
|
||||||
return localConfirmationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemoteConfirmationCode() {
|
|
||||||
return remoteConfirmationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getConnected() {
|
|
||||||
return connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getConnectionFailed() {
|
|
||||||
return connectionFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getLocalCompared() {
|
|
||||||
return localCompared;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getRemoteCompared() {
|
|
||||||
return remoteCompared;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getLocalMatched() {
|
|
||||||
return localMatched;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getRemoteMatched() {
|
|
||||||
return remoteMatched;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String getContactName() {
|
|
||||||
return contactName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A task for exchanging invitations with a remote peer.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface InvitationTask {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener to be informed of state changes and returns the
|
|
||||||
* task's current state.
|
|
||||||
*/
|
|
||||||
InvitationState addListener(InvitationListener l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given listener.
|
|
||||||
*/
|
|
||||||
void removeListener(InvitationListener l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously starts the connection process.
|
|
||||||
*/
|
|
||||||
void connect();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously informs the remote peer that the local peer's
|
|
||||||
* confirmation codes matched.
|
|
||||||
*/
|
|
||||||
void localConfirmationSucceeded();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously informs the remote peer that the local peer's
|
|
||||||
* confirmation codes did not match.
|
|
||||||
*/
|
|
||||||
void localConfirmationFailed();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates tasks for exchanging invitations with remote peers.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface InvitationTaskFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a task using the given local and remote invitation codes.
|
|
||||||
*/
|
|
||||||
InvitationTask createTask(int localCode, int remoteCode);
|
|
||||||
}
|
|
||||||
@@ -9,4 +9,5 @@ public interface BluetoothConstants {
|
|||||||
String PROP_ADDRESS = "address";
|
String PROP_ADDRESS = "address";
|
||||||
String PROP_UUID = "uuid";
|
String PROP_UUID = "uuid";
|
||||||
|
|
||||||
|
String PREF_BT_ENABLE = "enable";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ public interface LanTcpConstants {
|
|||||||
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.lan");
|
TransportId ID = new TransportId("org.briarproject.bramble.lan");
|
||||||
|
|
||||||
|
String PREF_LAN_IP_PORTS = "ipPorts";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ public interface PluginManager {
|
|||||||
*/
|
*/
|
||||||
Collection<DuplexPlugin> getDuplexPlugins();
|
Collection<DuplexPlugin> getDuplexPlugins();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns any duplex plugins that support invitations.
|
|
||||||
*/
|
|
||||||
Collection<DuplexPlugin> getInvitationPlugins();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns any duplex plugins that support key agreement.
|
* Returns any duplex plugins that support key agreement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,4 +8,12 @@ public interface TorConstants {
|
|||||||
int CONTROL_PORT = 59051;
|
int CONTROL_PORT = 59051;
|
||||||
|
|
||||||
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||||
|
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
|
||||||
|
|
||||||
|
String PREF_TOR_NETWORK = "network";
|
||||||
|
String PREF_TOR_PORT = "port";
|
||||||
|
|
||||||
|
int PREF_TOR_NETWORK_NEVER = 0;
|
||||||
|
int PREF_TOR_NETWORK_WIFI = 1;
|
||||||
|
int PREF_TOR_NETWORK_ALWAYS = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.plugin.duplex;
|
package org.briarproject.bramble.api.plugin.duplex;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -23,20 +22,6 @@ public interface DuplexPlugin extends Plugin {
|
|||||||
@Nullable
|
@Nullable
|
||||||
DuplexTransportConnection createConnection(ContactId c);
|
DuplexTransportConnection createConnection(ContactId c);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the plugin supports exchanging invitations.
|
|
||||||
*/
|
|
||||||
boolean supportsInvitations();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to create and return an invitation connection to the remote
|
|
||||||
* peer. Returns null if no connection can be established within the given
|
|
||||||
* time.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the plugin supports short-range key agreement.
|
* Returns true if the plugin supports short-range key agreement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.bramble.api.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a platform-specific secure random number generator.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface SecureRandomProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Provider} that provides a strong {@link SecureRandom}
|
||||||
|
* implementation, or null if the platform's default implementation should
|
||||||
|
* be used.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Provider getProvider();
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses a platform-specific source to provide a seed for a pseudo-random
|
|
||||||
* number generator.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface SeedProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the seed in bytes.
|
|
||||||
*/
|
|
||||||
int SEED_BYTES = 32;
|
|
||||||
|
|
||||||
byte[] getSeed();
|
|
||||||
}
|
|
||||||
@@ -15,9 +15,9 @@ public interface StreamReaderFactory {
|
|||||||
InputStream createStreamReader(InputStream in, StreamContext ctx);
|
InputStream createStreamReader(InputStream in, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link InputStream InputStream} for reading from an
|
* Creates an {@link InputStream InputStream} for reading from a contact
|
||||||
* invitation stream.
|
* exchangestream.
|
||||||
*/
|
*/
|
||||||
InputStream createInvitationStreamReader(InputStream in,
|
InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public interface StreamWriterFactory {
|
|||||||
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
|
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link OutputStream OutputStream} for writing to an
|
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
||||||
* invitation stream.
|
* exchange stream.
|
||||||
*/
|
*/
|
||||||
OutputStream createInvitationStreamWriter(OutputStream out,
|
OutputStream createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import org.briarproject.bramble.api.crypto.SecretKey;
|
|||||||
|
|
||||||
public interface TransportConstants {
|
public interface TransportConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the transport protocol.
|
||||||
|
*/
|
||||||
|
int PROTOCOL_VERSION = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the pseudo-random tag in bytes.
|
* The length of the pseudo-random tag in bytes.
|
||||||
*/
|
*/
|
||||||
@@ -14,21 +19,22 @@ public interface TransportConstants {
|
|||||||
*/
|
*/
|
||||||
int STREAM_HEADER_NONCE_LENGTH = 24;
|
int STREAM_HEADER_NONCE_LENGTH = 24;
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the stream header initialisation vector (IV) in bytes.
|
|
||||||
*/
|
|
||||||
int STREAM_HEADER_IV_LENGTH = STREAM_HEADER_NONCE_LENGTH - 8;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the message authentication code (MAC) in bytes.
|
* The length of the message authentication code (MAC) in bytes.
|
||||||
*/
|
*/
|
||||||
int MAC_LENGTH = 16;
|
int MAC_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the stream header plaintext in bytes. The stream header
|
||||||
|
* contains the protocol version, stream number and frame key.
|
||||||
|
*/
|
||||||
|
int STREAM_HEADER_PLAINTEXT_LENGTH = 2 + 8 + SecretKey.LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the stream header in bytes.
|
* The length of the stream header in bytes.
|
||||||
*/
|
*/
|
||||||
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
|
int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
|
||||||
+ MAC_LENGTH;
|
+ STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the frame nonce in bytes.
|
* The length of the frame nonce in bytes.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.io.File;
|
|||||||
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.net.Socket;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -59,4 +60,24 @@ public class IoUtils {
|
|||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for a bug in Android 7, see
|
||||||
|
// https://android-review.googlesource.com/#/c/271775/
|
||||||
|
public static InputStream getInputStream(Socket s) throws IOException {
|
||||||
|
try {
|
||||||
|
return s.getInputStream();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for a bug in Android 7, see
|
||||||
|
// https://android-review.googlesource.com/#/c/271775/
|
||||||
|
public static OutputStream getOutputStream(Socket s) throws IOException {
|
||||||
|
try {
|
||||||
|
return s.getOutputStream();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class PrivacyUtils {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String scrubMacAddress(@Nullable String address) {
|
public static String scrubMacAddress(@Nullable String address) {
|
||||||
if (address == null) return null;
|
if (address == null || address.length() == 0) return null;
|
||||||
// this is a fake address we need to know about
|
// this is a fake address we need to know about
|
||||||
if (address.equals("02:00:00:00:00:00")) return address;
|
if (address.equals("02:00:00:00:00:00")) return address;
|
||||||
// keep first and last octet of MAC address
|
// keep first and last octet of MAC address
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id 'java'
|
||||||
id "net.ltgt.apt" version "0.9"
|
id 'net.ltgt.apt' version '0.9'
|
||||||
id "idea"
|
id 'idea'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = 1.6
|
sourceCompatibility = 1.6
|
||||||
targetCompatibility = 1.6
|
targetCompatibility = 1.6
|
||||||
|
|
||||||
@@ -10,17 +11,18 @@ apply plugin: 'witness'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-api')
|
compile project(':bramble-api')
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
compile 'com.madgag.spongycastle:core:1.56.0.0'
|
||||||
compile 'com.madgag.spongycastle:core:1.54.0.0'
|
compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
|
||||||
compile 'com.h2database:h2:1.4.190'
|
compile 'org.bitlet:weupnp:0.1.4'
|
||||||
|
|
||||||
testCompile project(path: ':bramble-api', configuration: 'testOutput')
|
testCompile project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
|
'com.madgag.spongycastle:core:5e791b0eaa9e0c4594231b44f616a52adddb7dccedeb0ad9ad74887e19499a23',
|
||||||
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c'
|
'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||||
|
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.invitation.InvitationModule;
|
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
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;
|
||||||
@@ -32,7 +31,6 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
InvitationModule.class,
|
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
@@ -54,6 +52,7 @@ public class BrambleCoreModule {
|
|||||||
c.inject(new IdentityModule.EagerSingletons());
|
c.inject(new IdentityModule.EagerSingletons());
|
||||||
c.inject(new LifecycleModule.EagerSingletons());
|
c.inject(new LifecycleModule.EagerSingletons());
|
||||||
c.inject(new PluginModule.EagerSingletons());
|
c.inject(new PluginModule.EagerSingletons());
|
||||||
|
c.inject(new PropertiesModule.EagerSingletons());
|
||||||
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());
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Executor} that delegates its tasks to another {@link Executor}
|
||||||
|
* while limiting the number of tasks that are delegated concurrently. Tasks
|
||||||
|
* are delegated in the order they are submitted to this executor.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public class PoliteExecutor implements Executor {
|
||||||
|
|
||||||
|
private static final Level LOG_LEVEL = FINE;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Queue<Runnable> queue = new LinkedList<Runnable>();
|
||||||
|
private final Executor delegate;
|
||||||
|
private final int maxConcurrentTasks;
|
||||||
|
private final Logger log;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private int concurrentTasks = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag the tag to be used for logging
|
||||||
|
* @param delegate the executor to which tasks will be delegated
|
||||||
|
* @param maxConcurrentTasks the maximum number of tasks that will be
|
||||||
|
* delegated concurrently. If this is set to 1, tasks submitted to this
|
||||||
|
* executor will run in the order they are submitted and will not run
|
||||||
|
* concurrently
|
||||||
|
*/
|
||||||
|
public PoliteExecutor(String tag, Executor delegate,
|
||||||
|
int maxConcurrentTasks) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.maxConcurrentTasks = maxConcurrentTasks;
|
||||||
|
log = Logger.getLogger(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final Runnable r) {
|
||||||
|
final long submitted = System.currentTimeMillis();
|
||||||
|
Runnable wrapped = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (log.isLoggable(LOG_LEVEL)) {
|
||||||
|
long queued = System.currentTimeMillis() - submitted;
|
||||||
|
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
r.run();
|
||||||
|
} finally {
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
synchronized (lock) {
|
||||||
|
if (concurrentTasks < maxConcurrentTasks) {
|
||||||
|
concurrentTasks++;
|
||||||
|
delegate.execute(wrapped);
|
||||||
|
} else {
|
||||||
|
queue.add(wrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleNext() {
|
||||||
|
synchronized (lock) {
|
||||||
|
Runnable next = queue.poll();
|
||||||
|
if (next == null) concurrentTasks--;
|
||||||
|
else delegate.execute(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.RejectedExecutionHandler;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class TimeLoggingExecutor extends ThreadPoolExecutor {
|
||||||
|
|
||||||
|
private static final Level LOG_LEVEL = FINE;
|
||||||
|
|
||||||
|
private final Logger log;
|
||||||
|
|
||||||
|
public TimeLoggingExecutor(String tag, int corePoolSize, int maxPoolSize,
|
||||||
|
long keepAliveTime, TimeUnit unit,
|
||||||
|
BlockingQueue<Runnable> workQueue,
|
||||||
|
RejectedExecutionHandler handler) {
|
||||||
|
super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue,
|
||||||
|
handler);
|
||||||
|
log = Logger.getLogger(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final Runnable r) {
|
||||||
|
if (log.isLoggable(LOG_LEVEL)) {
|
||||||
|
final long submitted = System.currentTimeMillis();
|
||||||
|
super.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long started = System.currentTimeMillis();
|
||||||
|
long queued = started - submitted;
|
||||||
|
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
||||||
|
r.run();
|
||||||
|
long executing = System.currentTimeMillis() - started;
|
||||||
|
log.log(LOG_LEVEL, "Execution time " + executing + " ms");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
super.execute(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
private volatile boolean alice;
|
private volatile boolean alice;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ContactExchangeTaskImpl(DatabaseComponent db,
|
ContactExchangeTaskImpl(DatabaseComponent db,
|
||||||
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
|
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
|
||||||
BdfWriterFactory bdfWriterFactory, Clock clock,
|
BdfWriterFactory bdfWriterFactory, Clock clock,
|
||||||
ConnectionManager connectionManager, ContactManager contactManager,
|
ConnectionManager connectionManager, ContactManager contactManager,
|
||||||
@@ -146,12 +146,12 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
|
|
||||||
// Create the readers
|
// Create the readers
|
||||||
InputStream streamReader =
|
InputStream streamReader =
|
||||||
streamReaderFactory.createInvitationStreamReader(in,
|
streamReaderFactory.createContactExchangeStreamReader(in,
|
||||||
alice ? bobHeaderKey : aliceHeaderKey);
|
alice ? bobHeaderKey : aliceHeaderKey);
|
||||||
BdfReader r = bdfReaderFactory.createReader(streamReader);
|
BdfReader r = bdfReaderFactory.createReader(streamReader);
|
||||||
// Create the writers
|
// Create the writers
|
||||||
OutputStream streamWriter =
|
OutputStream streamWriter =
|
||||||
streamWriterFactory.createInvitationStreamWriter(out,
|
streamWriterFactory.createContactExchangeStreamWriter(out,
|
||||||
alice ? aliceHeaderKey : bobHeaderKey);
|
alice ? aliceHeaderKey : bobHeaderKey);
|
||||||
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.SecureRandomSpi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link SecureRandom} implementation that combines the outputs of two or
|
|
||||||
* more other implementations using XOR.
|
|
||||||
*/
|
|
||||||
class CombinedSecureRandom extends SecureRandom {
|
|
||||||
|
|
||||||
private static final Provider PROVIDER = new CombinedProvider();
|
|
||||||
|
|
||||||
CombinedSecureRandom(SecureRandom... randoms) {
|
|
||||||
super(new CombinedSecureRandomSpi(randoms), PROVIDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CombinedSecureRandomSpi extends SecureRandomSpi {
|
|
||||||
|
|
||||||
private final SecureRandom[] randoms;
|
|
||||||
|
|
||||||
private CombinedSecureRandomSpi(SecureRandom... randoms) {
|
|
||||||
if (randoms.length < 2) throw new IllegalArgumentException();
|
|
||||||
this.randoms = randoms;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] engineGenerateSeed(int numBytes) {
|
|
||||||
byte[] combined = new byte[numBytes];
|
|
||||||
for (SecureRandom random : randoms) {
|
|
||||||
byte[] b = random.generateSeed(numBytes);
|
|
||||||
int length = Math.min(numBytes, b.length);
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
combined[i] = (byte) (combined[i] ^ b[i]);
|
|
||||||
}
|
|
||||||
return combined;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineNextBytes(byte[] b) {
|
|
||||||
byte[] temp = new byte[b.length];
|
|
||||||
for (SecureRandom random : randoms) {
|
|
||||||
random.nextBytes(temp);
|
|
||||||
for (int i = 0; i < b.length; i++)
|
|
||||||
b[i] = (byte) (b[i] ^ temp[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineSetSeed(byte[] seed) {
|
|
||||||
for (SecureRandom random : randoms) random.setSeed(seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CombinedProvider extends Provider {
|
|
||||||
|
|
||||||
private CombinedProvider() {
|
|
||||||
super("Combined", 1.0, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,10 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
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.PseudoRandom;
|
|
||||||
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.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
@@ -29,7 +28,10 @@ import org.spongycastle.crypto.params.KeyParameter;
|
|||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,12 +40,13 @@ 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.INFO;
|
||||||
import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_BITS;
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
@@ -63,9 +66,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return s.getBytes(Charset.forName("US-ASCII"));
|
return s.getBytes(Charset.forName("US-ASCII"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// KDF labels for bluetooth confirmation code derivation
|
|
||||||
private static final byte[] BT_A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
|
|
||||||
private static final byte[] BT_B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
|
|
||||||
// KDF labels for contact exchange stream header key derivation
|
// KDF labels for contact exchange stream header key derivation
|
||||||
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
|
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
|
||||||
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
|
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
|
||||||
@@ -101,16 +101,26 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SeedProvider seedProvider) {
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||||
if (!FortunaSecureRandom.selfTest()) throw new RuntimeException();
|
|
||||||
SecureRandom platformSecureRandom = new SecureRandom();
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
String provider = platformSecureRandom.getProvider().getName();
|
SecureRandom defaultSecureRandom = new SecureRandom();
|
||||||
String algorithm = platformSecureRandom.getAlgorithm();
|
String name = defaultSecureRandom.getProvider().getName();
|
||||||
LOG.info("Default SecureRandom: " + provider + " " + algorithm);
|
String algorithm = defaultSecureRandom.getAlgorithm();
|
||||||
|
LOG.info("Default SecureRandom: " + name + " " + algorithm);
|
||||||
}
|
}
|
||||||
SecureRandom fortuna = new FortunaSecureRandom(seedProvider.getSeed());
|
Provider provider = secureRandomProvider.getProvider();
|
||||||
secureRandom = new CombinedSecureRandom(platformSecureRandom, fortuna);
|
if (provider == null) {
|
||||||
|
LOG.info("Using default");
|
||||||
|
} else {
|
||||||
|
installSecureRandomProvider(provider);
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
SecureRandom installedSecureRandom = new SecureRandom();
|
||||||
|
String name = installedSecureRandom.getProvider().getName();
|
||||||
|
String algorithm = installedSecureRandom.getAlgorithm();
|
||||||
|
LOG.info("Installed SecureRandom: " + name + " " + algorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
secureRandom = new SecureRandom();
|
||||||
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||||
PARAMETERS, secureRandom);
|
PARAMETERS, secureRandom);
|
||||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
@@ -124,6 +134,31 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private void installSecureRandomProvider(Provider provider) {
|
||||||
|
Provider[] providers = Security.getProviders("SecureRandom.SHA1PRNG");
|
||||||
|
if (providers == null || providers.length == 0
|
||||||
|
|| !provider.getClass().equals(providers[0].getClass())) {
|
||||||
|
Security.insertProviderAt(provider, 1);
|
||||||
|
}
|
||||||
|
// Check the new provider is the default when no algorithm is specified
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
if (!provider.getClass().equals(random.getProvider().getClass())) {
|
||||||
|
throw new SecurityException("Wrong SecureRandom provider: "
|
||||||
|
+ random.getProvider().getClass());
|
||||||
|
}
|
||||||
|
// Check the new provider is the default when SHA1PRNG is specified
|
||||||
|
try {
|
||||||
|
random = SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
if (!provider.getClass().equals(random.getProvider().getClass())) {
|
||||||
|
throw new SecurityException("Wrong SHA1PRNG provider: "
|
||||||
|
+ random.getProvider().getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
byte[] b = new byte[SecretKey.LENGTH];
|
byte[] b = new byte[SecretKey.LENGTH];
|
||||||
@@ -131,11 +166,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new SecretKey(b);
|
return new SecretKey(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
|
||||||
return new PseudoRandomImpl(seed1, seed2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecureRandom getSecureRandom() {
|
public SecureRandom getSecureRandom() {
|
||||||
return secureRandom;
|
return secureRandom;
|
||||||
@@ -207,20 +237,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return messageEncrypter.getKeyParser();
|
return messageEncrypter.getKeyParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int generateBTInvitationCode() {
|
|
||||||
int codeBytes = (CODE_BITS + 7) / 8;
|
|
||||||
byte[] random = new byte[codeBytes];
|
|
||||||
secureRandom.nextBytes(random);
|
|
||||||
return ByteUtils.readUint(random, CODE_BITS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
|
|
||||||
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
|
|
||||||
return ByteUtils.readUint(b, CODE_BITS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveHeaderKey(SecretKey master,
|
public SecretKey deriveHeaderKey(SecretKey master,
|
||||||
boolean alice) {
|
boolean alice) {
|
||||||
@@ -296,7 +312,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||||
return deriveMasterSecret(deriveSharedSecret(
|
return deriveMasterSecret(deriveSharedSecret(
|
||||||
theirPublicKey,ourKeyPair, alice));
|
theirPublicKey, ourKeyPair, alice));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -371,8 +387,11 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
|
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
|
long streamNumber) {
|
||||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
|
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
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
|
||||||
@@ -380,10 +399,14 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// 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();
|
||||||
// The input is the stream number as a 64-bit integer
|
// The input is the protocol version as a 16-bit integer, followed by
|
||||||
byte[] input = new byte[INT_64_BYTES];
|
// the stream number as a 64-bit integer
|
||||||
ByteUtils.writeUint64(streamNumber, input, 0);
|
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||||
prf.update(input, 0, input.length);
|
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||||
|
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||||
|
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||||
|
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||||
|
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||||
byte[] mac = new byte[macLength];
|
byte[] mac = new byte[macLength];
|
||||||
prf.doFinal(mac, 0);
|
prf.doFinal(mac, 0);
|
||||||
// The output is the first TAG_LENGTH bytes of the MAC
|
// The output is the first TAG_LENGTH bytes of the MAC
|
||||||
@@ -607,7 +630,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long sampleRunningTime(int iterations) {
|
private long sampleRunningTime(int iterations) {
|
||||||
byte[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
|
byte[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
int keyLengthInBits = SecretKey.LENGTH * 8;
|
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
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.CryptoExecutor;
|
||||||
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.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
@@ -31,14 +32,17 @@ public class CryptoModule {
|
|||||||
public static class EagerSingletons {
|
public static class EagerSingletons {
|
||||||
@Inject
|
@Inject
|
||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
Executor cryptoExecutor;
|
ExecutorService cryptoExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of executor threads.
|
* 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 =
|
private static final int MAX_EXECUTOR_THREADS =
|
||||||
Runtime.getRuntime().availableProcessors();
|
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||||
|
|
||||||
private final ExecutorService cryptoExecutor;
|
private final ExecutorService cryptoExecutor;
|
||||||
|
|
||||||
@@ -49,8 +53,8 @@ public class CryptoModule {
|
|||||||
RejectedExecutionHandler policy =
|
RejectedExecutionHandler policy =
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
// Create a limited # of threads and keep them in the pool for 60 secs
|
// Create a limited # of threads and keep them in the pool for 60 secs
|
||||||
cryptoExecutor = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
|
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
|
||||||
60, SECONDS, queue, policy);
|
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -60,8 +64,9 @@ public class CryptoModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
CryptoComponent provideCryptoComponent(SeedProvider seedProvider) {
|
CryptoComponent provideCryptoComponent(
|
||||||
return new CryptoComponentImpl(seedProvider);
|
SecureRandomProvider secureRandomProvider) {
|
||||||
|
return new CryptoComponentImpl(secureRandomProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -84,11 +89,18 @@ public class CryptoModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
Executor getCryptoExecutor(LifecycleManager lifecycleManager) {
|
ExecutorService getCryptoExecutorService(
|
||||||
|
LifecycleManager lifecycleManager) {
|
||||||
lifecycleManager.registerForShutdown(cryptoExecutor);
|
lifecycleManager.registerForShutdown(cryptoExecutor);
|
||||||
return 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,76 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.spongycastle.crypto.Digest;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message digest that prevents length extension attacks - see Ferguson and
|
|
||||||
* Schneier, <i>Practical Cryptography</i>, chapter 6.
|
|
||||||
* <p>
|
|
||||||
* "Let h be an interative hash function. The hash function h<sub>d</sub> is
|
|
||||||
* defined by h<sub>d</sub> := h(h(m)), and has a claimed security level of
|
|
||||||
* min(k, n/2) where k is the security level of h and n is the size of the hash
|
|
||||||
* result."
|
|
||||||
*/
|
|
||||||
@NotThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class DoubleDigest implements Digest {
|
|
||||||
|
|
||||||
private final Digest delegate;
|
|
||||||
|
|
||||||
DoubleDigest(Digest delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] digest() {
|
|
||||||
byte[] digest = new byte[delegate.getDigestSize()];
|
|
||||||
delegate.doFinal(digest, 0); // h(m)
|
|
||||||
delegate.update(digest, 0, digest.length);
|
|
||||||
delegate.doFinal(digest, 0); // h(h(m))
|
|
||||||
return digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int digest(byte[] buf, int offset, int len) {
|
|
||||||
byte[] digest = digest();
|
|
||||||
len = Math.min(len, digest.length);
|
|
||||||
System.arraycopy(digest, 0, buf, offset, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDigestSize() {
|
|
||||||
return delegate.getDigestSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAlgorithmName() {
|
|
||||||
return "Double " + delegate.getAlgorithmName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
delegate.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte input) {
|
|
||||||
delegate.update(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(byte[] input) {
|
|
||||||
delegate.update(input, 0, input.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte[] input, int offset, int len) {
|
|
||||||
delegate.update(input, offset, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int doFinal(byte[] out, int outOff) {
|
|
||||||
return digest(out, outOff, delegate.getDigestSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements the Fortuna pseudo-random number generator, as described in
|
|
||||||
* Ferguson and Schneier, <i>Practical Cryptography</i>, chapter 9.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class FortunaGenerator {
|
|
||||||
|
|
||||||
private static final int MAX_BYTES_PER_REQUEST = 1024 * 1024;
|
|
||||||
private static final int KEY_BYTES = 32;
|
|
||||||
private static final int BLOCK_BYTES = 16;
|
|
||||||
|
|
||||||
private final Lock lock = new ReentrantLock();
|
|
||||||
|
|
||||||
// The following are locking: lock
|
|
||||||
private final DoubleDigest digest = new DoubleDigest(new SHA256Digest());
|
|
||||||
private final BlockCipher cipher = new AESLightEngine();
|
|
||||||
private final byte[] key = new byte[KEY_BYTES];
|
|
||||||
private final byte[] counter = new byte[BLOCK_BYTES];
|
|
||||||
private final byte[] buffer = new byte[BLOCK_BYTES];
|
|
||||||
private final byte[] newKey = new byte[KEY_BYTES];
|
|
||||||
|
|
||||||
FortunaGenerator(byte[] seed) {
|
|
||||||
reseed(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reseed(byte[] seed) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, KEY_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
void incrementCounter() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
counter[0]++;
|
|
||||||
for (int i = 0; counter[i] == 0; i++) {
|
|
||||||
if (i + 1 == BLOCK_BYTES)
|
|
||||||
throw new RuntimeException("Counter exhausted");
|
|
||||||
counter[i + 1]++;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
byte[] getCounter() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return counter;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int nextBytes(byte[] dest, int off, int len) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
// Don't write more than the maximum number of bytes in one request
|
|
||||||
if (len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
|
|
||||||
cipher.init(true, new KeyParameter(key));
|
|
||||||
// Generate full blocks directly into the output buffer
|
|
||||||
int fullBlocks = len / BLOCK_BYTES;
|
|
||||||
for (int i = 0; i < fullBlocks; i++) {
|
|
||||||
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
}
|
|
||||||
// Generate a partial block if needed
|
|
||||||
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
|
|
||||||
if (remaining >= BLOCK_BYTES) throw new AssertionError();
|
|
||||||
if (remaining > 0) {
|
|
||||||
cipher.processBlock(counter, 0, buffer, 0);
|
|
||||||
incrementCounter();
|
|
||||||
// Copy the partial block to the output buffer and erase our copy
|
|
||||||
System.arraycopy(buffer, 0, dest, off + done, remaining);
|
|
||||||
for (int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
|
|
||||||
}
|
|
||||||
// Generate a new key
|
|
||||||
for (int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
|
|
||||||
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
}
|
|
||||||
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
|
|
||||||
for (int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
|
|
||||||
// Return the number of bytes written
|
|
||||||
return len;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.SecureRandomSpi;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link java.security.SecureRandom SecureRandom} implementation based on a
|
|
||||||
* {@link FortunaGenerator}.
|
|
||||||
*/
|
|
||||||
class FortunaSecureRandom extends SecureRandom {
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
static final byte[] SELF_TEST_VECTOR_1 =
|
|
||||||
StringUtils.fromHexString("4BD6EA599D47E3EE9DD911833C29CA22");
|
|
||||||
static final byte[] SELF_TEST_VECTOR_2 =
|
|
||||||
StringUtils.fromHexString("10984D576E6850E505CA9F42A9BFD88A");
|
|
||||||
static final byte[] SELF_TEST_VECTOR_3 =
|
|
||||||
StringUtils.fromHexString("1E12DA166BD86DCECDE50A8296018DE2");
|
|
||||||
|
|
||||||
private static final Provider PROVIDER = new FortunaProvider();
|
|
||||||
|
|
||||||
FortunaSecureRandom(byte[] seed) {
|
|
||||||
super(new FortunaSecureRandomSpi(seed), PROVIDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that the {@link #nextBytes(byte[])} and {@link #setSeed(byte[])}
|
|
||||||
* methods are passed through to the generator in the expected way.
|
|
||||||
*/
|
|
||||||
static boolean selfTest() {
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
SecureRandom r = new FortunaSecureRandom(seed);
|
|
||||||
byte[] output = new byte[16];
|
|
||||||
r.nextBytes(output);
|
|
||||||
if (!Arrays.equals(SELF_TEST_VECTOR_1, output)) return false;
|
|
||||||
r.nextBytes(output);
|
|
||||||
if (!Arrays.equals(SELF_TEST_VECTOR_2, output)) return false;
|
|
||||||
r.setSeed(seed);
|
|
||||||
r.nextBytes(output);
|
|
||||||
return Arrays.equals(SELF_TEST_VECTOR_3, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FortunaSecureRandomSpi extends SecureRandomSpi {
|
|
||||||
|
|
||||||
private final FortunaGenerator generator;
|
|
||||||
|
|
||||||
private FortunaSecureRandomSpi(byte[] seed) {
|
|
||||||
generator = new FortunaGenerator(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] engineGenerateSeed(int numBytes) {
|
|
||||||
byte[] b = new byte[numBytes];
|
|
||||||
engineNextBytes(b);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineNextBytes(byte[] b) {
|
|
||||||
int offset = 0;
|
|
||||||
while (offset < b.length)
|
|
||||||
offset += generator.nextBytes(b, offset, b.length - offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineSetSeed(byte[] seed) {
|
|
||||||
generator.reseed(seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FortunaProvider extends Provider {
|
|
||||||
|
|
||||||
private FortunaProvider() {
|
|
||||||
super("Fortuna", 1.0, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,31 +11,14 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
|
class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
|
||||||
|
|
||||||
private static final int LOWER = 26;
|
// The minimum number of unique characters in a strong password
|
||||||
private static final int UPPER = 26;
|
private static final int STRONG_UNIQUE_CHARS = 12;
|
||||||
private static final int DIGIT = 10;
|
|
||||||
private static final int OTHER = 10;
|
|
||||||
private static final double STRONG = Math.log(Math.pow(LOWER + UPPER +
|
|
||||||
DIGIT + OTHER, 10));
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float estimateStrength(String password) {
|
public float estimateStrength(String password) {
|
||||||
HashSet<Character> unique = new HashSet<Character>();
|
HashSet<Character> unique = new HashSet<Character>();
|
||||||
int length = password.length();
|
int length = password.length();
|
||||||
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
|
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
|
||||||
boolean lower = false, upper = false, digit = false, other = false;
|
return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS);
|
||||||
for (char c : unique) {
|
|
||||||
if (Character.isLowerCase(c)) lower = true;
|
|
||||||
else if (Character.isUpperCase(c)) upper = true;
|
|
||||||
else if (Character.isDigit(c)) digit = true;
|
|
||||||
else other = true;
|
|
||||||
}
|
|
||||||
int alphabetSize = 0;
|
|
||||||
if (lower) alphabetSize += LOWER;
|
|
||||||
if (upper) alphabetSize += UPPER;
|
|
||||||
if (digit) alphabetSize += DIGIT;
|
|
||||||
if (other) alphabetSize += OTHER;
|
|
||||||
double score = Math.log(Math.pow(alphabetSize, unique.size()));
|
|
||||||
return Math.min(1, (float) (score / STRONG));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
|
||||||
|
|
||||||
@NotThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class PseudoRandomImpl implements PseudoRandom {
|
|
||||||
|
|
||||||
private final FortunaGenerator generator;
|
|
||||||
|
|
||||||
PseudoRandomImpl(int seed1, int seed2) {
|
|
||||||
byte[] seed = new byte[INT_32_BYTES * 2];
|
|
||||||
ByteUtils.writeUint32(seed1, seed, 0);
|
|
||||||
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
|
|
||||||
generator = new FortunaGenerator(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] nextBytes(int length) {
|
|
||||||
byte[] b = new byte[length];
|
|
||||||
int offset = 0;
|
|
||||||
while (offset < length) offset += generator.nextBytes(b, offset, length);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,7 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamDecrypter createInvitationStreamDecrypter(InputStream in,
|
public StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -117,7 +119,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
|
|
||||||
private void readStreamHeader() throws IOException {
|
private void readStreamHeader() throws IOException {
|
||||||
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
||||||
byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH];
|
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
|
||||||
// Read the stream header
|
// Read the stream header
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (offset < STREAM_HEADER_LENGTH) {
|
while (offset < STREAM_HEADER_LENGTH) {
|
||||||
@@ -126,21 +128,35 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
if (read == -1) throw new EOFException();
|
if (read == -1) throw new EOFException();
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
// The nonce consists of the stream number followed by the IV
|
// Extract the nonce
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
|
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
|
||||||
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce,
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
|
|
||||||
// Decrypt and authenticate the stream header
|
// Decrypt and authenticate the stream header
|
||||||
try {
|
try {
|
||||||
cipher.init(false, streamHeaderKey, streamHeaderNonce);
|
cipher.init(false, streamHeaderKey, streamHeaderNonce);
|
||||||
int decrypted = cipher.process(streamHeaderCiphertext,
|
int decrypted = cipher.process(streamHeaderCiphertext,
|
||||||
STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH,
|
STREAM_HEADER_NONCE_LENGTH,
|
||||||
|
STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH,
|
||||||
streamHeaderPlaintext, 0);
|
streamHeaderPlaintext, 0);
|
||||||
if (decrypted != SecretKey.LENGTH) throw new RuntimeException();
|
if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
|
||||||
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
frameKey = new SecretKey(streamHeaderPlaintext);
|
// Check the protocol version
|
||||||
|
int receivedProtocolVersion =
|
||||||
|
ByteUtils.readUint16(streamHeaderPlaintext, 0);
|
||||||
|
if (receivedProtocolVersion != PROTOCOL_VERSION)
|
||||||
|
throw new FormatException();
|
||||||
|
// Check the stream number
|
||||||
|
long receivedStreamNumber = ByteUtils.readUint64(streamHeaderPlaintext,
|
||||||
|
INT_16_BYTES);
|
||||||
|
if (receivedStreamNumber != streamNumber) throw new FormatException();
|
||||||
|
// Extract the frame key
|
||||||
|
byte[] frameKeyBytes = new byte[SecretKey.LENGTH];
|
||||||
|
System.arraycopy(streamHeaderPlaintext, INT_16_BYTES + INT_64_BYTES,
|
||||||
|
frameKeyBytes, 0, SecretKey.LENGTH);
|
||||||
|
frameKey = new SecretKey(frameKeyBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,8 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -36,22 +37,22 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber);
|
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
|
||||||
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
|
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
|
||||||
streamHeaderIv, ctx.getHeaderKey(), frameKey);
|
streamHeaderNonce, ctx.getHeaderKey(), frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
public StreamEncrypter createContactExchangeStreamDecrypter(
|
||||||
SecretKey headerKey) {
|
OutputStream out, SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv,
|
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||||
headerKey, frameKey);
|
headerKey, frameKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -33,7 +35,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private final long streamNumber;
|
private final long streamNumber;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] tag;
|
private final byte[] tag;
|
||||||
private final byte[] streamHeaderIv;
|
private final byte[] streamHeaderNonce;
|
||||||
private final byte[] frameNonce, frameHeader;
|
private final byte[] frameNonce, frameHeader;
|
||||||
private final byte[] framePlaintext, frameCiphertext;
|
private final byte[] framePlaintext, frameCiphertext;
|
||||||
|
|
||||||
@@ -41,13 +43,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private boolean writeTag, writeStreamHeader;
|
private boolean writeTag, writeStreamHeader;
|
||||||
|
|
||||||
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
||||||
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv,
|
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
|
||||||
SecretKey streamHeaderKey, SecretKey frameKey) {
|
SecretKey streamHeaderKey, SecretKey frameKey) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.cipher = cipher;
|
this.cipher = cipher;
|
||||||
this.streamNumber = streamNumber;
|
this.streamNumber = streamNumber;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.streamHeaderIv = streamHeaderIv;
|
this.streamHeaderNonce = streamHeaderNonce;
|
||||||
this.streamHeaderKey = streamHeaderKey;
|
this.streamHeaderKey = streamHeaderKey;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
frameNonce = new byte[FRAME_NONCE_LENGTH];
|
frameNonce = new byte[FRAME_NONCE_LENGTH];
|
||||||
@@ -62,6 +64,8 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
@Override
|
@Override
|
||||||
public void writeFrame(byte[] payload, int payloadLength,
|
public void writeFrame(byte[] payload, int payloadLength,
|
||||||
int paddingLength, boolean finalFrame) throws IOException {
|
int paddingLength, boolean finalFrame) throws IOException {
|
||||||
|
if (payloadLength < 0 || paddingLength < 0)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
// Don't allow the frame counter to wrap
|
// Don't allow the frame counter to wrap
|
||||||
@@ -112,22 +116,23 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeStreamHeader() throws IOException {
|
private void writeStreamHeader() throws IOException {
|
||||||
// The nonce consists of the stream number followed by the IV
|
// The header contains the protocol version, stream number and frame key
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
|
||||||
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
|
ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
|
||||||
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES,
|
ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
INT_16_BYTES);
|
||||||
byte[] streamHeaderPlaintext = frameKey.getBytes();
|
System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
|
||||||
|
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
|
||||||
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
||||||
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0,
|
System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
// Encrypt and authenticate the frame key
|
// Encrypt and authenticate the stream header key
|
||||||
try {
|
try {
|
||||||
cipher.init(true, streamHeaderKey, streamHeaderNonce);
|
cipher.init(true, streamHeaderKey, streamHeaderNonce);
|
||||||
int encrypted = cipher.process(streamHeaderPlaintext, 0,
|
int encrypted = cipher.process(streamHeaderPlaintext, 0,
|
||||||
SecretKey.LENGTH, streamHeaderCiphertext,
|
STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
if (encrypted != SecretKey.LENGTH + MAC_LENGTH)
|
if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException badCipher) {
|
} catch (GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
|
|||||||
@@ -70,25 +70,7 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
|
|||||||
byte[] subKey = new byte[SUBKEY_LENGTH];
|
byte[] subKey = new byte[SUBKEY_LENGTH];
|
||||||
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
|
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
|
||||||
|
|
||||||
// Reverse the order of the Poly130 subkey
|
// Clamp the subkey
|
||||||
//
|
|
||||||
// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
|
|
||||||
// subkey for crypto_onetimeauth_poly1305, which interprets it
|
|
||||||
// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
|
|
||||||
// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
|
|
||||||
// where the XSalsa20 output is defined as (r, s, t, ...).
|
|
||||||
//
|
|
||||||
// BC's Poly1305 implementation interprets the subkey as
|
|
||||||
// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
|
|
||||||
// the reference implementation).
|
|
||||||
//
|
|
||||||
// To be NaCl-compatible, we reverse the subkey.
|
|
||||||
System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
|
|
||||||
System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0,
|
|
||||||
SUBKEY_LENGTH / 2);
|
|
||||||
System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2,
|
|
||||||
SUBKEY_LENGTH / 2);
|
|
||||||
// Now we can clamp the correct part of the subkey
|
|
||||||
Poly1305KeyGenerator.clamp(subKey);
|
Poly1305KeyGenerator.clamp(subKey);
|
||||||
|
|
||||||
// Initialize Poly1305 with the subkey
|
// Initialize Poly1305 with the subkey
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
@@ -130,8 +131,14 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
// Don't allow reentrant locking
|
// Don't allow reentrant locking
|
||||||
if (lock.getReadHoldCount() > 0) throw new IllegalStateException();
|
if (lock.getReadHoldCount() > 0) throw new IllegalStateException();
|
||||||
if (lock.getWriteHoldCount() > 0) throw new IllegalStateException();
|
if (lock.getWriteHoldCount() > 0) throw new IllegalStateException();
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
if (readOnly) lock.readLock().lock();
|
if (readOnly) lock.readLock().lock();
|
||||||
else lock.writeLock().lock();
|
else lock.writeLock().lock();
|
||||||
|
if (LOG.isLoggable(FINE)) {
|
||||||
|
long duration = System.currentTimeMillis() - start;
|
||||||
|
if (readOnly) LOG.fine("Waited " + duration + " ms for read lock");
|
||||||
|
else LOG.fine("Waited " + duration + " ms for write lock");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return new Transaction(db.startTransaction(), readOnly);
|
return new Transaction(db.startTransaction(), readOnly);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
@@ -661,7 +668,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
acked.add(m);
|
acked.add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transaction.attach(new MessagesAckedEvent(c, acked));
|
if (acked.size() > 0) {
|
||||||
|
transaction.attach(new MessagesAckedEvent(c, acked));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.db;
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
|
||||||
@@ -36,8 +37,8 @@ public class DatabaseExecutorModule {
|
|||||||
RejectedExecutionHandler policy =
|
RejectedExecutionHandler policy =
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
// Use a single thread and keep it in the pool for 60 secs
|
// Use a single thread and keep it in the pool for 60 secs
|
||||||
databaseExecutor = new ThreadPoolExecutor(0, 1, 60, SECONDS, queue,
|
databaseExecutor = new TimeLoggingExecutor("DatabaseExecutor", 0, 1,
|
||||||
policy);
|
60, SECONDS, queue, policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
private static final int SCHEMA_VERSION = 29;
|
private static final int SCHEMA_VERSION = 30;
|
||||||
private static final int MIN_SCHEMA_VERSION = 29;
|
private static final int MIN_SCHEMA_VERSION = 30;
|
||||||
|
|
||||||
private static final String CREATE_SETTINGS =
|
private static final String CREATE_SETTINGS =
|
||||||
"CREATE TABLE settings"
|
"CREATE TABLE settings"
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
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.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A connection thread for the peer being Alice in the invitation protocol.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
class AliceConnector extends Connector {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(AliceConnector.class.getName());
|
|
||||||
|
|
||||||
AliceConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
|
|
||||||
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
|
|
||||||
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
|
|
||||||
group, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Create an incoming or outgoing connection
|
|
||||||
DuplexTransportConnection conn = createInvitationConnection(true);
|
|
||||||
if (conn == null) return;
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
|
|
||||||
// Don't proceed with more than one connection
|
|
||||||
if (group.getAndSetConnected()) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Carry out the key agreement protocol
|
|
||||||
InputStream in;
|
|
||||||
OutputStream out;
|
|
||||||
BdfReader r;
|
|
||||||
BdfWriter w;
|
|
||||||
SecretKey master;
|
|
||||||
try {
|
|
||||||
in = conn.getReader().getInputStream();
|
|
||||||
out = conn.getWriter().getOutputStream();
|
|
||||||
r = bdfReaderFactory.createReader(in);
|
|
||||||
w = bdfWriterFactory.createWriter(out);
|
|
||||||
// Alice goes first
|
|
||||||
sendPublicKeyHash(w);
|
|
||||||
byte[] hash = receivePublicKeyHash(r);
|
|
||||||
sendPublicKey(w);
|
|
||||||
byte[] key = receivePublicKey(r);
|
|
||||||
master = deriveMasterSecret(hash, key, true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The key agreement succeeded - derive the confirmation codes
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
|
|
||||||
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
|
|
||||||
int bobCode = crypto.deriveBTConfirmationCode(master, false);
|
|
||||||
group.keyAgreementSucceeded(aliceCode, bobCode);
|
|
||||||
// Exchange confirmation results
|
|
||||||
boolean localMatched, remoteMatched;
|
|
||||||
try {
|
|
||||||
localMatched = group.waitForLocalConfirmationResult();
|
|
||||||
sendConfirmation(w, localMatched);
|
|
||||||
remoteMatched = receiveConfirmation(r);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for confirmation");
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (remoteMatched) group.remoteConfirmationSucceeded();
|
|
||||||
else group.remoteConfirmationFailed();
|
|
||||||
if (!(localMatched && remoteMatched)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation failed");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Confirmation succeeded - upgrade to a secure connection
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation succeeded");
|
|
||||||
contactExchangeTask.startExchange(group, localAuthor, master, conn,
|
|
||||||
plugin.getId(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
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.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A connection thread for the peer being Bob in the invitation protocol.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
class BobConnector extends Connector {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(BobConnector.class.getName());
|
|
||||||
|
|
||||||
BobConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
|
|
||||||
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
|
|
||||||
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
|
|
||||||
group, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Create an incoming or outgoing connection
|
|
||||||
DuplexTransportConnection conn = createInvitationConnection(false);
|
|
||||||
if (conn == null) return;
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
|
|
||||||
// Carry out the key agreement protocol
|
|
||||||
InputStream in;
|
|
||||||
OutputStream out;
|
|
||||||
BdfReader r;
|
|
||||||
BdfWriter w;
|
|
||||||
SecretKey master;
|
|
||||||
try {
|
|
||||||
in = conn.getReader().getInputStream();
|
|
||||||
out = conn.getWriter().getOutputStream();
|
|
||||||
r = bdfReaderFactory.createReader(in);
|
|
||||||
w = bdfWriterFactory.createWriter(out);
|
|
||||||
// Alice goes first
|
|
||||||
byte[] hash = receivePublicKeyHash(r);
|
|
||||||
// Don't proceed with more than one connection
|
|
||||||
if (group.getAndSetConnected()) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendPublicKeyHash(w);
|
|
||||||
byte[] key = receivePublicKey(r);
|
|
||||||
sendPublicKey(w);
|
|
||||||
master = deriveMasterSecret(hash, key, false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The key agreement succeeded - derive the confirmation codes
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
|
|
||||||
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
|
|
||||||
int bobCode = crypto.deriveBTConfirmationCode(master, false);
|
|
||||||
group.keyAgreementSucceeded(bobCode, aliceCode);
|
|
||||||
// Exchange confirmation results
|
|
||||||
boolean localMatched, remoteMatched;
|
|
||||||
try {
|
|
||||||
remoteMatched = receiveConfirmation(r);
|
|
||||||
localMatched = group.waitForLocalConfirmationResult();
|
|
||||||
sendConfirmation(w, localMatched);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for confirmation");
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (remoteMatched) group.remoteConfirmationSucceeded();
|
|
||||||
else group.remoteConfirmationFailed();
|
|
||||||
if (!(localMatched && remoteMatched)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation failed");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Confirmation succeeded - upgrade to a secure connection
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation succeeded");
|
|
||||||
contactExchangeTask.startExchange(group, localAuthor, master, conn,
|
|
||||||
plugin.getId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
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.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
|
|
||||||
|
|
||||||
// FIXME: This class has way too many dependencies
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class Connector extends Thread {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(Connector.class.getName());
|
|
||||||
private static final String LABEL_PUBLIC_KEY =
|
|
||||||
"org.briarproject.bramble.invitation.PUBLIC_KEY";
|
|
||||||
|
|
||||||
protected final CryptoComponent crypto;
|
|
||||||
protected final BdfReaderFactory bdfReaderFactory;
|
|
||||||
protected final BdfWriterFactory bdfWriterFactory;
|
|
||||||
protected final ContactExchangeTask contactExchangeTask;
|
|
||||||
protected final ConnectorGroup group;
|
|
||||||
protected final DuplexPlugin plugin;
|
|
||||||
protected final LocalAuthor localAuthor;
|
|
||||||
protected final PseudoRandom random;
|
|
||||||
protected final String pluginName;
|
|
||||||
|
|
||||||
private final KeyPair keyPair;
|
|
||||||
private final KeyParser keyParser;
|
|
||||||
|
|
||||||
Connector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
|
|
||||||
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
|
|
||||||
super("Connector");
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
|
||||||
this.bdfWriterFactory = bdfWriterFactory;
|
|
||||||
this.contactExchangeTask = contactExchangeTask;
|
|
||||||
this.group = group;
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.localAuthor = localAuthor;
|
|
||||||
this.random = random;
|
|
||||||
pluginName = plugin.getClass().getName();
|
|
||||||
keyPair = crypto.generateAgreementKeyPair();
|
|
||||||
keyParser = crypto.getAgreementKeyParser();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
DuplexTransportConnection createInvitationConnection(boolean alice) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " creating invitation connection");
|
|
||||||
return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT,
|
|
||||||
alice);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPublicKeyHash(BdfWriter w) throws IOException {
|
|
||||||
byte[] hash =
|
|
||||||
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
|
|
||||||
w.writeRaw(hash);
|
|
||||||
w.flush();
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent hash");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] receivePublicKeyHash(BdfReader r) throws IOException {
|
|
||||||
int hashLength = crypto.getHashLength();
|
|
||||||
byte[] b = r.readRaw(hashLength);
|
|
||||||
if (b.length < hashLength) throw new FormatException();
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash");
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPublicKey(BdfWriter w) throws IOException {
|
|
||||||
byte[] key = keyPair.getPublic().getEncoded();
|
|
||||||
w.writeRaw(key);
|
|
||||||
w.flush();
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent key");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] receivePublicKey(BdfReader r)
|
|
||||||
throws GeneralSecurityException, IOException {
|
|
||||||
byte[] b = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
|
|
||||||
keyParser.parsePublicKey(b);
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received key");
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
SecretKey deriveMasterSecret(byte[] hash, byte[] key, boolean alice)
|
|
||||||
throws GeneralSecurityException {
|
|
||||||
// Check that the hash matches the key
|
|
||||||
byte[] keyHash =
|
|
||||||
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
|
|
||||||
if (!Arrays.equals(hash, keyHash)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " hash does not match key");
|
|
||||||
throw new GeneralSecurityException();
|
|
||||||
}
|
|
||||||
// Derive the master secret
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " deriving master secret");
|
|
||||||
return crypto.deriveMasterSecret(key, keyPair, alice);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendConfirmation(BdfWriter w, boolean confirmed) throws IOException {
|
|
||||||
w.writeBoolean(confirmed);
|
|
||||||
w.flush();
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " sent confirmation: " + confirmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean receiveConfirmation(BdfReader r) throws IOException {
|
|
||||||
boolean confirmed = r.readBoolean();
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " received confirmation: " + confirmed);
|
|
||||||
return confirmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void tryToClose(DuplexTransportConnection conn,
|
|
||||||
boolean exception) {
|
|
||||||
try {
|
|
||||||
LOG.info("Closing connection");
|
|
||||||
conn.getReader().dispose(exception, true);
|
|
||||||
conn.getWriter().dispose(exception);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeListener;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationListener;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationState;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTask;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONFIRMATION_TIMEOUT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A task consisting of one or more parallel connection attempts.
|
|
||||||
*/
|
|
||||||
@MethodsNotNullByDefault
|
|
||||||
@ParametersNotNullByDefault
|
|
||||||
class ConnectorGroup extends Thread implements InvitationTask,
|
|
||||||
ContactExchangeListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ConnectorGroup.class.getName());
|
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final BdfReaderFactory bdfReaderFactory;
|
|
||||||
private final BdfWriterFactory bdfWriterFactory;
|
|
||||||
private final ContactExchangeTask contactExchangeTask;
|
|
||||||
private final IdentityManager identityManager;
|
|
||||||
private final PluginManager pluginManager;
|
|
||||||
private final int localInvitationCode, remoteInvitationCode;
|
|
||||||
private final Collection<InvitationListener> listeners;
|
|
||||||
private final AtomicBoolean connected;
|
|
||||||
private final CountDownLatch localConfirmationLatch;
|
|
||||||
private final Lock lock = new ReentrantLock();
|
|
||||||
|
|
||||||
// The following are locking: lock
|
|
||||||
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
|
|
||||||
private boolean connectionFailed = false;
|
|
||||||
private boolean localCompared = false, remoteCompared = false;
|
|
||||||
private boolean localMatched = false, remoteMatched = false;
|
|
||||||
private String remoteName = null;
|
|
||||||
|
|
||||||
ConnectorGroup(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask,
|
|
||||||
IdentityManager identityManager, PluginManager pluginManager,
|
|
||||||
int localInvitationCode, int remoteInvitationCode) {
|
|
||||||
super("ConnectorGroup");
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
|
||||||
this.bdfWriterFactory = bdfWriterFactory;
|
|
||||||
this.contactExchangeTask = contactExchangeTask;
|
|
||||||
this.identityManager = identityManager;
|
|
||||||
this.pluginManager = pluginManager;
|
|
||||||
this.localInvitationCode = localInvitationCode;
|
|
||||||
this.remoteInvitationCode = remoteInvitationCode;
|
|
||||||
listeners = new CopyOnWriteArrayList<InvitationListener>();
|
|
||||||
connected = new AtomicBoolean(false);
|
|
||||||
localConfirmationLatch = new CountDownLatch(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InvitationState addListener(InvitationListener l) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
listeners.add(l);
|
|
||||||
return new InvitationState(localInvitationCode,
|
|
||||||
remoteInvitationCode, localConfirmationCode,
|
|
||||||
remoteConfirmationCode, connected.get(), connectionFailed,
|
|
||||||
localCompared, remoteCompared, localMatched, remoteMatched,
|
|
||||||
remoteName);
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListener(InvitationListener l) {
|
|
||||||
listeners.remove(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connect() {
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
LocalAuthor localAuthor;
|
|
||||||
// Load the local pseudonym
|
|
||||||
try {
|
|
||||||
localAuthor = identityManager.getLocalAuthor();
|
|
||||||
} catch (DbException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
connectionFailed = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.connectionFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Start the connection threads
|
|
||||||
Collection<Connector> connectors = new ArrayList<Connector>();
|
|
||||||
// Alice is the party with the smaller invitation code
|
|
||||||
if (localInvitationCode < remoteInvitationCode) {
|
|
||||||
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
|
|
||||||
Connector c = createAliceConnector(plugin, localAuthor);
|
|
||||||
connectors.add(c);
|
|
||||||
c.start();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
|
|
||||||
Connector c = createBobConnector(plugin, localAuthor);
|
|
||||||
connectors.add(c);
|
|
||||||
c.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wait for the connection threads to finish
|
|
||||||
try {
|
|
||||||
for (Connector c : connectors) c.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for connectors");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
// If none of the threads connected, inform the listeners
|
|
||||||
if (!connected.get()) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
connectionFailed = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.connectionFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Connector createAliceConnector(DuplexPlugin plugin,
|
|
||||||
LocalAuthor localAuthor) {
|
|
||||||
PseudoRandom random = crypto.getPseudoRandom(localInvitationCode,
|
|
||||||
remoteInvitationCode);
|
|
||||||
return new AliceConnector(crypto, bdfReaderFactory, bdfWriterFactory,
|
|
||||||
contactExchangeTask, this, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Connector createBobConnector(DuplexPlugin plugin,
|
|
||||||
LocalAuthor localAuthor) {
|
|
||||||
PseudoRandom random = crypto.getPseudoRandom(remoteInvitationCode,
|
|
||||||
localInvitationCode);
|
|
||||||
return new BobConnector(crypto, bdfReaderFactory, bdfWriterFactory,
|
|
||||||
contactExchangeTask, this, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void localConfirmationSucceeded() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
localCompared = true;
|
|
||||||
localMatched = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
localConfirmationLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void localConfirmationFailed() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
localCompared = true;
|
|
||||||
localMatched = false;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
localConfirmationLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean getAndSetConnected() {
|
|
||||||
boolean redundant = connected.getAndSet(true);
|
|
||||||
if (!redundant)
|
|
||||||
for (InvitationListener l : listeners) l.connectionSucceeded();
|
|
||||||
return redundant;
|
|
||||||
}
|
|
||||||
|
|
||||||
void keyAgreementSucceeded(int localCode, int remoteCode) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
localConfirmationCode = localCode;
|
|
||||||
remoteConfirmationCode = remoteCode;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners)
|
|
||||||
l.keyAgreementSucceeded(localCode, remoteCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void keyAgreementFailed() {
|
|
||||||
for (InvitationListener l : listeners) l.keyAgreementFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean waitForLocalConfirmationResult() throws InterruptedException {
|
|
||||||
localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return localMatched;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remoteConfirmationSucceeded() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
remoteCompared = true;
|
|
||||||
remoteMatched = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.remoteConfirmationSucceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void remoteConfirmationFailed() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
remoteCompared = true;
|
|
||||||
remoteMatched = false;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.remoteConfirmationFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contactExchangeSucceeded(Author remoteAuthor) {
|
|
||||||
String name = remoteAuthor.getName();
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
remoteName = name;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners)
|
|
||||||
l.pseudonymExchangeSucceeded(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void duplicateContact(Author remoteAuthor) {
|
|
||||||
// TODO differentiate
|
|
||||||
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contactExchangeFailed() {
|
|
||||||
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class InvitationModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
InvitationTaskFactory provideInvitationTaskFactory(
|
|
||||||
InvitationTaskFactoryImpl invitationTaskFactory) {
|
|
||||||
return invitationTaskFactory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
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.identity.IdentityManager;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTask;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class InvitationTaskFactoryImpl implements InvitationTaskFactory {
|
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final BdfReaderFactory bdfReaderFactory;
|
|
||||||
private final BdfWriterFactory bdfWriterFactory;
|
|
||||||
private final ContactExchangeTask contactExchangeTask;
|
|
||||||
private final IdentityManager identityManager;
|
|
||||||
private final PluginManager pluginManager;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
InvitationTaskFactoryImpl(CryptoComponent crypto,
|
|
||||||
BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask,
|
|
||||||
IdentityManager identityManager, PluginManager pluginManager) {
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
|
||||||
this.bdfWriterFactory = bdfWriterFactory;
|
|
||||||
this.contactExchangeTask = contactExchangeTask;
|
|
||||||
this.identityManager = identityManager;
|
|
||||||
this.pluginManager = pluginManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InvitationTask createTask(int localCode, int remoteCode) {
|
|
||||||
return new ConnectorGroup(crypto, bdfReaderFactory, bdfWriterFactory,
|
|
||||||
contactExchangeTask, identityManager, pluginManager,
|
|
||||||
localCode, remoteCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -164,14 +164,6 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
return new ArrayList<DuplexPlugin>(duplexPlugins);
|
return new ArrayList<DuplexPlugin>(duplexPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<DuplexPlugin> getInvitationPlugins() {
|
|
||||||
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
|
|
||||||
for (DuplexPlugin d : duplexPlugins)
|
|
||||||
if (d.supportsInvitations()) supported.add(d);
|
|
||||||
return supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<DuplexPlugin> getKeyAgreementPlugins() {
|
public Collection<DuplexPlugin> getKeyAgreementPlugins() {
|
||||||
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
|
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ 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.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
||||||
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
|
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(LanTcpPlugin.class.getName());
|
Logger.getLogger(LanTcpPlugin.class.getName());
|
||||||
|
|
||||||
private static final int MAX_ADDRESSES = 5;
|
private static final int MAX_ADDRESSES = 4;
|
||||||
private static final String PROP_IP_PORTS = "ipPorts";
|
private static final String PROP_IP_PORTS = "ipPorts";
|
||||||
private static final String SEPARATOR = ",";
|
private static final String SEPARATOR = ",";
|
||||||
|
|
||||||
@@ -82,19 +83,19 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
|
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
|
||||||
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
|
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
|
||||||
String[] split = ipPorts.split(SEPARATOR);
|
String[] split = ipPorts.split(SEPARATOR);
|
||||||
List<InetSocketAddress> remotes = new ArrayList<InetSocketAddress>();
|
List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>();
|
||||||
for (String ipPort : split) {
|
for (String ipPort : split) {
|
||||||
InetSocketAddress a = parseSocketAddress(ipPort);
|
InetSocketAddress a = parseSocketAddress(ipPort);
|
||||||
if (a != null) remotes.add(a);
|
if (a != null) addresses.add(a);
|
||||||
}
|
}
|
||||||
return remotes;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setLocalSocketAddress(InetSocketAddress a) {
|
protected void setLocalSocketAddress(InetSocketAddress a) {
|
||||||
String ipPort = getIpPortString(a);
|
String ipPort = getIpPortString(a);
|
||||||
// Get the list of recently used addresses
|
// Get the list of recently used addresses
|
||||||
String setting = callback.getSettings().get(PROP_IP_PORTS);
|
String setting = callback.getSettings().get(PREF_LAN_IP_PORTS);
|
||||||
List<String> recent = new ArrayList<String>();
|
List<String> recent = new ArrayList<String>();
|
||||||
if (!StringUtils.isNullOrEmpty(setting))
|
if (!StringUtils.isNullOrEmpty(setting))
|
||||||
Collections.addAll(recent, setting.split(SEPARATOR));
|
Collections.addAll(recent, setting.split(SEPARATOR));
|
||||||
@@ -120,7 +121,7 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
}
|
}
|
||||||
// Save the setting
|
// Save the setting
|
||||||
Settings settings = new Settings();
|
Settings settings = new Settings();
|
||||||
settings.put(PROP_IP_PORTS, setting);
|
settings.put(PREF_LAN_IP_PORTS, setting);
|
||||||
callback.mergeSettings(settings);
|
callback.mergeSettings(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.plugin.tcp;
|
package org.briarproject.bramble.plugin.tcp;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
@@ -281,17 +280,6 @@ abstract class TcpPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsKeyAgreement() {
|
public boolean supportsKeyAgreement() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tcp;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -24,12 +25,12 @@ class TcpTransportConnection extends AbstractDuplexTransportConnection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getInputStream() throws IOException {
|
protected InputStream getInputStream() throws IOException {
|
||||||
return socket.getInputStream();
|
return IoUtils.getInputStream(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OutputStream getOutputStream() throws IOException {
|
protected OutputStream getOutputStream() throws IOException {
|
||||||
return socket.getOutputStream();
|
return IoUtils.getOutputStream(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.reporting;
|
package org.briarproject.bramble.reporting;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -130,7 +131,7 @@ public class DevReportServer {
|
|||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
socket.setSoTimeout(SOCKET_TIMEOUT_MS);
|
socket.setSoTimeout(SOCKET_TIMEOUT_MS);
|
||||||
in = socket.getInputStream();
|
in = IoUtils.getInputStream(socket);
|
||||||
reportDir.mkdirs();
|
reportDir.mkdirs();
|
||||||
reportFile = File.createTempFile(FILE_PREFIX, FILE_SUFFIX,
|
reportFile = File.createTempFile(FILE_PREFIX, FILE_SUFFIX,
|
||||||
reportDir);
|
reportDir);
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class DevReporterImpl implements DevReporter {
|
|||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
Socket s = connectToDevelopers();
|
Socket s = connectToDevelopers();
|
||||||
out = s.getOutputStream();
|
out = IoUtils.getOutputStream(s);
|
||||||
in = new FileInputStream(f);
|
in = new FileInputStream(f);
|
||||||
IoUtils.copyAndClose(in, out);
|
IoUtils.copyAndClose(in, out);
|
||||||
f.delete();
|
f.delete();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_SOCKET_TIMEOUT;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.SOCKS_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.SOCKS_PORT;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -17,6 +18,7 @@ public class SocksModule {
|
|||||||
SocketFactory provideTorSocketFactory() {
|
SocketFactory provideTorSocketFactory() {
|
||||||
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
|
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
|
||||||
SOCKS_PORT);
|
SOCKS_PORT);
|
||||||
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT);
|
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT,
|
||||||
|
EXTRA_SOCKET_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,36 @@ import org.briarproject.bramble.util.IoUtils;
|
|||||||
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.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
class SocksSocket extends Socket {
|
class SocksSocket extends Socket {
|
||||||
|
|
||||||
private final SocketAddress proxy;
|
private static final String[] ERRORS = {
|
||||||
private final int connectToProxyTimeout;
|
"Succeeded",
|
||||||
|
"General SOCKS server failure",
|
||||||
|
"Connection not allowed by ruleset",
|
||||||
|
"Network unreachable",
|
||||||
|
"Host unreachable",
|
||||||
|
"Connection refused",
|
||||||
|
"TTL expired",
|
||||||
|
"Command not supported",
|
||||||
|
"Address type not supported"
|
||||||
|
};
|
||||||
|
|
||||||
SocksSocket(SocketAddress proxy, int connectToProxyTimeout) {
|
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
|
||||||
|
|
||||||
|
private final SocketAddress proxy;
|
||||||
|
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||||
|
|
||||||
|
SocksSocket(SocketAddress proxy, int connectToProxyTimeout,
|
||||||
|
int extraSocketTimeout) {
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||||
|
this.extraSocketTimeout = extraSocketTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -28,29 +46,34 @@ class SocksSocket extends Socket {
|
|||||||
if (!(endpoint instanceof InetSocketAddress))
|
if (!(endpoint instanceof InetSocketAddress))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
InetSocketAddress inet = (InetSocketAddress) endpoint;
|
InetSocketAddress inet = (InetSocketAddress) endpoint;
|
||||||
|
InetAddress address = inet.getAddress();
|
||||||
|
if (address != null
|
||||||
|
&& !Arrays.equals(address.getAddress(), UNSPECIFIED_ADDRESS)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
String host = inet.getHostName();
|
String host = inet.getHostName();
|
||||||
if (host.length() > 255) throw new IllegalArgumentException();
|
if (host.length() > 255) throw new IllegalArgumentException();
|
||||||
int port = inet.getPort();
|
int port = inet.getPort();
|
||||||
|
|
||||||
// Connect to the proxy
|
// Connect to the proxy
|
||||||
super.connect(proxy, connectToProxyTimeout);
|
super.connect(proxy, connectToProxyTimeout);
|
||||||
OutputStream out = getOutputStream();
|
OutputStream out = IoUtils.getOutputStream(this);
|
||||||
InputStream in = getInputStream();
|
InputStream in = IoUtils.getInputStream(this);
|
||||||
|
|
||||||
// Request SOCKS 5 with no authentication
|
// Request SOCKS 5 with no authentication
|
||||||
sendMethodRequest(out);
|
sendMethodRequest(out);
|
||||||
receiveMethodResponse(in);
|
receiveMethodResponse(in);
|
||||||
|
|
||||||
// Use the supplied timeout temporarily
|
// Use the supplied timeout temporarily, plus any configured extra
|
||||||
int oldTimeout = getSoTimeout();
|
int oldTimeout = getSoTimeout();
|
||||||
setSoTimeout(timeout);
|
setSoTimeout(timeout + extraSocketTimeout);
|
||||||
|
|
||||||
// Connect to the endpoint via the proxy
|
// Connect to the endpoint via the proxy
|
||||||
sendConnectRequest(out, host, port);
|
sendConnectRequest(out, host, port);
|
||||||
receiveConnectResponse(in);
|
receiveConnectResponse(in);
|
||||||
|
|
||||||
// Restore the old timeout
|
// Restore the old timeout, plus any configured extra
|
||||||
setSoTimeout(oldTimeout);
|
setSoTimeout(oldTimeout + extraSocketTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMethodRequest(OutputStream out) throws IOException {
|
private void sendMethodRequest(OutputStream out) throws IOException {
|
||||||
@@ -93,13 +116,16 @@ class SocksSocket extends Socket {
|
|||||||
private void receiveConnectResponse(InputStream in) throws IOException {
|
private void receiveConnectResponse(InputStream in) throws IOException {
|
||||||
byte[] connectResponse = new byte[4];
|
byte[] connectResponse = new byte[4];
|
||||||
IoUtils.read(in, connectResponse);
|
IoUtils.read(in, connectResponse);
|
||||||
byte version = connectResponse[0];
|
int version = connectResponse[0] & 0xFF;
|
||||||
byte reply = connectResponse[1];
|
int reply = connectResponse[1] & 0xFF;
|
||||||
byte addressType = connectResponse[3];
|
int addressType = connectResponse[3] & 0xFF;
|
||||||
if (version != 5)
|
if (version != 5)
|
||||||
throw new IOException("Unsupported SOCKS version: " + version);
|
throw new IOException("Unsupported SOCKS version: " + version);
|
||||||
if (reply != 0)
|
if (reply != 0) {
|
||||||
throw new IOException("Connection failed: " + reply);
|
if (reply < ERRORS.length)
|
||||||
|
throw new IOException("Connection failed: " + ERRORS[reply]);
|
||||||
|
else throw new IOException("Connection failed: " + reply);
|
||||||
|
}
|
||||||
if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
|
if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
|
||||||
else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
|
else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
|
||||||
else throw new IOException("Unsupported address type: " + addressType);
|
else throw new IOException("Unsupported address type: " + addressType);
|
||||||
|
|||||||
@@ -11,16 +11,18 @@ import javax.net.SocketFactory;
|
|||||||
class SocksSocketFactory extends SocketFactory {
|
class SocksSocketFactory extends SocketFactory {
|
||||||
|
|
||||||
private final SocketAddress proxy;
|
private final SocketAddress proxy;
|
||||||
private final int connectToProxyTimeout;
|
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||||
|
|
||||||
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout) {
|
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout,
|
||||||
|
int extraSocketTimeout) {
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||||
|
this.extraSocketTimeout = extraSocketTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Socket createSocket() {
|
public Socket createSocket() {
|
||||||
return new SocksSocket(proxy, connectToProxyTimeout);
|
return new SocksSocket(proxy, connectToProxyTimeout, extraSocketTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.PoliteExecutor;
|
||||||
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.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -29,6 +31,16 @@ public class SyncModule {
|
|||||||
ValidationManager validationManager;
|
ValidationManager validationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of validation tasks to delegate to the crypto
|
||||||
|
* executor concurrently.
|
||||||
|
* <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_CONCURRENT_VALIDATION_TASKS =
|
||||||
|
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
GroupFactory provideGroupFactory(CryptoComponent crypto) {
|
GroupFactory provideGroupFactory(CryptoComponent crypto) {
|
||||||
return new GroupFactoryImpl(crypto);
|
return new GroupFactoryImpl(crypto);
|
||||||
@@ -62,10 +74,20 @@ public class SyncModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
ValidationManager getValidationManager(LifecycleManager lifecycleManager,
|
ValidationManager provideValidationManager(
|
||||||
EventBus eventBus, ValidationManagerImpl validationManager) {
|
LifecycleManager lifecycleManager, EventBus eventBus,
|
||||||
|
ValidationManagerImpl validationManager) {
|
||||||
lifecycleManager.registerService(validationManager);
|
lifecycleManager.registerService(validationManager);
|
||||||
eventBus.addListener(validationManager);
|
eventBus.addListener(validationManager);
|
||||||
return validationManager;
|
return validationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@ValidationExecutor
|
||||||
|
Executor provideValidationExecutor(
|
||||||
|
@CryptoExecutor Executor cryptoExecutor) {
|
||||||
|
return new PoliteExecutor("ValidationExecutor", cryptoExecutor,
|
||||||
|
MAX_CONCURRENT_VALIDATION_TASKS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.ElementType.PARAMETER;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for injecting the executor for validation tasks. Also used for
|
||||||
|
* annotating methods that should run on the validation executor.
|
||||||
|
* <p>
|
||||||
|
* The contract of this executor is that tasks may be run concurrently, and
|
||||||
|
* submitting a task will never block. Tasks must not run indefinitely. Tasks
|
||||||
|
* submitted during shutdown are discarded.
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Target({FIELD, METHOD, PARAMETER})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface ValidationExecutor {
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -50,8 +49,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
Logger.getLogger(ValidationManagerImpl.class.getName());
|
Logger.getLogger(ValidationManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor, validationExecutor;
|
||||||
private final Executor cryptoExecutor;
|
|
||||||
private final MessageFactory messageFactory;
|
private final MessageFactory messageFactory;
|
||||||
private final Map<ClientId, MessageValidator> validators;
|
private final Map<ClientId, MessageValidator> validators;
|
||||||
private final Map<ClientId, IncomingMessageHook> hooks;
|
private final Map<ClientId, IncomingMessageHook> hooks;
|
||||||
@@ -60,11 +58,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
@Inject
|
@Inject
|
||||||
ValidationManagerImpl(DatabaseComponent db,
|
ValidationManagerImpl(DatabaseComponent db,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@CryptoExecutor Executor cryptoExecutor,
|
@ValidationExecutor Executor validationExecutor,
|
||||||
MessageFactory messageFactory) {
|
MessageFactory messageFactory) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.validationExecutor = validationExecutor;
|
||||||
this.messageFactory = messageFactory;
|
this.messageFactory = messageFactory;
|
||||||
validators = new ConcurrentHashMap<ClientId, MessageValidator>();
|
validators = new ConcurrentHashMap<ClientId, MessageValidator>();
|
||||||
hooks = new ConcurrentHashMap<ClientId, IncomingMessageHook>();
|
hooks = new ConcurrentHashMap<ClientId, IncomingMessageHook>();
|
||||||
@@ -104,6 +102,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void validateOutstandingMessages(ClientId c) {
|
private void validateOutstandingMessages(ClientId c) {
|
||||||
try {
|
try {
|
||||||
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
|
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
|
||||||
@@ -130,6 +129,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void validateNextMessage(Queue<MessageId> unvalidated) {
|
private void validateNextMessage(Queue<MessageId> unvalidated) {
|
||||||
try {
|
try {
|
||||||
Message m;
|
Message m;
|
||||||
@@ -167,6 +167,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void deliverOutstandingMessages(ClientId c) {
|
private void deliverOutstandingMessages(ClientId c) {
|
||||||
try {
|
try {
|
||||||
Queue<MessageId> pending = new LinkedList<MessageId>();
|
Queue<MessageId> pending = new LinkedList<MessageId>();
|
||||||
@@ -194,6 +195,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void deliverNextPendingMessage(Queue<MessageId> pending) {
|
private void deliverNextPendingMessage(Queue<MessageId> pending) {
|
||||||
try {
|
try {
|
||||||
boolean anyInvalid = false, allDelivered = true;
|
boolean anyInvalid = false, allDelivered = true;
|
||||||
@@ -220,8 +222,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
Message m = messageFactory.createMessage(id, raw);
|
Message m = messageFactory.createMessage(id, raw);
|
||||||
Group g = db.getGroup(txn, m.getGroupId());
|
Group g = db.getGroup(txn, m.getGroupId());
|
||||||
ClientId c = g.getClientId();
|
ClientId c = g.getClientId();
|
||||||
Metadata meta = db.getMessageMetadataForValidator(txn,
|
Metadata meta =
|
||||||
id);
|
db.getMessageMetadataForValidator(txn, id);
|
||||||
DeliveryResult result = deliverMessage(txn, m, c, meta);
|
DeliveryResult result = deliverMessage(txn, m, c, meta);
|
||||||
if (result.valid) {
|
if (result.valid) {
|
||||||
pending.addAll(getPendingDependents(txn, id));
|
pending.addAll(getPendingDependents(txn, id));
|
||||||
@@ -240,8 +242,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
if (invalidate != null) invalidateNextMessageAsync(invalidate);
|
if (invalidate != null) invalidateNextMessageAsync(invalidate);
|
||||||
deliverNextPendingMessageAsync(pending);
|
|
||||||
if (toShare != null) shareNextMessageAsync(toShare);
|
if (toShare != null) shareNextMessageAsync(toShare);
|
||||||
|
deliverNextPendingMessageAsync(pending);
|
||||||
} catch (NoSuchMessageException e) {
|
} catch (NoSuchMessageException e) {
|
||||||
LOG.info("Message removed before delivery");
|
LOG.info("Message removed before delivery");
|
||||||
deliverNextPendingMessageAsync(pending);
|
deliverNextPendingMessageAsync(pending);
|
||||||
@@ -249,13 +251,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
LOG.info("Group removed before delivery");
|
LOG.info("Group removed before delivery");
|
||||||
deliverNextPendingMessageAsync(pending);
|
deliverNextPendingMessageAsync(pending);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateMessageAsync(final Message m, final Group g) {
|
private void validateMessageAsync(final Message m, final Group g) {
|
||||||
cryptoExecutor.execute(new Runnable() {
|
validationExecutor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
validateMessage(m, g);
|
validateMessage(m, g);
|
||||||
@@ -263,10 +264,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ValidationExecutor
|
||||||
private void validateMessage(Message m, Group g) {
|
private void validateMessage(Message m, Group g) {
|
||||||
MessageValidator v = validators.get(g.getClientId());
|
MessageValidator v = validators.get(g.getClientId());
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
LOG.warning("No validator");
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("No validator for " + g.getClientId().getString());
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
MessageContext context = v.validateMessage(m, g);
|
MessageContext context = v.validateMessage(m, g);
|
||||||
@@ -291,6 +294,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void storeMessageContext(Message m, ClientId c,
|
private void storeMessageContext(Message m, ClientId c,
|
||||||
MessageContext context) {
|
MessageContext context) {
|
||||||
try {
|
try {
|
||||||
@@ -353,6 +357,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private DeliveryResult deliverMessage(Transaction txn, Message m,
|
private DeliveryResult deliverMessage(Transaction txn, Message m,
|
||||||
ClientId c, Metadata meta) throws DbException {
|
ClientId c, Metadata meta) throws DbException {
|
||||||
// Deliver the message to the client if it's registered a hook
|
// Deliver the message to the client if it's registered a hook
|
||||||
@@ -362,10 +367,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
try {
|
try {
|
||||||
shareMsg = hook.incomingMessage(txn, m, meta);
|
shareMsg = hook.incomingMessage(txn, m, meta);
|
||||||
} catch (InvalidMessageException e) {
|
} catch (InvalidMessageException e) {
|
||||||
// message is invalid, mark it as such and delete it
|
invalidateMessage(txn, m.getId());
|
||||||
db.setMessageState(txn, m.getId(), INVALID);
|
|
||||||
db.deleteMessageMetadata(txn, m.getId());
|
|
||||||
db.deleteMessage(txn, m.getId());
|
|
||||||
return new DeliveryResult(false, false);
|
return new DeliveryResult(false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,6 +375,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
return new DeliveryResult(true, shareMsg);
|
return new DeliveryResult(true, shareMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m)
|
private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Queue<MessageId> pending = new LinkedList<MessageId>();
|
Queue<MessageId> pending = new LinkedList<MessageId>();
|
||||||
@@ -392,6 +395,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void shareOutstandingMessages(ClientId c) {
|
private void shareOutstandingMessages(ClientId c) {
|
||||||
try {
|
try {
|
||||||
Queue<MessageId> toShare = new LinkedList<MessageId>();
|
Queue<MessageId> toShare = new LinkedList<MessageId>();
|
||||||
@@ -424,6 +428,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void shareNextMessage(Queue<MessageId> toShare) {
|
private void shareNextMessage(Queue<MessageId> toShare) {
|
||||||
try {
|
try {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
@@ -457,6 +462,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void invalidateNextMessage(Queue<MessageId> invalidate) {
|
private void invalidateNextMessage(Queue<MessageId> invalidate) {
|
||||||
try {
|
try {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
@@ -479,6 +485,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void invalidateMessage(Transaction txn, MessageId m)
|
private void invalidateMessage(Transaction txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
db.setMessageState(txn, m, INVALID);
|
db.setMessageState(txn, m, INVALID);
|
||||||
@@ -486,6 +493,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
db.deleteMessageMetadata(txn, m);
|
db.deleteMessageMetadata(txn, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private Queue<MessageId> getDependentsToInvalidate(Transaction txn,
|
private Queue<MessageId> getDependentsToInvalidate(Transaction txn,
|
||||||
MessageId m) throws DbException {
|
MessageId m) throws DbException {
|
||||||
Queue<MessageId> invalidate = new LinkedList<MessageId>();
|
Queue<MessageId> invalidate = new LinkedList<MessageId>();
|
||||||
@@ -515,6 +523,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void loadGroupAndValidate(final Message m) {
|
private void loadGroupAndValidate(final Message m) {
|
||||||
try {
|
try {
|
||||||
Group g;
|
Group g;
|
||||||
@@ -534,6 +543,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class DeliveryResult {
|
private static class DeliveryResult {
|
||||||
|
|
||||||
private final boolean valid, share;
|
private final boolean valid, share;
|
||||||
|
|
||||||
private DeliveryResult(boolean valid, boolean share) {
|
private DeliveryResult(boolean valid, boolean share) {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
abstract class AbstractSecureRandomProvider implements SecureRandomProvider {
|
||||||
|
|
||||||
|
// Contribute whatever slightly unpredictable info we have to the pool
|
||||||
|
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||||
|
out.writeLong(System.currentTimeMillis());
|
||||||
|
out.writeLong(System.nanoTime());
|
||||||
|
out.writeLong(Runtime.getRuntime().freeMemory());
|
||||||
|
List<NetworkInterface> ifaces =
|
||||||
|
Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||||
|
for (NetworkInterface i : ifaces) {
|
||||||
|
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
|
||||||
|
for (InetAddress a : addrs) out.write(a.getAddress());
|
||||||
|
byte[] hardware = i.getHardwareAddress();
|
||||||
|
if (hardware != null) out.write(hardware);
|
||||||
|
}
|
||||||
|
for (Entry<String, String> e : System.getenv().entrySet()) {
|
||||||
|
out.writeUTF(e.getKey());
|
||||||
|
out.writeUTF(e.getValue());
|
||||||
|
}
|
||||||
|
Properties properties = System.getProperties();
|
||||||
|
for (String key : properties.stringPropertyNames())
|
||||||
|
out.writeUTF(properties.getProperty(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(LinuxSecureRandomProvider.class.getName());
|
||||||
|
|
||||||
|
private static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
|
||||||
|
private final AtomicBoolean seeded = new AtomicBoolean(false);
|
||||||
|
private final File outputDevice;
|
||||||
|
|
||||||
|
LinuxSecureRandomProvider() {
|
||||||
|
this(RANDOM_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxSecureRandomProvider(File outputDevice) {
|
||||||
|
this.outputDevice = outputDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Provider getProvider() {
|
||||||
|
if (!seeded.getAndSet(true)) writeSeed();
|
||||||
|
return new LinuxProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeSeed() {
|
||||||
|
try {
|
||||||
|
DataOutputStream out = new DataOutputStream(
|
||||||
|
new FileOutputStream(outputDevice));
|
||||||
|
writeToEntropyPool(out);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// On some devices /dev/urandom isn't writable - this isn't fatal
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private static class LinuxProvider extends Provider {
|
||||||
|
|
||||||
|
private LinuxProvider() {
|
||||||
|
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
|
||||||
|
// Although /dev/urandom is not a SHA-1 PRNG, some callers
|
||||||
|
// explicitly request a SHA1PRNG SecureRandom and we need to
|
||||||
|
// prevent them from getting the default implementation whose
|
||||||
|
// output may have low entropy.
|
||||||
|
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
|
||||||
|
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandomSpi;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
public class LinuxSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(LinuxSecureRandomSpi.class.getName());
|
||||||
|
|
||||||
|
private static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
|
||||||
|
private final File inputDevice, outputDevice;
|
||||||
|
|
||||||
|
public LinuxSecureRandomSpi() {
|
||||||
|
this(RANDOM_DEVICE, RANDOM_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
|
||||||
|
this.inputDevice = inputDevice;
|
||||||
|
this.outputDevice = outputDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineSetSeed(byte[] seed) {
|
||||||
|
try {
|
||||||
|
DataOutputStream out = new DataOutputStream(
|
||||||
|
new FileOutputStream(outputDevice));
|
||||||
|
out.write(seed);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// On some devices /dev/urandom isn't writable - this isn't fatal
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineNextBytes(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
DataInputStream in = new DataInputStream(
|
||||||
|
new FileInputStream(inputDevice));
|
||||||
|
in.readFully(bytes);
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] engineGenerateSeed(int len) {
|
||||||
|
byte[] seed = new byte[len];
|
||||||
|
engineNextBytes(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class LinuxSeedProvider implements SeedProvider {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(LinuxSeedProvider.class.getName());
|
|
||||||
|
|
||||||
private final String outputFile, inputFile;
|
|
||||||
|
|
||||||
LinuxSeedProvider() {
|
|
||||||
this("/dev/urandom", "/dev/urandom");
|
|
||||||
}
|
|
||||||
|
|
||||||
LinuxSeedProvider(String outputFile, String inputFile) {
|
|
||||||
this.outputFile = outputFile;
|
|
||||||
this.inputFile = inputFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSeed() {
|
|
||||||
byte[] seed = new byte[SEED_BYTES];
|
|
||||||
// Contribute whatever slightly unpredictable info we have to the pool
|
|
||||||
try {
|
|
||||||
DataOutputStream out = new DataOutputStream(
|
|
||||||
new FileOutputStream(outputFile));
|
|
||||||
writeToEntropyPool(out);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// On some devices /dev/urandom isn't writable - this isn't fatal
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
// Read the seed from the pool
|
|
||||||
try {
|
|
||||||
DataInputStream in = new DataInputStream(
|
|
||||||
new FileInputStream(inputFile));
|
|
||||||
in.readFully(seed);
|
|
||||||
in.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeToEntropyPool(DataOutputStream out) throws IOException {
|
|
||||||
out.writeLong(System.currentTimeMillis());
|
|
||||||
out.writeLong(System.nanoTime());
|
|
||||||
List<NetworkInterface> ifaces =
|
|
||||||
Collections.list(NetworkInterface.getNetworkInterfaces());
|
|
||||||
for (NetworkInterface i : ifaces) {
|
|
||||||
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
|
|
||||||
for (InetAddress a : addrs) out.write(a.getAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,10 +29,10 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createInvitationStreamReader(InputStream in,
|
public InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamReaderImpl(
|
return new StreamReaderImpl(
|
||||||
streamDecrypterFactory.createInvitationStreamDecrypter(in,
|
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
|
||||||
headerKey));
|
headerKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream createInvitationStreamWriter(OutputStream out,
|
public OutputStream createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamWriterImpl(
|
return new StreamWriterImpl(
|
||||||
streamEncrypterFactory.createInvitationStreamEncrypter(out,
|
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
|
||||||
headerKey));
|
headerKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,7 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
@@ -126,7 +127,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||||
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, inKeys.getTagKey(), streamNumber);
|
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
inContexts.put(new Bytes(tag), tagCtx);
|
inContexts.put(new Bytes(tag), tagCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +244,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Add tags for any stream numbers added to the window
|
// Add tags for any stream numbers added to the window
|
||||||
for (long streamNumber : change.getAdded()) {
|
for (long streamNumber : change.getAdded()) {
|
||||||
byte[] addTag = new byte[TAG_LENGTH];
|
byte[] addTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(addTag, inKeys.getTagKey(), streamNumber);
|
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
inContexts.put(new Bytes(addTag), new TagContext(
|
inContexts.put(new Bytes(addTag), new TagContext(
|
||||||
tagCtx.contactId, inKeys, streamNumber));
|
tagCtx.contactId, inKeys, streamNumber));
|
||||||
}
|
}
|
||||||
@@ -250,7 +253,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : change.getRemoved()) {
|
for (long streamNumber : change.getRemoved()) {
|
||||||
if (streamNumber == tagCtx.streamNumber) continue;
|
if (streamNumber == tagCtx.streamNumber) continue;
|
||||||
byte[] removeTag = new byte[TAG_LENGTH];
|
byte[] removeTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(removeTag, inKeys.getTagKey(), streamNumber);
|
crypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||||
|
PROTOCOL_VERSION, streamNumber);
|
||||||
inContexts.remove(new Bytes(removeTag));
|
inContexts.remove(new Bytes(removeTag));
|
||||||
}
|
}
|
||||||
// Write the window back to the DB
|
// Write the window back to the DB
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class PoliteExecutorTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private static final String TAG = "Test";
|
||||||
|
private static final int TASKS = 10;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTasksAreDelegatedInOrderOfSubmission() throws Exception {
|
||||||
|
// Delegate to a single-threaded executor
|
||||||
|
Executor delegate = Executors.newSingleThreadExecutor();
|
||||||
|
// Allow all the tasks to be delegated straight away
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(TASKS);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
list.add(result);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
latch.await();
|
||||||
|
// The tasks should have run in the order they were submitted
|
||||||
|
assertEquals(ascendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueuedTasksAreDelegatedInOrderOfSubmission()
|
||||||
|
throws Exception {
|
||||||
|
// Delegate to a single-threaded executor
|
||||||
|
Executor delegate = Executors.newSingleThreadExecutor();
|
||||||
|
// Allow two tasks to be delegated at a time
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 2);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(TASKS);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
list.add(result);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
latch.await();
|
||||||
|
// The tasks should have run in the order they were submitted
|
||||||
|
assertEquals(ascendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTasksRunInParallelOnDelegate() throws Exception {
|
||||||
|
// Delegate to a multi-threaded executor
|
||||||
|
Executor delegate = Executors.newCachedThreadPool();
|
||||||
|
// Allow all the tasks to be delegated straight away
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch[] latches = new CountDownLatch[TASKS];
|
||||||
|
for (int i = 0; i < TASKS; i++) latches[i] = new CountDownLatch(1);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Each task waits for the next task, if any, to finish
|
||||||
|
if (result < TASKS - 1) latches[result + 1].await();
|
||||||
|
list.add(result);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
latches[result].countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
for (int i = 0; i < TASKS; i++) latches[i].await();
|
||||||
|
// The tasks should have finished in reverse order
|
||||||
|
assertEquals(descendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTasksDoNotRunInParallelOnDelegate() throws Exception {
|
||||||
|
// Delegate to a multi-threaded executor
|
||||||
|
Executor delegate = Executors.newCachedThreadPool();
|
||||||
|
// Allow one task to be delegated at a time
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 1);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(TASKS);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Each task runs faster than the previous task
|
||||||
|
Thread.sleep(TASKS - result);
|
||||||
|
list.add(result);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
latch.await();
|
||||||
|
// The tasks should have finished in the order they were submitted
|
||||||
|
assertEquals(ascendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> ascendingOrder() {
|
||||||
|
Integer[] array = new Integer[TASKS];
|
||||||
|
for (int i = 0; i < TASKS; i++) array[i] = i;
|
||||||
|
return Arrays.asList(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> descendingOrder() {
|
||||||
|
Integer[] array = new Integer[TASKS];
|
||||||
|
for (int i = 0; i < TASKS; i++) array[i] = TASKS - 1 - i;
|
||||||
|
return Arrays.asList(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
byte[] seed = new byte[32];
|
byte[] seed = new byte[32];
|
||||||
new SecureRandom().nextBytes(seed);
|
new SecureRandom().nextBytes(seed);
|
||||||
// Montgomery ladder multiplier
|
// Montgomery ladder multiplier
|
||||||
SecureRandom random = new FortunaSecureRandom(seed);
|
SecureRandom random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters montgomeryGeneratorParams =
|
ECKeyGenerationParameters montgomeryGeneratorParams =
|
||||||
new ECKeyGenerationParameters(PARAMETERS, random);
|
new ECKeyGenerationParameters(PARAMETERS, random);
|
||||||
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
||||||
@@ -63,7 +63,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
ECPublicKeyParameters montgomeryPublic2 =
|
ECPublicKeyParameters montgomeryPublic2 =
|
||||||
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
|
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
|
||||||
// Default multiplier
|
// Default multiplier
|
||||||
random = new FortunaSecureRandom(seed);
|
random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters defaultGeneratorParams =
|
ECKeyGenerationParameters defaultGeneratorParams =
|
||||||
new ECKeyGenerationParameters(defaultParameters, random);
|
new ECKeyGenerationParameters(defaultParameters, random);
|
||||||
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class FortunaGeneratorTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCounterInitialisedToOne() {
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
// The counter is little-endian
|
|
||||||
byte[] expected = new byte[16];
|
|
||||||
expected[0] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementCounter() {
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
// Increment the counter until it reaches 255
|
|
||||||
for (int i = 1; i < 255; i++) f.incrementCounter();
|
|
||||||
byte[] expected = new byte[16];
|
|
||||||
expected[0] = (byte) 255;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
// Increment the counter again - it should carry into the next byte
|
|
||||||
f.incrementCounter();
|
|
||||||
expected[0] = 0;
|
|
||||||
expected[1] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
// Increment the counter until it carries into the next byte
|
|
||||||
for (int i = 256; i < 65536; i++) f.incrementCounter();
|
|
||||||
expected[0] = 0;
|
|
||||||
expected[1] = 0;
|
|
||||||
expected[2] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNextBytes() {
|
|
||||||
// Generate several outputs with the same seed - they should all match
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
byte[] out1 = new byte[48];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out1, 0, 48);
|
|
||||||
// One byte longer than a block, with an offset of one
|
|
||||||
byte[] out2 = new byte[49];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out2, 1, 48);
|
|
||||||
for (int i = 0; i < 48; i++) assertEquals(out1[i], out2[i + 1]);
|
|
||||||
// One byte shorter than a block
|
|
||||||
byte[] out3 = new byte[47];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out3, 0, 47);
|
|
||||||
for (int i = 0; i < 47; i++) assertEquals(out1[i], out3[i]);
|
|
||||||
// Less than a block, with an offset greater than a block
|
|
||||||
byte[] out4 = new byte[32];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out4, 17, 15);
|
|
||||||
for (int i = 0; i < 15; i++) assertEquals(out1[i], out4[i + 17]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRekeying() {
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
FortunaGenerator f = new FortunaGenerator(seed);
|
|
||||||
// Generate three blocks of output
|
|
||||||
byte[] out1 = new byte[48];
|
|
||||||
f.nextBytes(out1, 0, 48);
|
|
||||||
// Create another generator with the same seed and generate one block
|
|
||||||
f = new FortunaGenerator(seed);
|
|
||||||
byte[] out2 = new byte[16];
|
|
||||||
f.nextBytes(out2, 0, 16);
|
|
||||||
// The generator should have rekeyed with the 2nd and 3rd blocks
|
|
||||||
byte[] expectedKey = new byte[32];
|
|
||||||
System.arraycopy(out1, 16, expectedKey, 0, 32);
|
|
||||||
// The generator's counter should have been incremented 3 times
|
|
||||||
byte[] expectedCounter = new byte[16];
|
|
||||||
expectedCounter[0] = 4;
|
|
||||||
// The next expected output block is the counter encrypted with the key
|
|
||||||
byte[] expectedOutput = new byte[16];
|
|
||||||
BlockCipher c = new AESLightEngine();
|
|
||||||
c.init(true, new KeyParameter(expectedKey));
|
|
||||||
c.processBlock(expectedCounter, 0, expectedOutput, 0);
|
|
||||||
// Check that the generator produces the expected output block
|
|
||||||
byte[] out3 = new byte[16];
|
|
||||||
f.nextBytes(out3, 0, 16);
|
|
||||||
assertArrayEquals(expectedOutput, out3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMaximumRequestLength() {
|
|
||||||
int expectedMax = 1024 * 1024;
|
|
||||||
byte[] output = new byte[expectedMax + 123];
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
assertEquals(expectedMax, f.nextBytes(output, 0, output.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_1;
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_2;
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_3;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class FortunaSecureRandomTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClassPassesSelfTest() {
|
|
||||||
assertTrue(FortunaSecureRandom.selfTest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSelfTestVectorsAreReproducible() {
|
|
||||||
byte[] key = new byte[32], seed = new byte[32];
|
|
||||||
byte[] counter = new byte[16], output = new byte[16];
|
|
||||||
byte[] newKey = new byte[32];
|
|
||||||
// Calculate the initial key
|
|
||||||
DoubleDigest digest = new DoubleDigest(new SHA256Digest());
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, 32);
|
|
||||||
// Calculate the first output block and the new key
|
|
||||||
BlockCipher c = new AESLightEngine();
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 1;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
counter[0] = 2;
|
|
||||||
c.processBlock(counter, 0, newKey, 0);
|
|
||||||
counter[0] = 3;
|
|
||||||
c.processBlock(counter, 0, newKey, 16);
|
|
||||||
System.arraycopy(newKey, 0, key, 0, 32);
|
|
||||||
// The first self-test vector should match the first output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_1, output);
|
|
||||||
// Calculate the second output block and the new key before reseeding
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 4;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
counter[0] = 5;
|
|
||||||
c.processBlock(counter, 0, newKey, 0);
|
|
||||||
counter[0] = 6;
|
|
||||||
c.processBlock(counter, 0, newKey, 16);
|
|
||||||
System.arraycopy(newKey, 0, key, 0, 32);
|
|
||||||
// The second self-test vector should match the second output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_2, output);
|
|
||||||
// Calculate the new key after reseeding
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, 32);
|
|
||||||
// Calculate the third output block
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 8;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
// The third self-test vector should match the third output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_3, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ public class HashTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public HashTest() {
|
public HashTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
@@ -14,8 +14,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeriveMasterSecret() throws Exception {
|
public void testDeriveMasterSecret() throws Exception {
|
||||||
SeedProvider seedProvider = new TestSeedProvider();
|
SecureRandomProvider
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
secureRandomProvider = new TestSecureRandomProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
@@ -27,8 +28,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
public void testDeriveSharedSecret() throws Exception {
|
||||||
SeedProvider seedProvider = new TestSeedProvider();
|
SecureRandomProvider
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
secureRandomProvider = new TestSecureRandomProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
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.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class KeyDerivationTest extends BrambleTestCase {
|
public class KeyDerivationTest extends BrambleTestCase {
|
||||||
|
|
||||||
@@ -23,7 +27,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
private final SecretKey master;
|
private final SecretKey master;
|
||||||
|
|
||||||
public KeyDerivationTest() {
|
public KeyDerivationTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
master = TestUtils.getSecretKey();
|
master = TestUtils.getSecretKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +160,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertAllDifferent(List<SecretKey> keys) {
|
private void assertAllDifferent(List<SecretKey> keys) {
|
||||||
for (SecretKey ki : keys) {
|
Set<Bytes> set = new HashSet<Bytes>();
|
||||||
for (SecretKey kj : keys) {
|
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
|
||||||
if (ki == kj) assertArrayEquals(ki.getBytes(), kj.getBytes());
|
|
||||||
else assertFalse(Arrays.equals(ki.getBytes(), kj.getBytes()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ 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.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSeedProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementPublicKeyLength() throws Exception {
|
public void testAgreementPublicKeyLength() throws Exception {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
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.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class MacTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public MacTest() {
|
public MacTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class PasswordBasedKdfTest extends BrambleTestCase {
|
public class PasswordBasedKdfTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSeedProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionAndDecryption() {
|
public void testEncryptionAndDecryption() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
|||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE;
|
||||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ public class PasswordStrengthEstimatorImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWeakPasswords() {
|
public void testWeakPasswords() {
|
||||||
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
|
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
|
||||||
assertTrue(e.estimateStrength("") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("") == NONE);
|
||||||
assertTrue(e.estimateStrength("password") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("password") < QUITE_STRONG);
|
||||||
assertTrue(e.estimateStrength("letmein") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("letmein") < QUITE_STRONG);
|
||||||
assertTrue(e.estimateStrength("123456") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("123456") < QUITE_STRONG);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.spongycastle.crypto.Digest;
|
||||||
|
import org.spongycastle.crypto.engines.Salsa20Engine;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
import org.spongycastle.crypto.params.ParametersWithIV;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class PseudoRandom {
|
||||||
|
|
||||||
|
private final Salsa20Engine cipher = new Salsa20Engine();
|
||||||
|
|
||||||
|
PseudoRandom(byte[] seed) {
|
||||||
|
// Hash the seed to produce a 32-byte key
|
||||||
|
byte[] key = new byte[32];
|
||||||
|
Digest digest = new Blake2sDigest();
|
||||||
|
digest.update(seed, 0, seed.length);
|
||||||
|
digest.doFinal(key, 0);
|
||||||
|
// Initialise the stream cipher with an all-zero nonce
|
||||||
|
byte[] nonce = new byte[8];
|
||||||
|
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] nextBytes(int length) {
|
||||||
|
byte[] in = new byte[length], out = new byte[length];
|
||||||
|
cipher.processBytes(in, 0, length, out, 0);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user