Compare commits

...

204 Commits

Author SHA1 Message Date
akwizgran
1a83b2c99b Bumped version number for beta release. 2017-10-17 09:41:11 +01:00
akwizgran
f641fae1c7 Added new translations. 2017-10-16 17:10:53 +01:00
akwizgran
deb43d9872 Updated translations. 2017-10-16 17:08:07 +01:00
akwizgran
cee4e1305e Merge branch 'extend-expiry' into 'master'
Extend expiry and show a green snackbar about it once

See merge request !606
2017-10-12 17:03:26 +00:00
akwizgran
a1f989c43c Use black text for the expiry extension notice. 2017-10-12 17:51:57 +01:00
akwizgran
b67abadbac Use a setting to record whether update notice has been shown 2017-10-12 17:51:57 +01:00
Torsten Grote
8c29c85696 Extend expiry and show a green snackbar about it once 2017-10-12 17:51:57 +01:00
akwizgran
13d35229d5 Merge branch '1091-reduce-polling-queries' into 'master'
Reduce number of DB queries used when polling for connections

Closes #1091

See merge request !604
2017-10-11 13:45:14 +00:00
Torsten Grote
f0137b41b6 Merge branch 'accept-sdk-license-agreement-for-ci' into 'master'
Accept build tools license agreement for CI runner

See merge request !607
2017-10-11 13:24:25 +00:00
akwizgran
b221d21903 Accept all SDK license agreements for CI runner. 2017-10-11 14:18:02 +01:00
Torsten Grote
8bac202626 Add Hindi, Finnish and Basque translations 2017-10-10 10:04:22 -03:00
Torsten Grote
973151c949 Merge branch 'report-bluetooth-and-wifi-support' into 'master'
Report Bluetooth LE and Wi-Fi Direct support in crash reports and feedback

See merge request !605
2017-10-10 12:16:29 +00:00
akwizgran
ed26ab78a5 Merge branch '158-permission-requests' into 'master'
Add permission requests for Android 6+

Closes #158

See merge request !601
2017-10-10 10:40:14 +00:00
akwizgran
8454b2d235 Code cleanup, shortened button text to help with layout. 2017-10-10 11:33:07 +01:00
akwizgran
91d0f89f60 Removed unused import. 2017-10-10 11:08:40 +01:00
akwizgran
e074672e86 Reduce DB queries for looking up transport properties. 2017-10-10 10:59:39 +01:00
akwizgran
6c1901fe5b Reduced DB queries when polling for LAN connections. 2017-10-09 15:20:03 +01:00
goapunk
49052be627 Add permission requests for Android 6+
* Add request for the camera

Signed-off-by: goapunk <noobie@goapunks.net>
2017-10-04 13:17:51 +02:00
Torsten Grote
5b5b540630 Merge branch '299-disable-bluetooth-at-shutdown' into 'master'
Disable Bluetooth at shutdown if we enabled it

See merge request !602
2017-10-03 15:38:22 +00:00
akwizgran
9993bac3a1 Disable Bluetooth at shutdown if we enabled it. 2017-10-03 15:59:07 +01:00
akwizgran
3c95988693 Merge branch '539-clear-notifications' into 'master'
Don't show dismissed notifications again when items are removed

Closes #539

See merge request !600
2017-10-02 14:46:54 +00:00
akwizgran
fc5c3b470e Merge branch 'patch-1' into 'master'
Contacts, on your side

See merge request !594
2017-10-02 13:14:00 +00:00
akwizgran
53f05a72ba Removed logging. 2017-09-29 15:31:25 +01:00
akwizgran
2c10ae7d06 Clear notifications when dismissed.
Also fixed an issue with notifications alerting again when items
were removed.
2017-09-29 15:23:27 +01:00
akwizgran
6b9010c557 Merge branch '703-create-test-data' into 'master'
Add an option to debug builds to create fake test data

Closes #703

See merge request !595
2017-09-28 10:37:03 +00:00
Torsten Grote
1bf0fdfa81 Add an option to debug builds to create fake test data 2017-09-27 13:55:29 -03:00
Torsten Grote
237759aac0 Add Simplified Chinese translation 2017-09-27 13:32:07 -03:00
akwizgran
2a141e0a97 Merge branch 'disableAaptCruncher' into 'master'
Disable PNG crunching for reproducibility

See merge request !596
2017-09-27 16:04:23 +00:00
akwizgran
d6900be68e Merge branch '1051-fix-pink' into 'master'
Fix pink navigation drawer items with current support library

Closes #1051

See merge request !598
2017-09-27 16:02:22 +00:00
Torsten Grote
a35d7c7204 Fix pink navigation drawer items with current support library 2017-09-27 12:09:06 -03:00
Torsten Grote
86287f9241 Merge branch 'spongy-castle-158' into 'master'
Upgrade Spongy Castle to 1.58

See merge request !597
2017-09-27 15:01:15 +00:00
akwizgran
0b2e3dd96f Upgrade Spongy Castle to 1.58. 2017-09-27 15:54:37 +01:00
Torsten Grote
90aa1d1ce7 Disable PNG crunching for reproducibility
This can help to prevent non-determinism introduced by the crunching
process.

More information:
e48f9f0773

With enabled and disabled crunching,
the size of the signed release APK was 17809681 bytes.

Related to #164
2017-09-27 11:35:25 -03:00
Michael Rogers
ef2286ab53 Bumped version number for beta release. 2017-09-20 14:51:10 +01:00
akwizgran
47b25f3221 Merge branch '1064-rss-date-npe' into 'master'
Fix NPE when some RSS items don't have dates and add test

Closes #1064

See merge request !591
2017-09-20 12:21:06 +00:00
Torsten Grote
c30bfa12ce Fix NPE when some RSS items don't have dates and add test 2017-09-20 09:11:06 -03:00
akwizgran
d0fc04251d Merge branch 'three-new-langs' into 'master'
Add Norwegian Bokmål, Occitan (post 1500) and Serbian

See merge request !593
2017-09-20 11:15:44 +00:00
akwizgran
dcbb41eb7a Merge branch '1069-forum-sharing-exception' into 'master'
Fix crash when sharing a forum while it was just shared with us

Closes #1069

See merge request !592
2017-09-20 11:14:20 +00:00
Allan Nordhøy
5c51259269 "Connection aborted!" no und 2017-09-19 19:39:57 +00:00
Allan Nordhøy
7eefa07052 Contact connections → contacts
by us → on your side
2017-09-19 18:56:22 +00:00
Torsten Grote
999bdf8866 Add Norwegian Bokmål, Occitan (post 1500) and Serbian 2017-09-19 14:47:39 -03:00
Torsten Grote
911c0c0fd9 Fix crash when sharing a forum while it was just shared with us 2017-09-19 14:30:57 -03:00
akwizgran
99d8cc64a6 Merge branch '1024-message-tree-npe' into 'master'
Don't add threaded messages to the UI before their parents

Closes #1024

See merge request !585
2017-09-19 15:37:58 +00:00
akwizgran
ba727d7568 Don't add threaded messages to the UI before their parents. 2017-09-19 16:31:27 +01:00
Torsten Grote
ed01048f9f Merge branch 'remove-old-bluetooth-code' into 'master'
Remove old Bluetooth code and location permission

See merge request !584
2017-09-19 14:16:13 +00:00
Torsten Grote
043ee3c58e Merge branch '1044-crash-when-setting-ringtone' into 'master'
Don't crash if the chosen ringtone can't be loaded

Closes #1044

See merge request !586
2017-09-19 13:11:44 +00:00
Torsten Grote
6e0af7deda Merge branch '1060-upgrade-tor' into 'master'
Upgrade Tor to 0.2.9.12

Closes #1060

See merge request !590
2017-09-19 12:14:55 +00:00
akwizgran
9591db2097 Upgrade Tor to 0.2.9.12.
Libevent 2.0.22-stable, OpenSSL 1.0.2l and GeoIP 2017-09-06.
2017-09-19 12:49:22 +01:00
akwizgran
329a4c64f6 Merge branch '1028-lost-reply-id' into 'master'
Keep the reply ID up to date in ThreadListActivity

Closes #1028

See merge request !587
2017-09-18 15:10:38 +00:00
Torsten Grote
79015bc5ae Merge branch '1042-catch-npe-when-getting-socket-streams' into 'master'
Catch NPE when getting socket input/output streams

Closes #1042

See merge request !589
2017-09-18 14:55:08 +00:00
akwizgran
27422ab9f9 Catch NPE when getting socket input/output streams.
Works around a bug in Android 7, fixed in 7.1.
2017-09-18 15:47:12 +01:00
Torsten Grote
abcb682498 Merge branch '1040-rss-feed-illegal-argument-exception' into 'master'
Catch IllegalArgumentException when parsing RSS feed

Closes #1040

See merge request !588
2017-09-18 14:38:22 +00:00
akwizgran
5044127c46 Catch IllegalArgumentException when parsing RSS feed. 2017-09-18 15:26:12 +01:00
akwizgran
0e4b8ca62e Keep the activity's reply ID up to date. 2017-09-18 15:13:16 +01:00
akwizgran
822017c69c Don't crash if the chosen ringtone can't be loaded. 2017-09-18 13:37:10 +01:00
akwizgran
eb6561b93d Updated translations for German, French and Russian. 2017-09-15 10:40:05 +01:00
akwizgran
eb9d0c00a8 Report Bluetooth LE and Wi-Fi Direct support. 2017-08-16 12:21:13 +01:00
Michael Rogers
d24b1884a2 Removed old Bluetooth code and the location permission it requires. 2017-08-11 12:42:47 +01:00
Michael Rogers
078534889e Bumped version number for beta release. 2017-08-04 15:16:51 +01:00
Torsten Grote
e92713006a Fix string in Spanish translation 2017-08-04 10:57:43 -03:00
akwizgran
18f43f3bc1 Merge branch '871-rss-feeds-lost' into 'master'
Fix bug where RSS feeds got lost when a fetching error occured

Closes #871

See merge request !583
2017-08-04 13:52:26 +00:00
akwizgran
a4118b40e1 Merge branch 'debug-build-alongside-beta' into 'master'
Make debug builds installable alongside official beta build

See merge request !582
2017-08-02 16:54:25 +00:00
Torsten Grote
de29fbc324 Fix bug where RSS feeds got lost when a fetching error occured 2017-08-01 15:32:51 -03:00
Torsten Grote
3197dcf9b5 Merge branch 'checked-camera-exceptions' into 'master'
Throw checked exceptions for camera errors

See merge request !580
2017-08-01 16:54:45 +00:00
akwizgran
35aad409fd Merge branch '994-notification-sound-delay' into 'master'
Always play a notification sound, if at least 2sec after last one

Closes #994

See merge request !581
2017-08-01 16:20:35 +00:00
Torsten Grote
08ce6a7331 Change app name for debug builds 2017-08-01 13:08:12 -03:00
Torsten Grote
33a0099065 Make debug builds installable alongside official beta build 2017-08-01 12:57:11 -03:00
Torsten Grote
34d20fafda Always play a notification sound, if at least 2sec after last one
This is the same behavior as Signal.
We might want to adjust the delay later on.

This is also introduces a new BriarNotificationBuilder as a first step
to clean up the Notification Manager code.
2017-08-01 12:47:11 -03:00
Michael Rogers
aafddcd0f0 Bumped version number for beta release (for real this time). 2017-08-01 16:43:47 +01:00
akwizgran
0d6983b4ef Throw checked exceptions for camera errors. 2017-08-01 15:56:20 +01:00
akwizgran
69bfb72171 Merge branch '1002-cam-get-params-npe' into 'master'
Catch RuntimeException when getting camera parameters

See merge request !579
2017-08-01 13:56:45 +00:00
Torsten Grote
1aa33ec9b2 Catch RuntimeException when getting camera parameters 2017-08-01 10:49:04 -03:00
akwizgran
6702df1e22 Merge branch '1008-qr-decoding-crash' into 'master'
Catch IllegalArgumentException when decoding QrCode

Closes #1008

See merge request !578
2017-08-01 13:36:09 +00:00
akwizgran
c1748c9a86 Bumped version number for beta release. 2017-08-01 14:32:05 +01:00
akwizgran
9df624c62a Merge branch '1009-camera-npe' into 'master'
Prevent NPE in CameraView

Closes #1009 and #997

See merge request !577
2017-08-01 13:29:33 +00:00
Torsten Grote
0ee6197d7f Catch IllegalArgumentException when decoding QrCode 2017-08-01 10:21:02 -03:00
Torsten Grote
b03a7dce3e Catch runtime exception when setting best camera parameters
Closes #997
2017-08-01 10:09:21 -03:00
Torsten Grote
6c59d7dd5f Prevent NPE in CameraView
This prevents crashes, but still might cause the camera to not show up
thus preventing the user from adding contacts.
2017-08-01 09:41:42 -03:00
Michael Rogers
050191f0ef Bumped version number for beta release. 2017-08-01 12:31:47 +01:00
akwizgran
4b5a19ce5d Merge branch 'update-translations' into 'master'
Update translations, add Turkish and Russian

See merge request !575
2017-08-01 09:28:17 +00:00
akwizgran
7c4dd991b9 Merge branch '1016-reblog-runtime-error' into 'master'
Runtime error fix due to window requests

Closes #1016 and #1007

See merge request !576
2017-08-01 09:25:39 +00:00
Ernir Erlingsson
8455569e88 moved window requests above onCreate 2017-07-30 22:42:03 +02:00
Torsten Grote
d25676559c Update translations, add Turkish and Russian 2017-07-29 11:03:51 -03:00
Michael Rogers
a9437f7985 Bumped version number for beta release. 2017-07-28 18:01:19 +01:00
akwizgran
8141a97fc9 Merge branch '1015-recent-emoji-crash' into 'master'
Prevent a crash caused by empty emoji

Closes #1015

See merge request !571
2017-07-28 16:59:02 +00:00
Torsten Grote
db842bd7e4 Prevent a crash caused by empty emoji
The crash happens because the serialization of recently used emoji uses
';' to separate the emojis.
One of the ASCII emojis however has a ';' in the beginning.
When this one is used by the user,
it causes an empty string to be returned when deserializing.

This commit prevents the crash by changing the separator to a tab.
It uses a different settings string to store the emoji,
so users will lose the list of recently used emoji when they update to
this version.

PS. That wasn't my idea ;)
2017-07-28 13:49:51 -03:00
Torsten Grote
6dbec3a864 Merge branch 'enable-logging-for-beta-builds' into 'master'
Enable logging for beta builds

See merge request !573
2017-07-28 15:58:01 +00:00
akwizgran
29f658cf4d Merge branch '1006-blog-crash' into 'master'
Prevent crash in blog by ensuring a listener always exists

Closes #1006

See merge request !574
2017-07-28 15:53:43 +00:00
akwizgran
ca83744a84 Merge branch 'close-feed-stream' into 'master'
Close InputStream from RSS feed and prevent NPE

See merge request !572
2017-07-28 15:48:01 +00:00
Torsten Grote
d91a9e2be4 Prevent crash in blog by ensuring a listener always exists 2017-07-28 12:42:56 -03:00
akwizgran
8408c3f467 Enable logging for beta builds.
Some devices were logging and others not, due to the log level being set in the SplashScreenActivity constructor.
2017-07-28 16:41:24 +01:00
Torsten Grote
544c83a64c Close InputStream from RSS feed and prevent NPE 2017-07-28 10:38:01 -03:00
Michael Rogers
3800cd5e4f Bumped version number for beta release. 2017-07-28 11:17:09 +01:00
akwizgran
259f2cd419 Merge branch '993-fix-full-text-blog-posts' into 'master'
Show blog posts with full text when clicked

Closes #993

See merge request !570
2017-07-26 11:01:38 +00:00
Torsten Grote
20eb022c36 Show blog posts with full text when clicked
This fixes a regression that was introduced in !551.
2017-07-25 15:50:04 -03:00
akwizgran
531e555b52 Bumped version number for beta release. 2017-07-25 18:43:19 +01:00
akwizgran
a9024aa34b Merge branch '955-shared-with-update' into 'master'
Fix "shared with" counter not being updated

Closes #955

See merge request !569
2017-07-25 17:40:40 +00:00
akwizgran
d4e3b7842c Merge branch 'blog-sharing-tests' into 'master'
Add unit tests for BlogSharingManager

See merge request !567
2017-07-25 17:40:29 +00:00
Torsten Grote
167fddfbcc Add unit tests for BlogSharingManager 2017-07-25 12:45:36 -03:00
Torsten Grote
a48d642648 Fix UI bug in CreateForumActivity and adapt group creation 2017-07-25 12:32:53 -03:00
Torsten Grote
9a70f054c7 Use proper GroupId when reacting to accepted invitations
Fixes #955
2017-07-25 10:03:13 -03:00
Torsten Grote
ca43d13bd6 Merge branch 'inject-properties-module-eager-singletons' into 'master'
Inject properties module's eager singletons

See merge request !568
2017-07-25 12:55:59 +00:00
akwizgran
5b71004179 Inject properties module's eager singletons. 2017-07-25 13:49:15 +01:00
akwizgran
63befccdbf Bumped expiry time and version number for beta release. 2017-07-21 11:52:09 +01:00
akwizgran
4ecf7c02d0 Merge branch '979-duplicate-blog-session' into 'master'
Fix Blog Sharing Sessions

Closes #979

See merge request !566
2017-07-21 10:27:21 +00:00
Torsten Grote
f25badc18c Move responsibility for pre-sharing blogs to sharing manager
to have all the code related to that in one place,
so it is easier to maintain and to spot bugs.

This also checks that only blogs without an existing sharing session
are shared and initialized again.
It extends an existing test to catch the missing check.

This removes some debugging information from the previous commit
to not leak private information via the sharing sessions.

Fixes #979
2017-07-17 14:07:47 -03:00
akwizgran
6e931e9ba5 Bump version number and expiry date for beta release. 2017-07-17 10:32:46 +01:00
akwizgran
7e749124bf Merge branch '617-protocol-versioning' into 'master'
Protocol versioning for BTP

See merge request !557
2017-07-17 09:24:35 +00:00
akwizgran
5822eb7808 Remove 'this'. 2017-07-17 10:16:50 +01:00
Torsten Grote
7a7e086541 Merge branch '982-name-not-found-exception' into 'master'
Use fully-qualified class names in manifest

Closes #982

See merge request !565
2017-07-14 12:19:50 +00:00
akwizgran
abab3167c2 Use fully-qualified class names in manifest.
This prevents a crash on Android 4 when the package name in build.gradle differs from the name in the manifest.
2017-07-14 12:01:55 +01:00
Torsten Grote
8d08570568 Merge branch '977-crash-when-opening-rss-blog' into 'master'
Create correct shareable for RSS blogs

Closes #977

See merge request !564
2017-07-07 17:42:54 +00:00
akwizgran
2007078f13 Added test for sharing an RSS blog. 2017-07-07 18:02:15 +01:00
akwizgran
dfb71a7978 Merge branch '942-block-blog-notifications' into 'master'
Block blog notifications when viewing combined feed

See merge request !563
2017-07-07 16:32:07 +00:00
akwizgran
480b0e3a03 Create correct shareable for RSS blogs.
Also removed "personal blog" wording that doesn't apply to RSS blogs.
2017-07-07 17:30:44 +01:00
akwizgran
8f8751f4ac Block blog notifications when viewing combined feed. 2017-07-07 15:34:00 +01:00
akwizgran
de2ea112ee Merge branch '933-beta-warning' into 'master'
Show Beta Expiry Warning

Closes #933

See merge request !559
2017-07-07 12:19:04 +00:00
Torsten Grote
6f99a53fd9 Show beta expiry warning in main activity 2017-07-07 09:12:07 -03:00
akwizgran
a8a9b9032d Merge branch 'warn_on_extra_translation' into 'master'
Show a warning instead of throwing an error for unused translations

See merge request !561
2017-07-07 12:01:23 +00:00
goapunk
6b15fb89de Show a warning instead of throwing an error for unused translations
Signed-off-by: goapunk <noobie@goapunks.net>
2017-07-07 12:19:44 +02:00
Torsten Grote
a711d6b8a1 Merge branch '106-fix-package-name' into 'master'
Set applicationId to match app_package

See merge request !560
2017-07-05 13:46:30 +00:00
akwizgran
5678f8aaa4 Update Robolectric tests so they can find resources. 2017-07-05 14:40:03 +01:00
akwizgran
2fe37f6c26 Set applicationId to match app_package. 2017-07-05 14:17:35 +01:00
Torsten Grote
a879747968 Translation Update 2017-07-05 09:50:14 -03:00
akwizgran
95e8fd7ee0 Merge branch 'notify_more_agressively' into 'master'
Notify more aggressively

See merge request !555
2017-07-05 11:36:37 +00:00
goapunk
4416aaaa4c Notify more aggressively
* Add setting to show notification on the lockscreen
* Don't block notifications in the contact-/-group/-forum/-bloglist

Signed-off-by: goapunk <noobie@goapunks.net>
2017-07-05 13:05:21 +02:00
Torsten Grote
500d5f0efe Merge branch '822-group-creation-workflow' into 'master'
Revisit private group creation workflow

Closes #822

See merge request !553
2017-07-04 17:19:11 +00:00
akwizgran
fc8978fd90 Create forum when button is clicked. 2017-07-04 16:17:59 +01:00
akwizgran
73df126bd4 Create and share private groups separately, as with forums. 2017-07-04 16:17:58 +01:00
akwizgran
9146488c7d Use same layout and behaviour for creating groups and forums. 2017-07-04 16:17:58 +01:00
akwizgran
613a7fe376 Merge branch '962-removing-contacts' into 'master'
Add test where two contacts remove each other

Closes #962

See merge request !558
2017-07-04 15:14:27 +00:00
akwizgran
ecb62f00d4 Code cleanup. 2017-07-04 16:09:32 +01:00
akwizgran
c4540a03cd Protocol versioning for BTP. 2017-07-04 16:09:32 +01:00
akwizgran
3e31da99b5 Merge branch '106-beta-namespace' into 'master'
Change app name and package name for beta release

Closes #106

See merge request !545
2017-07-04 14:40:47 +00:00
Torsten Grote
098c1d0b1e Add test where two contacts remove each other 2017-07-04 11:30:17 -03:00
akwizgran
178e908c86 Use a different package name and app name for beta builds. 2017-07-03 18:20:05 +01:00
akwizgran
ecf7cf14ae Merge branch '957-rss-url-case-sensitivity' into 'master'
Handle RSS URLs case-insensitively

Closes #957

See merge request !544
2017-07-03 16:22:52 +00:00
akwizgran
09e2a15a73 Merge branch '951-remove-visibility-indicators' into 'master'
Remove visibility indicators from private group join messages

Closes #951

See merge request !546
2017-07-03 16:22:31 +00:00
akwizgran
ab387860a6 Removed visibility indicators from private group member list. 2017-07-03 16:37:29 +01:00
akwizgran
f63fc94f2b Removed visibility indicators from private group join messages. 2017-07-03 16:37:29 +01:00
Michael Rogers
41e5928cca Validate and normalise RSS URLs. 2017-07-03 16:36:50 +01:00
Torsten Grote
8303175494 Merge branch 'use-f-droid-base-docker-image' into 'master'
Use F-Droid's base Docker image

See merge request !556
2017-07-03 15:35:28 +00:00
akwizgran
151eb6935b Use F-Droid's base Docker image. 2017-07-03 16:17:03 +01:00
akwizgran
6a419c0c7b Merge branch '968-downgrade-h2' into 'master'
Downgrade H2 to 1.4.192

Closes #968

See merge request !550
2017-07-03 11:51:35 +00:00
akwizgran
1795b32121 Downgrade H2 to 1.4.192. 2017-07-03 12:36:45 +01:00
akwizgran
01971768ce Merge branch '970_settings_use_summaries' into 'master'
Split notification settings into title and summary.

Closes #970

See merge request !554
2017-07-03 11:19:15 +00:00
akwizgran
ef7483ab01 Merge branch '787-tapping-blog-author-opens-same-blog' into 'master'
Don't reopen the same blog when the author is tapped

Closes #787

See merge request !551
2017-07-03 10:43:15 +00:00
akwizgran
527d11473d Merge branch '805-introduction-message-layout' into 'master'
Use smaller layout for introduction message screen

Closes #805

See merge request !552
2017-07-03 10:40:10 +00:00
akwizgran
775dadc9a0 Merge branch '904-notify-tor-controller' into 'master'
Notify Tor controller if Tor has crashed

Closes #904

See merge request !543
2017-07-03 09:29:04 +00:00
akwizgran
800b10a988 Merge branch '956-tap-protection-ux' into 'master'
Don't show tap protection dialog until it's needed

Closes #956

See merge request !548
2017-07-03 09:22:31 +00:00
akwizgran
c977bf047d Removed overrides of showScreenFilterWarning. 2017-07-03 10:08:36 +01:00
goapunk
660a25f21d Split notification settings into title and summary.
Signed-off-by: goapunk <noobie@goapunks.net>
2017-07-03 09:27:07 +02:00
Torsten Grote
e7fd6d23af Merge branch '962-check-blog-subscription-when-removing-contact' into 'master'
Check personal blog subscription when removing contact

See merge request !549
2017-06-30 20:57:52 +00:00
Torsten Grote
46982897f0 Merge branch '963-load-thread-list-messages-on-start' into 'master'
Load messages each time activity starts

See merge request !547
2017-06-30 20:53:13 +00:00
Torsten Grote
d24de68d64 Merge branch '574-upgrade-tor' into 'master'
Upgrade Tor to 0.2.9

Closes #574

See merge request !542
2017-06-30 20:36:22 +00:00
akwizgran
7514c46a3f Use smaller layout for introduction message screen. 2017-06-30 16:23:16 +01:00
akwizgran
6632c0f8e3 Don't reopen the same blog when the author is tapped. 2017-06-30 14:24:32 +01:00
akwizgran
79aafcda69 Fixed a test, added a regression test. 2017-06-30 12:58:44 +01:00
akwizgran
05af21e8dc Check personal blog subscription when removing contact. 2017-06-30 12:04:34 +01:00
akwizgran
0dc62cbbdc Fixed a test. 2017-06-30 10:01:35 +01:00
akwizgran
f3a084cfd2 Removed option to remember shown overlay apps. 2017-06-29 18:47:36 +01:00
akwizgran
8b32f82566 Don't show tap protection dialog until it's needed. 2017-06-29 18:18:39 +01:00
akwizgran
d598b6ed44 Load messages each time activity starts. 2017-06-26 16:21:25 +01:00
akwizgran
f5dc6f24b9 Bumped expiry date to 1 September 2017. 2017-06-26 14:50:58 +01:00
akwizgran
37454392da Update Tor binaries to 0.2.9.11. 2017-06-23 22:06:00 +01:00
akwizgran
de7f9111d3 Update Tor patch to 0.2.9.11. 2017-06-23 15:23:26 +01:00
akwizgran
96d2889a6c Notify Tor controller if Tor has crashed. 2017-06-23 14:57:00 +01:00
Torsten Grote
f6412d1e9a Merge branch 'spongy-castle-1-56' into 'master'
Upgrade Spongy Castle and some other dependencies

See merge request !541
2017-06-15 17:37:22 +00:00
akwizgran
b377cd6b1c Upgrade Spongy Castle and some other dependencies. 2017-06-12 17:44:08 +01:00
Ernir Erlingsson
f6cdbda5bb Merge branch 'master' of https://code.briarproject.org/akwizgran/briar 2017-05-29 11:06:16 +02:00
Ernir Erlingsson
855c600a3e hotfix list restore NPE for user testing 2017-05-29 11:05:46 +02:00
akwizgran
ea6e8303b0 Merge branch '954_dont_show_on_startup_failure' into 'master'
Don't show screenfilter warning in StartupFailureActivity

See merge request !539
2017-05-29 09:05:18 +00:00
goapunk
d4934040d9 Don't show screenfilter warning in StartupFailureActivity
Signed-off-by: goapunk <noobie@goapunks.net>
2017-05-22 12:40:33 +02:00
akwizgran
3449677b24 Bumped version number and expiry date. 2017-05-19 12:07:29 +01:00
akwizgran
1ad3a6646e Merge branch '941-store-correct-parent-id' into 'master'
Store correct original parent ID when rewrapping blog posts

See merge request !534
2017-05-12 09:53:27 +00:00
akwizgran
2d10f6b2bd Merge branch '884-emoji-text-view-layout-bug' into 'master'
Remove ellipsizing support from EmojiTextView

Closes #884

See merge request !533
2017-05-12 09:35:33 +00:00
akwizgran
5b05424d83 Merge branch 'master' into '941-store-correct-parent-id'
# Conflicts:
#   briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
2017-05-12 09:34:24 +00:00
akwizgran
0826022d82 Merge branch 'bring_annotations_in_line' into 'master'
Bring nullable annotation imports in line

See merge request !536
2017-05-12 09:33:00 +00:00
akwizgran
a901bfb9cb Merge branch '948-vector-crash' into 'master'
Remove scientific notation from vector drawables to prevent crashes

Closes #948

See merge request !537
2017-05-12 09:28:59 +00:00
akwizgran
03cdce122a Merge branch '947-bluetooth-address-crash' into 'master'
Don't crash on empty bluetooth addresses

See merge request !538
2017-05-12 09:26:57 +00:00
goapunk
f2e0e16969 Bring nullable annotation imports in line
Signed-off-by: goapunk <noobie@goapunks.net>
2017-05-12 10:06:56 +02:00
Torsten Grote
0c441e2ff3 Don't crash on empty bluetooth addresses 2017-05-10 15:06:09 -03:00
Torsten Grote
21302304a5 Remove scientific notation from vector drawables to prevent crashes
Details: http://stackoverflow.com/a/40829348
2017-05-10 14:56:59 -03:00
Torsten Grote
6839d8b844 Merge branch 'wifi-manager-memory-leak' into 'master'
Use application context to get WifiManager

See merge request !535
2017-05-10 17:01:52 +00:00
Torsten Grote
aee65a716c Merge branch '798-remove-contact-blogs' into 'master'
Allow to remove pre-shared blogs of our contacts

Closes #798

See merge request !529
2017-05-10 16:58:38 +00:00
Torsten Grote
6a07d8f2c9 Allow to remove pre-shared blogs of our contacts 2017-05-10 13:50:07 -03:00
Ernir Erlingsson
3c1ea81cd0 Merge branch '853-disabled-menu-items' into 'master'
Remove theme default color override

Closes #853

See merge request !527
2017-05-06 20:26:15 +00:00
Ernir Erlingsson
025f417bc7 Merge branch '894-list-position-restore' into 'master'
save and restore list position for threaded lists

Closes #894 and #946

See merge request !528
2017-05-06 19:37:02 +00:00
Ernir Erlingsson
c9dcd906c9 final pre-merge fixes 2017-05-06 21:36:25 +02:00
Ernir Erlingsson
7024e04d15 fixed final akwizgran comments 2017-05-06 21:31:53 +02:00
akwizgran
0b8ac947db Use application context to get WifiManager. 2017-05-05 15:43:27 +01:00
Ernir Erlingsson
948410a064 fixed unread buttons for threaded lists and akwizgran's comments 2017-05-05 14:49:53 +02:00
akwizgran
2841339cac Merge branch '468-ci' into 'master'
Set up basic CI

Closes #468

See merge request !530
2017-05-05 09:11:06 +00:00
Torsten Grote
e8e82bd805 Update Translations 2017-05-04 10:26:49 -03:00
Ernir Erlingsson
6876f40a0e Merge branch 'fix_groupname_validation' into 'master'
Fix groupname validation

See merge request !531
2017-05-04 07:26:55 +00:00
Ernir Erlingsson
5f4e1ecdfd improvements after code review #1
fix
2017-05-02 11:42:55 +02:00
Ernir Erlingsson
044719432a list position save and restore now implemented for threaded lists 2017-05-02 11:42:55 +02:00
Ernir Erlingsson
d1a929da85 bumped expire date 2017-05-02 11:42:15 +02:00
goapunk
2a8978a60d fix group name validation
Signed-off-by: goapunk <noobie@goapunks.net>
2017-04-29 16:49:37 +02:00
Torsten Grote
c0afad7a26 Set up basic CI 2017-04-28 13:24:41 -03:00
akwizgran
37281c6c23 Remove ellipsizing support from EmojiTextView.
This is a workaround for a layout bug.
2017-04-28 15:39:24 +01:00
akwizgran
76a5e25656 Added tests for wrapping and rewrapping blog posts. 2017-04-19 12:16:18 +01:00
akwizgran
3575b74837 Store correct original parent ID when rewrapping blog posts. 2017-04-19 12:15:34 +01:00
Torsten Grote
f1c7996960 Remove theme default color override 2017-04-18 09:14:00 -03:00
275 changed files with 9141 additions and 5304 deletions

19
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,19 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- export GRADLE_USER_HOME=$PWD/.gradle
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;23.0.3"
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/

View File

@@ -12,8 +12,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 22 targetSdkVersion 22
versionCode 1 versionCode 1611
versionName "1.0" versionName "0.16.11"
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 {

View File

@@ -39,7 +39,7 @@ public class AndroidPluginModule {
EventBus eventBus) { EventBus eventBus) {
Context appContext = app.getApplicationContext(); Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor, DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
androidExecutor, appContext, random, backoffFactory); androidExecutor, appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext, DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
locationUtils, reporter, eventBus, torSocketFactory, locationUtils, reporter, eventBus, torSocketFactory,
backoffFactory); backoffFactory);

View File

@@ -11,8 +11,9 @@ 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.event.Event;
import org.briarproject.bramble.api.event.EventListener;
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;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -23,6 +24,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
@@ -30,23 +33,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,8 +55,6 @@ 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;
@@ -75,14 +67,10 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class DroidtoothPlugin implements DuplexPlugin { class DroidtoothPlugin implements DuplexPlugin, EventListener {
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;
@@ -166,9 +154,7 @@ class DroidtoothPlugin implements DuplexPlugin {
} else { } else {
// Enable Bluetooth if settings allow // Enable Bluetooth if settings allow
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) { if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
wasEnabledByUs = true; enableAdapter();
if (adapter.enable()) LOG.info("Enabling Bluetooth");
else LOG.info("Could not enable Bluetooth");
} else { } else {
LOG.info("Not enabling Bluetooth"); LOG.info("Not enabling Bluetooth");
} }
@@ -259,13 +245,27 @@ class DroidtoothPlugin implements DuplexPlugin {
return new DroidtoothTransportConnection(this, s); return new DroidtoothTransportConnection(this, s);
} }
private void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override @Override
public void stop() { public void stop() {
running = false; running = false;
if (receiver != null) appContext.unregisterReceiver(receiver); if (receiver != null) appContext.unregisterReceiver(receiver);
tryToClose(socket); tryToClose(socket);
// Disable Bluetooth if we enabled it and it's still enabled disableAdapter();
if (wasEnabledByUs && adapter.isEnabled()) { }
private void disableAdapter() {
if (adapter != null && adapter.isEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth"); if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth"); else LOG.info("Could not disable Bluetooth");
} }
@@ -363,8 +363,7 @@ class DroidtoothPlugin implements DuplexPlugin {
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null; if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties().get(c); TransportProperties p = callback.getRemoteProperties(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null; if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
@@ -374,90 +373,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;
@@ -472,7 +387,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(
@@ -513,6 +428,33 @@ class DroidtoothPlugin implements DuplexPlugin {
return StringUtils.macToString(mac); return StringUtils.macToString(mac);
} }
@Override
public void eventOccurred(Event e) {
if (e instanceof EnableBluetoothEvent) {
enableAdapterAsync();
} else if (e instanceof DisableBluetoothEvent) {
disableAdapterAsync();
}
}
private void enableAdapterAsync() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
enableAdapter();
}
});
}
private void disableAdapterAsync() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
disableAdapter();
}
});
}
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@Override @Override
@@ -536,115 +478,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;

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.droidtooth;
import android.content.Context; import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -31,15 +32,18 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Context appContext;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public DroidtoothPluginFactory(Executor ioExecutor, public DroidtoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext, AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, BackoffFactory backoffFactory) { SecureRandom secureRandom, EventBus eventBus,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -57,7 +61,10 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(DuplexPluginCallback callback) { public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext, DroidtoothPlugin plugin = new DroidtoothPlugin(ioExecutor,
secureRandom, backoff, callback, MAX_LATENCY); androidExecutor, appContext, secureRandom, backoff, callback,
MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
} }
} }

View File

@@ -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;
@@ -56,6 +55,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner; import java.util.Scanner;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -85,13 +85,13 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER; 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_NETWORK_WIFI;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion; import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class TorPlugin implements DuplexPlugin, EventHandler, EventListener { class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final String PROP_ONION = "onion";
private static final String[] EVENTS = { private static final String[] EVENTS = {
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR" "CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
}; };
@@ -539,16 +539,21 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
// TODO: Pass properties to connectAndCallBack() Map<ContactId, TransportProperties> remote =
for (ContactId c : callback.getRemoteProperties().keySet()) callback.getRemoteProperties();
if (!connected.contains(c)) connectAndCallBack(c); for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
}
} }
private void connectAndCallBack(final ContactId c) { private void connectAndCallBack(final ContactId c,
final TransportProperties p) {
ioExecutor.execute(new Runnable() { ioExecutor.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
DuplexTransportConnection d = createConnection(c); if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
callback.outgoingConnectionCreated(c, d); callback.outgoingConnectionCreated(c, d);
@@ -560,8 +565,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null; if (!isRunning()) return null;
TransportProperties p = callback.getRemoteProperties().get(c); return createConnection(callback.getRemoteProperties(c));
if (p == null) return null; }
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
String onion = p.get(PROP_ONION); String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null; if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) { if (!ONION.matcher(onion).matches()) {
@@ -589,17 +597,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;

View File

@@ -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

View File

@@ -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',

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -4,5 +4,10 @@ public interface LanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.lan"); TransportId ID = new TransportId("org.briarproject.bramble.lan");
// a transport property (shared with contacts)
String PROP_IP_PORTS = "ipPorts";
// a local setting
String PREF_LAN_IP_PORTS = "ipPorts"; String PREF_LAN_IP_PORTS = "ipPorts";
} }

View File

@@ -29,6 +29,11 @@ public interface PluginCallback {
*/ */
Map<ContactId, TransportProperties> getRemoteProperties(); Map<ContactId, TransportProperties> getRemoteProperties();
/**
* Returns the plugin's remote transport properties for the given contact.
*/
TransportProperties getRemoteProperties(ContactId c);
/** /**
* Merges the given settings with the namespaced settings * Merges the given settings with the namespaced settings
*/ */

View File

@@ -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.
*/ */

View File

@@ -4,6 +4,8 @@ public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor"); TransportId ID = new TransportId("org.briarproject.bramble.tor");
String PROP_ONION = "onion";
int SOCKS_PORT = 59050; int SOCKS_PORT = 59050;
int CONTROL_PORT = 59051; int CONTROL_PORT = 59051;
@@ -16,4 +18,5 @@ public interface TorConstants {
int PREF_TOR_NETWORK_NEVER = 0; int PREF_TOR_NETWORK_NEVER = 0;
int PREF_TOR_NETWORK_WIFI = 1; int PREF_TOR_NETWORK_WIFI = 1;
int PREF_TOR_NETWORK_ALWAYS = 2; int PREF_TOR_NETWORK_ALWAYS = 2;
} }

View File

@@ -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.
*/ */

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that asks the Bluetooth plugin to disable the Bluetooth adapter if
* we previously enabled it.
*/
@Immutable
@NotNullByDefault
public class DisableBluetoothEvent extends Event {
}

View File

@@ -0,0 +1,14 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event asks the Bluetooth plugin to enable the Bluetooth adapter.
*/
@Immutable
@NotNullByDefault
public class EnableBluetoothEvent extends Event {
}

View File

@@ -49,6 +49,13 @@ public interface TransportPropertyManager {
Map<ContactId, TransportProperties> getRemoteProperties(TransportId t) Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
throws DbException; throws DbException;
/**
* Returns the remote transport properties for the given contact and
* transport.
*/
TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException;
/** /**
* Merges the given properties with the existing local properties for the * Merges the given properties with the existing local properties for the
* given transport. * given transport.

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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.

View File

@@ -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);
}
}
} }

View File

@@ -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

View File

@@ -8,6 +8,7 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetDecoder;
import java.util.Collection; import java.util.Collection;
import java.util.Random;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -27,6 +28,7 @@ public class StringUtils {
'0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
}; };
private static final Random random = new Random();
public static boolean isNullOrEmpty(@Nullable String s) { public static boolean isNullOrEmpty(@Nullable String s) {
return s == null || s.length() == 0; return s == null || s.length() == 0;
@@ -139,4 +141,12 @@ public class StringUtils {
} }
return s.toString(); return s.toString();
} }
public static String getRandomString(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++)
c[i] = (char) ('a' + random.nextInt(26));
return new String(c);
}
} }

View File

@@ -34,13 +34,6 @@ public class TestUtils {
return getRandomBytes(UniqueId.LENGTH); return getRandomBytes(UniqueId.LENGTH);
} }
public static String getRandomString(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++)
c[i] = (char) ('a' + random.nextInt(26));
return new String(c);
}
public static SecretKey getSecretKey() { public static SecretKey getSecretKey() {
return new SecretKey(getRandomBytes(SecretKey.LENGTH)); return new SecretKey(getRandomBytes(SecretKey.LENGTH));
} }

View File

@@ -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.58.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:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c' 'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
] ]
} }

View File

@@ -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());

View File

@@ -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);

View File

@@ -4,7 +4,6 @@ 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;
@@ -41,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 {
@@ -66,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");
@@ -169,14 +166,6 @@ class CryptoComponentImpl implements CryptoComponent {
return new SecretKey(b); return new SecretKey(b);
} }
@Override
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
byte[] seed = new byte[INT_32_BYTES * 2];
ByteUtils.writeUint32(seed1, seed, 0);
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
return new PseudoRandomImpl(seed);
}
@Override @Override
public SecureRandom getSecureRandom() { public SecureRandom getSecureRandom() {
return secureRandom; return secureRandom;
@@ -248,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) {
@@ -412,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
@@ -421,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

View File

@@ -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);
} }

View File

@@ -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);
} }
} }

View File

@@ -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);
} }
} }

View File

@@ -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];
@@ -114,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);

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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>();
@@ -291,6 +283,16 @@ class PluginManagerImpl implements PluginManager, Service {
} }
} }
@Override
public TransportProperties getRemoteProperties(ContactId c) {
try {
return transportPropertyManager.getRemoteProperties(c, id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return new TransportProperties();
}
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
try { try {

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.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;
@@ -35,6 +34,7 @@ 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.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_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;
@@ -45,7 +45,6 @@ class LanTcpPlugin extends TcpPlugin {
Logger.getLogger(LanTcpPlugin.class.getName()); Logger.getLogger(LanTcpPlugin.class.getName());
private static final int MAX_ADDRESSES = 4; private static final int MAX_ADDRESSES = 4;
private static final String PROP_IP_PORTS = "ipPorts";
private static final String SEPARATOR = ","; private static final String SEPARATOR = ",";
LanTcpPlugin(Executor ioExecutor, Backoff backoff, LanTcpPlugin(Executor ioExecutor, Backoff backoff,
@@ -126,9 +125,8 @@ class LanTcpPlugin extends TcpPlugin {
} }
@Override @Override
protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) { protected List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p = callback.getRemoteProperties().get(c); TransportProperties p) {
if (p == null) return Collections.emptyList();
return parseSocketAddresses(p.get(PROP_IP_PORTS)); return parseSocketAddresses(p.get(PROP_IP_PORTS));
} }

View File

@@ -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;
@@ -10,6 +9,7 @@ import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
@@ -25,6 +25,8 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -67,11 +69,11 @@ abstract class TcpPlugin implements DuplexPlugin {
protected abstract void setLocalSocketAddress(InetSocketAddress a); protected abstract void setLocalSocketAddress(InetSocketAddress a);
/** /**
* Returns zero or more socket addresses for connecting to the given * Returns zero or more socket addresses for connecting to a contact with
* contact. * the given transport properties.
*/ */
protected abstract List<InetSocketAddress> getRemoteSocketAddresses( protected abstract List<InetSocketAddress> getRemoteSocketAddresses(
ContactId c); TransportProperties p);
/** /**
* Returns true if connections to the given address can be attempted. * Returns true if connections to the given address can be attempted.
@@ -208,16 +210,21 @@ abstract class TcpPlugin implements DuplexPlugin {
public void poll(Collection<ContactId> connected) { public void poll(Collection<ContactId> connected) {
if (!isRunning()) return; if (!isRunning()) return;
backoff.increment(); backoff.increment();
// TODO: Pass properties to connectAndCallBack() Map<ContactId, TransportProperties> remote =
for (ContactId c : callback.getRemoteProperties().keySet()) callback.getRemoteProperties();
if (!connected.contains(c)) connectAndCallBack(c); for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
}
} }
private void connectAndCallBack(final ContactId c) { private void connectAndCallBack(final ContactId c,
final TransportProperties p) {
ioExecutor.execute(new Runnable() { ioExecutor.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
DuplexTransportConnection d = createConnection(c); if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
callback.outgoingConnectionCreated(c, d); callback.outgoingConnectionCreated(c, d);
@@ -229,7 +236,12 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
if (!isRunning()) return null; if (!isRunning()) return null;
for (InetSocketAddress remote : getRemoteSocketAddresses(c)) { return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
if (!isConnectable(remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress(); SocketAddress local = socket.getLocalSocketAddress();
@@ -281,17 +293,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;

View File

@@ -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

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.tcp; package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -78,9 +77,8 @@ class WanTcpPlugin extends TcpPlugin {
} }
@Override @Override
protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) { protected List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p = callback.getRemoteProperties().get(c); TransportProperties p) {
if (p == null) return Collections.emptyList();
InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT)); InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT));
if (parsed == null) return Collections.emptyList(); if (parsed == null) return Collections.emptyList();
return Collections.singletonList(parsed); return Collections.singletonList(parsed);

View File

@@ -160,35 +160,52 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public Map<ContactId, TransportProperties> getRemoteProperties( public Map<ContactId, TransportProperties> getRemoteProperties(
TransportId t) throws DbException { TransportId t) throws DbException {
Map<ContactId, TransportProperties> remote =
new HashMap<ContactId, TransportProperties>();
Transaction txn = db.startTransaction(true);
try { try {
Map<ContactId, TransportProperties> remote = for (Contact c : db.getContacts(txn))
new HashMap<ContactId, TransportProperties>(); remote.put(c.getId(), getRemoteProperties(txn, c, t));
Transaction txn = db.startTransaction(true); db.commitTransaction(txn);
try { } finally {
for (Contact c : db.getContacts(txn)) { db.endTransaction(txn);
// Don't return properties for inactive contacts }
if (!c.isActive()) continue; return remote;
Group g = getContactGroup(c); }
// Find the latest remote update
LatestUpdate latest = findLatest(txn, g.getId(), t, false); private TransportProperties getRemoteProperties(Transaction txn, Contact c,
if (latest != null) { TransportId t) throws DbException {
// Retrieve and parse the latest remote properties // Don't return properties for inactive contacts
BdfList message = clientHelper.getMessageAsList(txn, if (!c.isActive()) return new TransportProperties();
latest.messageId); Group g = getContactGroup(c);
if (message == null) throw new DbException(); try {
remote.put(c.getId(), parseProperties(message)); // Find the latest remote update
} LatestUpdate latest = findLatest(txn, g.getId(), t, false);
} if (latest == null) return new TransportProperties();
db.commitTransaction(txn); // Retrieve and parse the latest remote properties
} finally { BdfList message =
db.endTransaction(txn); clientHelper.getMessageAsList(txn, latest.messageId);
} if (message == null) throw new DbException();
return remote; return parseProperties(message);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
} }
@Override
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException {
TransportProperties p;
Transaction txn = db.startTransaction(true);
try {
p = getRemoteProperties(txn, db.getContact(txn, c), t);
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return p;
}
@Override @Override
public void mergeLocalProperties(TransportId t, TransportProperties p) public void mergeLocalProperties(TransportId t, TransportProperties p)
throws DbException { throws DbException {

View File

@@ -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);

View File

@@ -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();

View File

@@ -57,8 +57,8 @@ class SocksSocket extends Socket {
// 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);

View File

@@ -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));
} }
} }

View File

@@ -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));
} }
} }

View File

@@ -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

View File

@@ -20,7 +20,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
@@ -65,7 +65,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
new Message(messageId, groupId, timestamp, rawMessage); new Message(messageId, groupId, timestamp, rawMessage);
private final Metadata metadata = new Metadata(); private final Metadata metadata = new Metadata();
private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42)); private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
private final String label = TestUtils.getRandomString(5); private final String label = StringUtils.getRandomString(5);
public ClientHelperImplTest() { public ClientHelperImplTest() {
clientHelper = clientHelper =

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider; import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test; import org.junit.Test;
import java.util.Arrays; import java.util.Arrays;
@@ -15,7 +16,7 @@ public class HashTest extends BrambleTestCase {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final String label = TestUtils.getRandomString(42); private final String label = StringUtils.getRandomString(42);
private final byte[] inputBytes = TestUtils.getRandomBytes(123); private final byte[] inputBytes = TestUtils.getRandomBytes(123);
private final byte[] inputBytes1 = TestUtils.getRandomBytes(234); private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
private final byte[] inputBytes2 = new byte[0]; private final byte[] inputBytes2 = new byte[0];
@@ -40,7 +41,7 @@ public class HashTest extends BrambleTestCase {
@Test @Test
public void testDifferentLabelsProduceDifferentHashes() { public void testDifferentLabelsProduceDifferentHashes() {
String label2 = TestUtils.getRandomString(42); String label2 = StringUtils.getRandomString(42);
byte[] hash1 = crypto.hash(label, inputBytes, inputBytes1, inputBytes2); byte[] hash1 = crypto.hash(label, inputBytes, inputBytes1, inputBytes2);
byte[] hash2 = byte[] hash2 =
crypto.hash(label2, inputBytes, inputBytes1, inputBytes2); crypto.hash(label2, inputBytes, inputBytes1, inputBytes2);

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.engines.Salsa20Engine; import org.spongycastle.crypto.engines.Salsa20Engine;
@@ -11,11 +10,11 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
class PseudoRandomImpl implements PseudoRandom { class PseudoRandom {
private final Salsa20Engine cipher = new Salsa20Engine(); private final Salsa20Engine cipher = new Salsa20Engine();
PseudoRandomImpl(byte[] seed) { PseudoRandom(byte[] seed) {
// Hash the seed to produce a 32-byte key // Hash the seed to produce a 32-byte key
byte[] key = new byte[32]; byte[] key = new byte[32];
Digest digest = new Blake2sDigest(); Digest digest = new Blake2sDigest();
@@ -26,8 +25,7 @@ class PseudoRandomImpl implements PseudoRandom {
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce)); cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
} }
@Override byte[] nextBytes(int length) {
public byte[] nextBytes(int length) {
byte[] in = new byte[length], out = new byte[length]; byte[] in = new byte[length], out = new byte[length];
cipher.processBytes(in, 0, length, out, 0); cipher.processBytes(in, 0, length, out, 0);
return out; return out;

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import java.security.Provider; import java.security.Provider;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.SecureRandomSpi; import java.security.SecureRandomSpi;
@@ -19,7 +17,7 @@ class PseudoSecureRandom extends SecureRandom {
private final PseudoRandom pseudoRandom; private final PseudoRandom pseudoRandom;
private PseudoSecureRandomSpi(byte[] seed) { private PseudoSecureRandomSpi(byte[] seed) {
pseudoRandom = new PseudoRandomImpl(seed); pseudoRandom = new PseudoRandom(seed);
} }
@Override @Override

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider; import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test; import org.junit.Test;
import java.util.Arrays; import java.util.Arrays;
@@ -18,7 +19,7 @@ public class SignatureTest extends BrambleTestCase {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final byte[] publicKey, privateKey; private final byte[] publicKey, privateKey;
private final String label = TestUtils.getRandomString(42); private final String label = StringUtils.getRandomString(42);
private final byte[] inputBytes = TestUtils.getRandomBytes(123); private final byte[] inputBytes = TestUtils.getRandomBytes(123);
public SignatureTest() { public SignatureTest() {
@@ -64,7 +65,7 @@ public class SignatureTest extends BrambleTestCase {
public void testDifferentLabelsProduceDifferentSignatures() public void testDifferentLabelsProduceDifferentSignatures()
throws Exception { throws Exception {
// Generate a second label // Generate a second label
String label2 = TestUtils.getRandomString(42); String label2 = StringUtils.getRandomString(42);
// Calculate the signature with different inputs // Calculate the signature with different inputs
// the results should be different // the results should be different
byte[] sig1 = crypto.sign(label, inputBytes, privateKey); byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
@@ -100,7 +101,7 @@ public class SignatureTest extends BrambleTestCase {
@Test @Test
public void testDifferentLabelFailsVerification() throws Exception { public void testDifferentLabelFailsVerification() throws Exception {
// Generate a second label // Generate a second label
String label2 = TestUtils.getRandomString(42); String label2 = StringUtils.getRandomString(42);
// calculate the signature with different label, should fail to verify // calculate the signature with different label, should fail to verify
byte[] sig = crypto.sign(label, inputBytes, privateKey); byte[] sig = crypto.sign(label, inputBytes, privateKey);
assertFalse(crypto.verify(label2, inputBytes, publicKey, sig)); assertFalse(crypto.verify(label2, inputBytes, publicKey, sig));

View File

@@ -14,7 +14,8 @@ import static junit.framework.Assert.assertEquals;
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
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_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_NONCE_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@@ -22,7 +23,8 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher; private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey; private final SecretKey streamHeaderKey, frameKey;
private final byte[] streamHeaderIv, payload; private final byte[] streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final int payloadLength = 123, paddingLength = 234; private final int payloadLength = 123, paddingLength = 234;
private final long streamNumber = 1234; private final long streamNumber = 1234;
@@ -30,7 +32,12 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
cipher = new TestAuthenticatedCipher(); // Null cipher cipher = new TestAuthenticatedCipher(); // Null cipher
streamHeaderKey = TestUtils.getSecretKey(); streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey(); frameKey = TestUtils.getSecretKey();
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH); streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
payload = TestUtils.getRandomBytes(payloadLength); payload = TestUtils.getRandomBytes(payloadLength);
} }
@@ -47,7 +54,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1); byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -76,6 +85,85 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
assertEquals(-1, s.readFrame(buffer)); assertEquals(-1, s.readFrame(buffer));
} }
@Test(expected = IOException.class)
public void testWrongProtocolVersionThrowsException() throws Exception {
byte[] wrongProtocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION + 1, wrongProtocolVersionBytes,
0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(wrongProtocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class)
public void testWrongStreamNumberThrowsException() throws Exception {
byte[] wrongStreamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber + 1, wrongStreamNumberBytes, 0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(wrongStreamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class) @Test(expected = IOException.class)
public void testTruncatedFrameThrowsException() throws Exception { public void testTruncatedFrameThrowsException() throws Exception {
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -83,7 +171,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength); paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -111,7 +201,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload = TestUtils.getRandomBytes(payloadLength); byte[] payload = TestUtils.getRandomBytes(payloadLength);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -138,7 +230,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
padding[paddingLength - 1] = 1; padding[paddingLength - 1] = 1;
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -162,7 +256,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength); paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.crypto;
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.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -11,8 +12,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HE
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.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -21,7 +23,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher; private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey; private final SecretKey streamHeaderKey, frameKey;
private final byte[] tag, streamHeaderIv, payload; private final byte[] tag, streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final long streamNumber = 1234; private final long streamNumber = 1234;
private final int payloadLength = 123, paddingLength = 234; private final int payloadLength = 123, paddingLength = 234;
@@ -30,7 +33,12 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
streamHeaderKey = TestUtils.getSecretKey(); streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey(); frameKey = TestUtils.getSecretKey();
tag = TestUtils.getRandomBytes(TAG_LENGTH); tag = TestUtils.getRandomBytes(TAG_LENGTH);
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH); streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
payload = TestUtils.getRandomBytes(payloadLength); payload = TestUtils.getRandomBytes(payloadLength);
} }
@@ -38,7 +46,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePayloadLength() throws Exception { public void testRejectsNegativePayloadLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, -1, 0, false); s.writeFrame(payload, -1, 0, false);
} }
@@ -47,7 +56,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePaddingLength() throws Exception { public void testRejectsNegativePaddingLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, 0, -1, false); s.writeFrame(payload, 0, -1, false);
} }
@@ -56,7 +66,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsMaxPayloadPlusPadding() throws Exception { public void testRejectsMaxPayloadPlusPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1]; byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false); s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
@@ -66,7 +77,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadIncludingPadding() throws Exception { public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH]; byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false); s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
@@ -78,7 +90,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadWithoutPadding() throws Exception { public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH]; byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false); s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
@@ -90,14 +103,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception { public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, false); s.writeFrame(payload, payloadLength, 0, false);
// Expect the tag, stream header, frame header, payload and MAC // Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -113,14 +129,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithTag() throws Exception { public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, true); s.writeFrame(payload, payloadLength, 0, true);
// Expect the tag, stream header, frame header, payload and MAC // Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -136,13 +155,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception { public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, false); s.writeFrame(payload, payloadLength, 0, false);
// Expect the stream header, frame header, payload and MAC // Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -158,13 +180,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception { public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, true); s.writeFrame(payload, payloadLength, 0, true);
// Expect the stream header, frame header, payload and MAC // Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -180,14 +205,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithTag() throws Exception { public void testWritePaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false); s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the tag, stream header, frame header, payload, padding and MAC // Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -205,14 +233,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithTag() throws Exception { public void testWritePaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true); s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the tag, stream header, frame header, payload, padding and MAC // Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -230,13 +261,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception { public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false); s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the stream header, frame header, payload, padding and MAC // Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -254,13 +288,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithoutTag() throws Exception { public void testWritePaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true); s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the stream header, frame header, payload, padding and MAC // Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -278,7 +315,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteTwoFramesWithTag() throws Exception { public void testWriteTwoFramesWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
int payloadLength1 = 345, paddingLength1 = 456; int payloadLength1 = 345, paddingLength1 = 456;
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1); byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
@@ -289,7 +327,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// MAC, second frame header, payload, padding, MAC // MAC, second frame header, payload, padding, MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -315,7 +355,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception { throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream once // Flush the stream once
s.flush(); s.flush();
@@ -323,7 +364,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header // Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
@@ -335,7 +378,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception { throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream twice // Flush the stream twice
s.flush(); s.flush();
@@ -344,7 +388,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header // Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
@@ -355,14 +401,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testFlushDoesNotWriteTagIfNull() throws Exception { public void testFlushDoesNotWriteTagIfNull() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream once // Flush the stream once
s.flush(); s.flush();
// Expect the stream header // Expect the stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);

View File

@@ -0,0 +1,59 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
public class TagEncodingTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final SecretKey tagKey;
private final long streamNumber = 1234567890;
public TagEncodingTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
tagKey = TestUtils.getSecretKey();
}
@Test
public void testKeyAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
SecretKey tagKey = TestUtils.getSecretKey();
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testProtocolVersionAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testStreamNumberAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
assertTrue(set.add(new Bytes(tag)));
}
}
}

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.junit.Test; import org.junit.Test;
@@ -159,7 +158,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadString8() throws Exception { public void testReadString8() throws Exception {
String longest = TestUtils.getRandomString(Byte.MAX_VALUE); String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// "foo", the empty string, and 127 random letters // "foo", the empty string, and 127 random letters
setContents("41" + "03" + "666F6F" + "41" + "00" + setContents("41" + "03" + "666F6F" + "41" + "00" +
@@ -181,7 +180,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testSkipString8() throws Exception { public void testSkipString8() throws Exception {
String longest = TestUtils.getRandomString(Byte.MAX_VALUE); String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// "foo", the empty string, and 127 random letters // "foo", the empty string, and 127 random letters
setContents("41" + "03" + "666F6F" + "41" + "00" + setContents("41" + "03" + "666F6F" + "41" + "00" +
@@ -194,9 +193,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadString16() throws Exception { public void testReadString16() throws Exception {
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
String longest = TestUtils.getRandomString(Short.MAX_VALUE); String longest = StringUtils.getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// 128 random letters and 2^15 -1 random letters // 128 random letters and 2^15 -1 random letters
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex); setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
@@ -207,7 +206,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadString16ChecksMaxLength() throws Exception { public void testReadString16ChecksMaxLength() throws Exception {
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 128 random letters, twice // 128 random letters, twice
setContents("42" + "0080" + shortHex + "42" + "0080" + shortHex); setContents("42" + "0080" + shortHex + "42" + "0080" + shortHex);
@@ -218,9 +217,9 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testSkipString16() throws Exception { public void testSkipString16() throws Exception {
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
String longest = TestUtils.getRandomString(Short.MAX_VALUE); String longest = StringUtils.getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
// 128 random letters and 2^15 - 1 random letters // 128 random letters and 2^15 - 1 random letters
setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex); setContents("42" + "0080" + shortHex + "42" + "7FFF" + longHex);
@@ -231,7 +230,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testReadString32() throws Exception { public void testReadString32() throws Exception {
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters // 2^15 random letters
setContents("44" + "00008000" + shortHex); setContents("44" + "00008000" + shortHex);
@@ -241,7 +240,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testReadString32ChecksMaxLength() throws Exception { public void testReadString32ChecksMaxLength() throws Exception {
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters, twice // 2^15 random letters, twice
setContents("44" + "00008000" + shortHex + setContents("44" + "00008000" + shortHex +
@@ -253,7 +252,7 @@ public class BdfReaderImplTest extends BrambleTestCase {
@Test @Test
public void testSkipString32() throws Exception { public void testSkipString32() throws Exception {
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
// 2^15 random letters, twice // 2^15 random letters, twice
setContents("44" + "00008000" + shortHex + setContents("44" + "00008000" + shortHex +

View File

@@ -81,7 +81,7 @@ public class BdfWriterImplTest extends BrambleTestCase {
@Test @Test
public void testWriteString8() throws IOException { public void testWriteString8() throws IOException {
String longest = TestUtils.getRandomString(Byte.MAX_VALUE); String longest = StringUtils.getRandomString(Byte.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
w.writeString("foo bar baz bam "); w.writeString("foo bar baz bam ");
w.writeString(longest); w.writeString(longest);
@@ -93,9 +93,9 @@ public class BdfWriterImplTest extends BrambleTestCase {
@Test @Test
public void testWriteString16() throws IOException { public void testWriteString16() throws IOException {
String shortest = TestUtils.getRandomString(Byte.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Byte.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
String longest = TestUtils.getRandomString(Short.MAX_VALUE); String longest = StringUtils.getRandomString(Short.MAX_VALUE);
String longHex = StringUtils.toHexString(longest.getBytes("UTF-8")); String longHex = StringUtils.toHexString(longest.getBytes("UTF-8"));
w.writeString(shortest); w.writeString(shortest);
w.writeString(longest); w.writeString(longest);
@@ -106,7 +106,7 @@ public class BdfWriterImplTest extends BrambleTestCase {
@Test @Test
public void testWriteString32() throws IOException { public void testWriteString32() throws IOException {
String shortest = TestUtils.getRandomString(Short.MAX_VALUE + 1); String shortest = StringUtils.getRandomString(Short.MAX_VALUE + 1);
String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8")); String shortHex = StringUtils.toHexString(shortest.getBytes("UTF-8"));
w.writeString(shortest); w.writeString(shortest);
// STRING_32 tag, length 2^15, UTF-8 bytes // STRING_32 tag, length 2^15, UTF-8 bytes

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -48,8 +49,8 @@ public class BasicH2Test extends BrambleTestCase {
createTable(connection); createTable(connection);
// Generate an ID and two names // Generate an ID and two names
byte[] id = TestUtils.getRandomId(); byte[] id = TestUtils.getRandomId();
String oldName = TestUtils.getRandomString(50); String oldName = StringUtils.getRandomString(50);
String newName = TestUtils.getRandomString(50); String newName = StringUtils.getRandomString(50);
// Insert the ID and old name into the table // Insert the ID and old name into the table
insertRow(id, oldName); insertRow(id, oldName);
// Check that the old name can be retrieved using the ID // Check that the old name can be retrieved using the ID
@@ -78,8 +79,8 @@ public class BasicH2Test extends BrambleTestCase {
String[] newNames = new String[BATCH_SIZE]; String[] newNames = new String[BATCH_SIZE];
for (int i = 0; i < BATCH_SIZE; i++) { for (int i = 0; i < BATCH_SIZE; i++) {
ids[i] = TestUtils.getRandomId(); ids[i] = TestUtils.getRandomId();
oldNames[i] = TestUtils.getRandomString(50); oldNames[i] = StringUtils.getRandomString(50);
newNames[i] = TestUtils.getRandomString(50); newNames[i] = StringUtils.getRandomString(50);
} }
// Insert the IDs and old names into the table as a batch // Insert the IDs and old names into the table as a batch
insertBatch(ids, oldNames); insertBatch(ids, oldNames);

View File

@@ -49,6 +49,7 @@ import org.briarproject.bramble.api.transport.OutgoingKeys;
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.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
@@ -94,7 +95,7 @@ public class DatabaseComponentImplTest extends BrambleTestCase {
private final Contact contact; private final Contact contact;
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
clientId = new ClientId(TestUtils.getRandomString(5)); clientId = new ClientId(StringUtils.getRandomString(5));
groupId = new GroupId(TestUtils.getRandomId()); groupId = new GroupId(TestUtils.getRandomId());
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH]; byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
group = new Group(groupId, clientId, descriptor); group = new Group(groupId, clientId, descriptor);

View File

@@ -24,6 +24,7 @@ import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestDatabaseConfig; import org.briarproject.bramble.test.TestDatabaseConfig;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -84,7 +85,7 @@ public class H2DatabaseTest extends BrambleTestCase {
public H2DatabaseTest() throws Exception { public H2DatabaseTest() throws Exception {
groupId = new GroupId(TestUtils.getRandomId()); groupId = new GroupId(TestUtils.getRandomId());
clientId = new ClientId(TestUtils.getRandomString(5)); clientId = new ClientId(StringUtils.getRandomString(5));
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH]; byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
group = new Group(groupId, clientId, descriptor); group = new Group(groupId, clientId, descriptor);
AuthorId authorId = new AuthorId(TestUtils.getRandomId()); AuthorId authorId = new AuthorId(TestUtils.getRandomId());

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
@@ -31,8 +32,9 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final LocalAuthor localAuthor = private final LocalAuthor localAuthor =
new LocalAuthor(new AuthorId(TestUtils.getRandomId()), new LocalAuthor(new AuthorId(TestUtils.getRandomId()),
TestUtils.getRandomString(8), TestUtils.getRandomBytes(42), StringUtils.getRandomString(8),
TestUtils.getRandomBytes(42), 0); TestUtils.getRandomBytes(42), TestUtils.getRandomBytes(42),
0);
private final Collection<LocalAuthor> localAuthors = private final Collection<LocalAuthor> localAuthors =
Collections.singletonList(localAuthor); Collections.singletonList(localAuthor);
@@ -93,7 +95,7 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
assertEquals(UNKNOWN, identityManager.getAuthorStatus(authorId)); assertEquals(UNKNOWN, identityManager.getAuthorStatus(authorId));
// add one unverified contact // add one unverified contact
Author author = new Author(authorId, TestUtils.getRandomString(8), Author author = new Author(authorId, StringUtils.getRandomString(8),
TestUtils.getRandomBytes(42)); TestUtils.getRandomBytes(42));
Contact contact = Contact contact =
new Contact(new ContactId(1), author, localAuthor.getId(), new Contact(new ContactId(1), author, localAuthor.getId(),

View File

@@ -311,6 +311,11 @@ public class LanTcpPluginTest extends BrambleTestCase {
return remote; return remote;
} }
@Override
public TransportProperties getRemoteProperties(ContactId c) {
return remote.get(c);
}
@Override @Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
} }

View File

@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
@@ -36,7 +37,7 @@ public class TransportPropertyValidatorTest extends BrambleTestCase {
bdfDictionary = new BdfDictionary(); bdfDictionary = new BdfDictionary();
GroupId groupId = new GroupId(TestUtils.getRandomId()); GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomString(5)); ClientId clientId = new ClientId(StringUtils.getRandomString(5));
byte[] descriptor = TestUtils.getRandomBytes(12); byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor); group = new Group(groupId, clientId, descriptor);
@@ -85,7 +86,7 @@ public class TransportPropertyValidatorTest extends BrambleTestCase {
public void testValidateLongTransportId() throws IOException { public void testValidateLongTransportId() throws IOException {
String wrongTransportIdString = String wrongTransportIdString =
TestUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1); StringUtils.getRandomString(MAX_TRANSPORT_ID_LENGTH + 1);
BdfList body = BdfList.of(wrongTransportIdString, 4, bdfDictionary); BdfList body = BdfList.of(wrongTransportIdString, 4, bdfDictionary);
tpv.validateMessage(message, group, body); tpv.validateMessage(message, group, body);
} }

View File

@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.StringUtils;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -34,6 +35,7 @@ import java.util.Collection;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
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.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -77,7 +79,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
headerKey = TestUtils.getSecretKey(); headerKey = TestUtils.getSecretKey();
streamNumber = 123; streamNumber = 123;
// Create a group // Create a group
ClientId clientId = new ClientId(TestUtils.getRandomString(5)); ClientId clientId = new ClientId(StringUtils.getRandomString(5));
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH]; byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
Group group = groupFactory.createGroup(clientId, descriptor); Group group = groupFactory.createGroup(clientId, descriptor);
// Add two messages to the group // Add two messages to the group
@@ -115,7 +117,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
private void read(byte[] connectionData) throws Exception { private void read(byte[] connectionData) throws Exception {
// Calculate the expected tag // Calculate the expected tag
byte[] expectedTag = new byte[TAG_LENGTH]; byte[] expectedTag = new byte[TAG_LENGTH];
crypto.encodeTag(expectedTag, tagKey, streamNumber); crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
// Read the tag // Read the tag
InputStream in = new ByteArrayInputStream(connectionData); InputStream in = new ByteArrayInputStream(connectionData);

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -51,7 +52,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
private final Executor dbExecutor = new ImmediateExecutor(); private final Executor dbExecutor = new ImmediateExecutor();
private final Executor validationExecutor = new ImmediateExecutor(); private final Executor validationExecutor = new ImmediateExecutor();
private final ClientId clientId = private final ClientId clientId =
new ClientId(TestUtils.getRandomString(5)); new ClientId(StringUtils.getRandomString(5));
private final MessageId messageId = new MessageId(TestUtils.getRandomId()); private final MessageId messageId = new MessageId(TestUtils.getRandomId());
private final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); private final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
private final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); private final MessageId messageId2 = new MessageId(TestUtils.getRandomId());

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils;
public abstract class ValidatorTestCase extends BrambleMockTestCase { public abstract class ValidatorTestCase extends BrambleMockTestCase {
@@ -28,7 +29,7 @@ public abstract class ValidatorTestCase extends BrambleMockTestCase {
protected final Message message = protected final Message message =
new Message(messageId, groupId, timestamp, raw); new Message(messageId, groupId, timestamp, raw);
protected final ClientId clientId = protected final ClientId clientId =
new ClientId(TestUtils.getRandomString(123)); new ClientId(StringUtils.getRandomString(123));
protected final byte[] descriptor = TestUtils.getRandomBytes(123); protected final byte[] descriptor = TestUtils.getRandomBytes(123);
protected final Group group = new Group(groupId, clientId, descriptor); protected final Group group = new Group(groupId, clientId, descriptor);

View File

@@ -33,6 +33,7 @@ import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
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.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
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;
@@ -86,7 +87,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets per contact) // Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(crypto).encodeTag(with(any(byte[].class)), exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
@@ -133,7 +134,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys // Save the keys
@@ -199,7 +200,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -247,7 +248,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -306,7 +307,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -355,7 +356,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -365,7 +366,8 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).addTransportKeys(txn, contactId, transportKeys); oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Encode a new tag after sliding the window // Encode a new tag after sliding the window
oneOf(crypto).encodeTag(with(any(byte[].class)), oneOf(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with((long) REORDERING_WINDOW_SIZE)); with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1) // Save the reordering window (previous rotation period, base 1)
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999, oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
@@ -428,7 +430,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
@@ -450,7 +452,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated

View File

@@ -7,10 +7,19 @@ apply plugin: 'witness'
dependencies { dependencies {
compile project(':bramble-core') compile project(':bramble-core')
compile fileTree(dir: 'libs', include: '*.jar') compile fileTree(dir: 'libs', include: '*.jar')
compile 'net.java.dev.jna:jna:4.4.0'
compile 'net.java.dev.jna:jna-platform:4.4.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput') testCompile project(path: ':bramble-core', configuration: 'testOutput')
} }
dependencyVerification {
verify = [
'net.java.dev.jna:jna:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
'net.java.dev.jna:jna-platform:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
]
}
tasks.withType(Test) { tasks.withType(Test) {
systemProperty 'java.library.path', 'libs' systemProperty 'java.library.path', 'libs'
} }

Binary file not shown.

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.bluetooth;
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;
@@ -19,33 +18,23 @@ import org.briarproject.bramble.util.OsUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
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.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.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.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.bluetooth.BluetoothStateException; import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice; import javax.bluetooth.LocalDevice;
import javax.microedition.io.Connector; import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection; import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier; import javax.microedition.io.StreamConnectionNotifier;
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 javax.bluetooth.DiscoveryAgent.GIAC; import static javax.bluetooth.DiscoveryAgent.GIAC;
@@ -67,7 +56,6 @@ class BluetoothPlugin implements DuplexPlugin {
private final Backoff backoff; private final Backoff backoff;
private final DuplexPluginCallback callback; private final DuplexPluginCallback callback;
private final int maxLatency; private final int maxLatency;
private final Semaphore discoverySemaphore = new Semaphore(1);
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false; private volatile boolean running = false;
@@ -261,8 +249,7 @@ class BluetoothPlugin implements DuplexPlugin {
@Override @Override
public DuplexTransportConnection createConnection(ContactId c) { public DuplexTransportConnection createConnection(ContactId c) {
if (!running) return null; if (!running) return null;
TransportProperties p = callback.getRemoteProperties().get(c); TransportProperties p = callback.getRemoteProperties(c);
if (p == null) return null;
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null; if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
@@ -273,95 +260,6 @@ class BluetoothPlugin implements DuplexPlugin {
return new BluetoothTransportConnection(this, s); return new BluetoothTransportConnection(this, s);
} }
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
if (!running) return null;
// Use the invitation codes to generate the UUID
byte[] b = r.nextBytes(UUID_BYTES);
String uuid = UUID.nameUUIDFromBytes(b).toString();
String url = makeUrl("localhost", uuid);
// Make the device discoverable if possible
makeDeviceDiscoverable();
// Bind a server socket for receiving invitation connections
final StreamConnectionNotifier ss;
try {
ss = (StreamConnectionNotifier) Connector.open(url);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
if (!running) {
tryToClose(ss);
return null;
}
// Create the background tasks
CompletionService<StreamConnection> complete =
new ExecutorCompletionService<>(ioExecutor);
List<Future<StreamConnection>> futures = new ArrayList<>();
if (alice) {
// Return the first connected socket
futures.add(complete.submit(new ListeningTask(ss)));
futures.add(complete.submit(new DiscoveryTask(uuid)));
} 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))));
}
StreamConnection chosen = null;
try {
Future<StreamConnection> f = complete.poll(timeout, MILLISECONDS);
if (f == null) return null; // No task completed within the timeout
chosen = f.get();
return new BluetoothTransportConnection(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<StreamConnection>> futures,
@Nullable final StreamConnection chosen) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
for (Future<StreamConnection> f : futures) {
try {
if (f.cancel(true)) {
LOG.info("Cancelled task");
} else {
StreamConnection 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;
@@ -376,7 +274,7 @@ class BluetoothPlugin implements DuplexPlugin {
String url = makeUrl("localhost", uuid); String url = makeUrl("localhost", uuid);
// Make the device discoverable if possible // Make the device discoverable if possible
makeDeviceDiscoverable(); makeDeviceDiscoverable();
// Bind a server socket for receiving invitation connections // Bind a server socket for receiving key agreementconnections
final StreamConnectionNotifier ss; final StreamConnectionNotifier ss;
try { try {
ss = (StreamConnectionNotifier) Connector.open(url); ss = (StreamConnectionNotifier) Connector.open(url);
@@ -431,77 +329,6 @@ class BluetoothPlugin implements DuplexPlugin {
} }
} }
private class DiscoveryTask implements Callable<StreamConnection> {
private final String uuid;
private DiscoveryTask(String uuid) {
this.uuid = uuid;
}
@Override
public StreamConnection call() throws Exception {
// Repeat discovery until we connect or get interrupted
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
while (true) {
if (!discoverySemaphore.tryAcquire())
throw new Exception("Discovery is already in progress");
try {
InvitationListener listener =
new InvitationListener(discoveryAgent, uuid);
discoveryAgent.startInquiry(GIAC, listener);
String url = listener.waitForUrl();
if (url != null) {
StreamConnection s = connect(url);
if (s != null) {
LOG.info("Outgoing connection");
return s;
}
}
} finally {
discoverySemaphore.release();
}
}
}
}
private static class ListeningTask implements Callable<StreamConnection> {
private final StreamConnectionNotifier serverSocket;
private ListeningTask(StreamConnectionNotifier serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public StreamConnection call() throws Exception {
StreamConnection s = serverSocket.acceptAndOpen();
LOG.info("Incoming connection");
return s;
}
}
private static class ReadableTask implements Callable<StreamConnection> {
private final Callable<StreamConnection> connectionTask;
private ReadableTask(Callable<StreamConnection> connectionTask) {
this.connectionTask = connectionTask;
}
@Override
public StreamConnection call() throws Exception {
StreamConnection s = connectionTask.call();
InputStream in = s.openInputStream();
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 StreamConnectionNotifier ss; private final StreamConnectionNotifier ss;

View File

@@ -1,109 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import static java.util.logging.Level.WARNING;
class InvitationListener implements DiscoveryListener {
private static final Logger LOG =
Logger.getLogger(InvitationListener.class.getName());
private final AtomicInteger searches = new AtomicInteger(1);
private final CountDownLatch finished = new CountDownLatch(1);
private final DiscoveryAgent discoveryAgent;
private final String uuid;
private volatile String url = null;
InvitationListener(DiscoveryAgent discoveryAgent, String uuid) {
this.discoveryAgent = discoveryAgent;
this.uuid = uuid;
}
String waitForUrl() throws InterruptedException {
finished.await();
return url;
}
@Override
public void deviceDiscovered(RemoteDevice device, DeviceClass deviceClass) {
UUID[] uuids = new UUID[] {new UUID(uuid, false)};
// Try to discover the services associated with the UUID
try {
discoveryAgent.searchServices(null, uuids, device, this);
searches.incrementAndGet();
} catch (BluetoothStateException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public void servicesDiscovered(int transaction, ServiceRecord[] services) {
for (ServiceRecord record : services) {
// Does this service have a URL?
String serviceUrl = record.getConnectionURL(
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
if (serviceUrl == null) continue;
// Does this service have the UUID we're looking for?
Collection<String> uuids = new TreeSet<>();
findNestedClassIds(record.getAttributeValue(0x1), uuids);
for (String u : uuids) {
if (uuid.equalsIgnoreCase(u)) {
// The UUID matches - store the URL
url = serviceUrl;
finished.countDown();
return;
}
}
}
}
@Override
public void inquiryCompleted(int discoveryType) {
if (searches.decrementAndGet() == 0) finished.countDown();
}
@Override
public void serviceSearchCompleted(int transaction, int response) {
if (searches.decrementAndGet() == 0) finished.countDown();
}
// UUIDs are sometimes buried in nested data elements
private void findNestedClassIds(Object o, Collection<String> ids) {
o = getDataElementValue(o);
if (o instanceof Enumeration<?>) {
for (Object o1 : Collections.list((Enumeration<?>) o))
findNestedClassIds(o1, ids);
} else if (o instanceof UUID) {
ids.add(o.toString());
}
}
private Object getDataElementValue(Object o) {
if (o instanceof DataElement) {
// Bluecove throws an exception if the type is unknown
try {
return ((DataElement) o).getValue();
} catch (ClassCastException e) {
return null;
}
}
return null;
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.modem; package org.briarproject.bramble.plugin.modem;
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;
@@ -146,8 +145,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
String fromIso = callback.getLocalProperties().get("iso3166"); String fromIso = callback.getLocalProperties().get("iso3166");
if (StringUtils.isNullOrEmpty(fromIso)) return null; if (StringUtils.isNullOrEmpty(fromIso)) return null;
// Get the ISO 3166 code for the callee's country // Get the ISO 3166 code for the callee's country
TransportProperties properties = callback.getRemoteProperties().get(c); TransportProperties properties = callback.getRemoteProperties(c);
if (properties == null) return null;
String toIso = properties.get("iso3166"); String toIso = properties.get("iso3166");
if (StringUtils.isNullOrEmpty(toIso)) return null; if (StringUtils.isNullOrEmpty(toIso)) return null;
// Get the callee's phone number // Get the callee's phone number
@@ -167,17 +165,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
return new ModemTransportConnection(); return new ModemTransportConnection();
} }
@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;

View File

@@ -9,8 +9,6 @@ import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@@ -65,12 +63,10 @@ public class ModemPluginTest extends BrambleTestCase {
final Modem modem = context.mock(Modem.class); final Modem modem = context.mock(Modem.class);
final TransportProperties local = new TransportProperties(); final TransportProperties local = new TransportProperties();
local.put("iso3166", ISO_1336); local.put("iso3166", ISO_1336);
TransportProperties p = new TransportProperties(); final TransportProperties remote = new TransportProperties();
p.put("iso3166", ISO_1336); remote.put("iso3166", ISO_1336);
p.put("number", NUMBER); remote.put("number", NUMBER);
ContactId contactId = new ContactId(234); final ContactId contactId = new ContactId(234);
final Map<ContactId, TransportProperties> remote =
Collections.singletonMap(contactId, p);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
oneOf(serialPortList).getPortNames(); oneOf(serialPortList).getPortNames();
@@ -82,7 +78,7 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection() // createConnection()
oneOf(callback).getLocalProperties(); oneOf(callback).getLocalProperties();
will(returnValue(local)); will(returnValue(local));
oneOf(callback).getRemoteProperties(); oneOf(callback).getRemoteProperties(contactId);
will(returnValue(remote)); will(returnValue(remote));
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(returnValue(true)); will(returnValue(true));
@@ -106,12 +102,10 @@ public class ModemPluginTest extends BrambleTestCase {
final Modem modem = context.mock(Modem.class); final Modem modem = context.mock(Modem.class);
final TransportProperties local = new TransportProperties(); final TransportProperties local = new TransportProperties();
local.put("iso3166", ISO_1336); local.put("iso3166", ISO_1336);
TransportProperties p = new TransportProperties(); final TransportProperties remote = new TransportProperties();
p.put("iso3166", ISO_1336); remote.put("iso3166", ISO_1336);
p.put("number", NUMBER); remote.put("number", NUMBER);
ContactId contactId = new ContactId(234); final ContactId contactId = new ContactId(234);
final Map<ContactId, TransportProperties> remote =
Collections.singletonMap(contactId, p);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
oneOf(serialPortList).getPortNames(); oneOf(serialPortList).getPortNames();
@@ -123,7 +117,7 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection() // createConnection()
oneOf(callback).getLocalProperties(); oneOf(callback).getLocalProperties();
will(returnValue(local)); will(returnValue(local));
oneOf(callback).getRemoteProperties(); oneOf(callback).getRemoteProperties(contactId);
will(returnValue(remote)); will(returnValue(remote));
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(returnValue(false)); will(returnValue(false));
@@ -147,12 +141,10 @@ public class ModemPluginTest extends BrambleTestCase {
final Modem modem = context.mock(Modem.class); final Modem modem = context.mock(Modem.class);
final TransportProperties local = new TransportProperties(); final TransportProperties local = new TransportProperties();
local.put("iso3166", ISO_1336); local.put("iso3166", ISO_1336);
TransportProperties p = new TransportProperties(); final TransportProperties remote = new TransportProperties();
p.put("iso3166", ISO_1336); remote.put("iso3166", ISO_1336);
p.put("number", NUMBER); remote.put("number", NUMBER);
ContactId contactId = new ContactId(234); final ContactId contactId = new ContactId(234);
final Map<ContactId, TransportProperties> remote =
Collections.singletonMap(contactId, p);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// start() // start()
oneOf(serialPortList).getPortNames(); oneOf(serialPortList).getPortNames();
@@ -164,7 +156,7 @@ public class ModemPluginTest extends BrambleTestCase {
// createConnection() // createConnection()
oneOf(callback).getLocalProperties(); oneOf(callback).getLocalProperties();
will(returnValue(local)); will(returnValue(local));
oneOf(callback).getRemoteProperties(); oneOf(callback).getRemoteProperties(contactId);
will(returnValue(remote)); will(returnValue(remote));
oneOf(modem).dial(NUMBER); oneOf(modem).dial(NUMBER);
will(throwException(new IOException())); will(throwException(new IOException()));

View File

@@ -1,6 +1,6 @@
[main] [main]
host = https://www.transifex.com host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, fr_FR: fr lang_map = pt_BR: pt-rBR, fr_FR: fr, nb_NO: nb, zh-Hans: zh-rCN
[briar.stringsxml-5] [briar.stringsxml-5]
file_filter = src/main/res/values-<lang>/strings.xml file_filter = src/main/res/values-<lang>/strings.xml

View File

@@ -5,13 +5,10 @@ dependencies {
def supportVersion = '23.2.1' def supportVersion = '23.2.1'
compile project(':briar-core') compile project(':briar-core')
compile project(':bramble-android') compile project(':bramble-android')
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:support-v4:$supportVersion" compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") { compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4' exclude module: 'support-v4'
} }
compile("com.android.support:preference-v14:$supportVersion") { compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4' exclude module: 'support-v4'
} }
@@ -20,7 +17,7 @@ dependencies {
exclude module: 'recyclerview-v7' exclude module: 'recyclerview-v7'
} }
compile "com.android.support:cardview-v7:$supportVersion" compile "com.android.support:cardview-v7:$supportVersion"
compile 'com.android.support:support-annotations:23.4.0' compile "com.android.support:support-annotations:$supportVersion"
compile('ch.acra:acra:4.8.5') { compile('ch.acra:acra:4.8.5') {
exclude module: 'support-v4' exclude module: 'support-v4'
exclude module: 'support-annotations' exclude module: 'support-annotations'
@@ -28,15 +25,16 @@ dependencies {
compile 'info.guardianproject.panic:panic:0.5' compile 'info.guardianproject.panic:panic:0.5'
compile 'info.guardianproject.trustedintents:trustedintents:0.2' compile 'info.guardianproject.trustedintents:trustedintents:0.2'
compile 'de.hdodenhof:circleimageview:2.1.0' compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.google.zxing:core:3.2.1' compile 'com.google.zxing:core:3.3.0'
provided 'javax.annotation:jsr250-api:1.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0' compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.github.bumptech.glide:glide:3.8.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0' compile 'uk.co.samuelwall:material-tap-target-prompt:1.9.2'
provided 'javax.annotation:jsr250-api:1.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput') testCompile project(path: ':bramble-core', configuration: 'testOutput')
testCompile 'org.robolectric:robolectric:3.0' testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:2.8.9'
} }
dependencyVerification { dependencyVerification {
@@ -45,20 +43,18 @@ dependencyVerification {
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2', 'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e', 'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503', 'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259', 'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448', 'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
'uk.co.samuelwall:material-tap-target-prompt:5d4951124366bc5c52e57beaa294db7611f0aa2a8d80e0163e1383e1966ba5b2',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d', 'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7', 'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686', 'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01', 'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1', 'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1', 'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe', 'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025', 'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
] ]
} }
@@ -82,12 +78,19 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 22 targetSdkVersion 22
resValue "string", "app_package", "org.briarproject.briar" versionCode 1611
versionName "0.16.11"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
resValue "string", "app_name", "Briar Beta"
buildConfigField "String", "GitHash", "\"${getGitHash()}\"" buildConfigField "String", "GitHash", "\"${getGitHash()}\""
} }
buildTypes { buildTypes {
debug { debug {
applicationIdSuffix ".debug"
resValue "string", "app_package", "org.briarproject.briar.beta.debug"
resValue "string", "app_name", "Briar Debug"
shrinkResources false shrinkResources false
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
@@ -104,8 +107,13 @@ android {
targetCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7
} }
aaptOptions {
cruncherEnabled = false
}
lintOptions { lintOptions {
warning 'MissingTranslation' warning 'MissingTranslation'
warning 'ImpliedQuantity' warning 'ImpliedQuantity'
warning 'ExtraTranslation'
} }
} }

View File

@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest
package="org.briarproject.briar" package="org.briarproject.briar"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android">
android:versionCode="13"
android:versionName="0.13">
<uses-feature android:name="android.hardware.bluetooth"/> <uses-feature android:name="android.hardware.bluetooth"/>
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
@@ -17,11 +15,9 @@
<uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Since API 23, this is needed to add contacts via Bluetooth -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application <application
android:name=".android.BriarApplicationImpl" android:name="org.briarproject.briar.android.BriarApplicationImpl"
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher_round" android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
@@ -29,15 +25,20 @@
android:theme="@style/BriarTheme"> android:theme="@style/BriarTheme">
<service <service
android:name=".android.BriarService" android:name="org.briarproject.briar.android.BriarService"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="org.briarproject.briar.android.BriarService"/> <action android:name="org.briarproject.briar.android.BriarService"/>
</intent-filter> </intent-filter>
</service> </service>
<service
android:name="org.briarproject.briar.android.NotificationCleanupService"
android:exported="false">
</service>
<activity <activity
android:name=".android.reporting.DevReportActivity" android:name="org.briarproject.briar.android.reporting.DevReportActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="false" android:exported="false"
android:finishOnTaskLaunch="true" android:finishOnTaskLaunch="true"
@@ -49,24 +50,24 @@
</activity> </activity>
<activity <activity
android:name=".android.splash.ExpiredActivity" android:name="org.briarproject.briar.android.splash.ExpiredActivity"
android:label="@string/app_name"> android:label="@string/app_name">
</activity> </activity>
<activity <activity
android:name=".android.login.PasswordActivity" android:name="org.briarproject.briar.android.login.PasswordActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:windowSoftInputMode="stateVisible"> android:windowSoftInputMode="stateVisible">
</activity> </activity>
<activity <activity
android:name=".android.login.SetupActivity" android:name="org.briarproject.briar.android.login.SetupActivity"
android:label="@string/setup_title" android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
</activity> </activity>
<activity <activity
android:name=".android.splash.SplashScreenActivity" android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>
@@ -76,268 +77,258 @@
</activity> </activity>
<activity <activity
android:name=".android.navdrawer.NavDrawerActivity" android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:launchMode="singleTop"> android:launchMode="singleTop">
</activity> </activity>
<activity <activity
android:name=".android.contact.ConversationActivity" android:name="org.briarproject.briar.android.contact.ConversationActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateHidden|adjustResize"> android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.creation.CreateGroupActivity" android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title" android:label="@string/groups_create_group_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.conversation.GroupActivity" android:name="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.invitation.GroupInvitationActivity" android:name="org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity"
android:label="@string/groups_invitations_title" android:label="@string/groups_invitations_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.privategroup.memberlist.GroupMemberListActivity" android:name="org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity"
android:label="@string/groups_member_list" android:label="@string/groups_member_list"
android:parentActivityName=".android.privategroup.conversation.GroupActivity" android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity" android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.reveal.RevealContactsActivity" android:name="org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity"
android:label="@string/groups_reveal_contacts" android:label="@string/groups_reveal_contacts"
android:parentActivityName=".android.privategroup.conversation.GroupActivity" android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"> android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity" android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.creation.GroupInviteActivity" android:name="org.briarproject.briar.android.privategroup.creation.GroupInviteActivity"
android:label="@string/groups_invite_members" android:label="@string/groups_invite_members"
android:parentActivityName=".android.privategroup.conversation.GroupActivity" android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"/> android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.sharing.ForumInvitationActivity" android:name="org.briarproject.briar.android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title" android:label="@string/forum_invitations_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.BlogInvitationActivity" android:name="org.briarproject.briar.android.sharing.BlogInvitationActivity"
android:label="@string/blogs_sharing_invitations_title" android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName=".android.contact.ConversationActivity"> android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity" android:value="org.briarproject.briar.android.contact.ConversationActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.forum.CreateForumActivity" android:name="org.briarproject.briar.android.forum.CreateForumActivity"
android:label="@string/create_forum_title" android:label="@string/create_forum_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.forum.ForumActivity" android:name="org.briarproject.briar.android.forum.ForumActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.ShareForumActivity" android:name="org.briarproject.briar.android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header" android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.forum.ForumActivity" android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.forum.ForumActivity" android:value="org.briarproject.briar.android.forum.ForumActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.ShareBlogActivity" android:name="org.briarproject.briar.android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header" android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.blog.BlogActivity" android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.ForumSharingStatusActivity" android:name="org.briarproject.briar.android.sharing.ForumSharingStatusActivity"
android:label="@string/sharing_status" android:label="@string/sharing_status"
android:parentActivityName=".android.forum.ForumActivity"> android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.forum.ForumActivity" android:value="org.briarproject.briar.android.forum.ForumActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.BlogSharingStatusActivity" android:name="org.briarproject.briar.android.sharing.BlogSharingStatusActivity"
android:label="@string/sharing_status" android:label="@string/sharing_status"
android:parentActivityName=".android.blog.BlogActivity"> android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.BlogActivity" android:name="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.blog.WriteBlogPostActivity" android:name="org.briarproject.briar.android.blog.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post" android:label="@string/blogs_write_blog_post"
android:parentActivityName=".android.blog.BlogActivity" android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize"> android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.ReblogActivity" android:name="org.briarproject.briar.android.blog.ReblogActivity"
android:label="@string/blogs_reblog_button" android:label="@string/blogs_reblog_button"
android:parentActivityName=".android.blog.BlogActivity" android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="stateHidden"> android:windowSoftInputMode="stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.RssFeedImportActivity" android:name="org.briarproject.briar.android.blog.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import" android:label="@string/blogs_rss_feeds_import"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize"> android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.RssFeedManageActivity" android:name="org.briarproject.briar.android.blog.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage" android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.invitation.AddContactActivity" android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title" android:label="@string/add_contact_title"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.introduction.IntroductionActivity" android:name="org.briarproject.briar.android.introduction.IntroductionActivity"
android:label="@string/introduction_activity_title" android:label="@string/introduction_activity_title"
android:parentActivityName=".android.contact.ConversationActivity" android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity"
android:windowSoftInputMode="stateHidden|adjustResize"> android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity" android:value="org.briarproject.briar.android.contact.ConversationActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.StartupFailureActivity" android:name="org.briarproject.briar.android.StartupFailureActivity"
android:label="@string/startup_failed_activity_title"> android:label="@string/startup_failed_activity_title">
</activity> </activity>
<activity <activity
android:name=".android.settings.SettingsActivity" android:name="org.briarproject.briar.android.settings.SettingsActivity"
android:label="@string/settings_button" android:label="@string/settings_button"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY"> android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/> <action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
@@ -346,27 +337,27 @@
</activity> </activity>
<activity <activity
android:name=".android.login.ChangePasswordActivity" android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
android:label="@string/change_password" android:label="@string/change_password"
android:parentActivityName=".android.settings.SettingsActivity"> android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.settings.SettingsActivity" android:value="org.briarproject.briar.android.settings.SettingsActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.panic.PanicPreferencesActivity" android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting" android:label="@string/panic_setting"
android:parentActivityName=".android.settings.SettingsActivity"> android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.settings.SettingsActivity" android:value="org.briarproject.briar.android.settings.SettingsActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.panic.PanicResponderActivity" android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
android:noHistory="true" android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay">
<!-- this can never have launchMode singleTask or singleInstance! --> <!-- this can never have launchMode singleTask or singleInstance! -->
@@ -377,7 +368,7 @@
</activity> </activity>
<activity <activity
android:name=".android.panic.ExitActivity" android:name="org.briarproject.briar.android.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay">
</activity> </activity>

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
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;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory; import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.keyagreement.PayloadParser;
@@ -27,11 +26,11 @@ import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule; import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.reporting.BriarReportSender; import org.briarproject.briar.android.reporting.BriarReportSender;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.ReferenceManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory; import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.feed.FeedManager; import org.briarproject.briar.api.feed.FeedManager;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
@@ -44,6 +43,7 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.api.test.TestDataCreator;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider; import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel; import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
@@ -73,11 +73,11 @@ public interface AndroidComponent
DatabaseConfig databaseConfig(); DatabaseConfig databaseConfig();
ReferenceManager referenceMangager();
@DatabaseExecutor @DatabaseExecutor
Executor databaseExecutor(); Executor databaseExecutor();
MessageTracker messageTracker();
LifecycleManager lifecycleManager(); LifecycleManager lifecycleManager();
IdentityManager identityManager(); IdentityManager identityManager();
@@ -86,8 +86,6 @@ public interface AndroidComponent
EventBus eventBus(); EventBus eventBus();
InvitationTaskFactory invitationTaskFactory();
AndroidNotificationManager androidNotificationManager(); AndroidNotificationManager androidNotificationManager();
ScreenFilterMonitor screenFilterMonitor(); ScreenFilterMonitor screenFilterMonitor();
@@ -140,6 +138,8 @@ public interface AndroidComponent
Clock clock(); Clock clock();
TestDataCreator testDataCreator();
@IoExecutor @IoExecutor
Executor ioExecutor(); Executor ioExecutor();
@@ -151,6 +151,8 @@ public interface AndroidComponent
void inject(RecentEmojiPageModel recentEmojiPageModel); void inject(RecentEmojiPageModel recentEmojiPageModel);
void inject(NotificationCleanupService notificationCleanupService);
// Eager singleton load // Eager singleton load
void inject(AppModule.EagerSingletons init); void inject(AppModule.EagerSingletons init);
} }

View File

@@ -2,14 +2,12 @@ package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder; import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -25,12 +23,14 @@ import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.ConversationActivity; import org.briarproject.briar.android.contact.ConversationActivity;
import org.briarproject.briar.android.forum.ForumActivity; import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity; import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.util.BriarNotificationBuilder;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent; import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent; import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
@@ -48,6 +48,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -61,7 +62,6 @@ import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE; import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL; import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID; import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
@@ -84,15 +84,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private static final int BLOG_POST_NOTIFICATION_ID = 6; private static final int BLOG_POST_NOTIFICATION_ID = 6;
private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 7; private static final int INTRODUCTION_SUCCESS_NOTIFICATION_ID = 7;
// Content URIs to differentiate between pending intents private static final long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
private static final String CONTACT_URI =
"content://org.briarproject.briar/contact";
private static final String GROUP_URI =
"content://org.briarproject.briar/group";
private static final String FORUM_URI =
"content://org.briarproject.briar/forum";
private static final String BLOG_URI =
"content://org.briarproject.briar/blog";
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(AndroidNotificationManagerImpl.class.getName()); Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
@@ -101,6 +93,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Context appContext;
private final Clock clock;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the main UI thread // The following must only be accessed on the main UI thread
@@ -116,16 +109,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private boolean blockContacts = false, blockGroups = false; private boolean blockContacts = false, blockGroups = false;
private boolean blockForums = false, blockBlogs = false; private boolean blockForums = false, blockBlogs = false;
private boolean blockIntroductions = false; private boolean blockIntroductions = false;
private long lastSound = 0;
private volatile Settings settings = new Settings(); private volatile Settings settings = new Settings();
@Inject @Inject
AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor, AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
SettingsManager settingsManager, AndroidExecutor androidExecutor, SettingsManager settingsManager, AndroidExecutor androidExecutor,
Application app) { Application app, Clock clock) {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.clock = clock;
appContext = app.getApplicationContext(); appContext = app.getApplicationContext();
} }
@@ -264,7 +259,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) contactCounts.put(c, 1); if (count == null) contactCounts.put(c, 1);
else contactCounts.put(c, count + 1); else contactCounts.put(c, count + 1);
contactTotal++; contactTotal++;
updateContactNotification(); updateContactNotification(true);
} }
}); });
} }
@@ -277,32 +272,30 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = contactCounts.remove(c); Integer count = contactCounts.remove(c);
if (count == null) return; // Already cleared if (count == null) return; // Already cleared
contactTotal -= count; contactTotal -= count;
updateContactNotification(); updateContactNotification(false);
} }
}); });
} }
@UiThread @UiThread
private void updateContactNotification() { private void updateContactNotification(boolean mayAlertAgain) {
if (contactTotal == 0) { if (contactTotal == 0) {
clearContactNotification(); clearContactNotification();
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) { } else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
NotificationCompat.Builder b = BriarNotificationBuilder b =
new NotificationCompat.Builder(appContext); new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_private_message); b.setSmallIcon(R.drawable.notification_private_message);
b.setColor(ContextCompat.getColor(appContext, b.setColorRes(R.color.briar_primary);
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString( b.setContentText(appContext.getResources().getQuantityString(
R.plurals.private_message_notification_text, contactTotal, R.plurals.private_message_notification_text, contactTotal,
contactTotal)); contactTotal));
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true); b.setNumber(contactTotal);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI); boolean showOnLockScreen =
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri)) settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setSound(Uri.parse(ringtoneUri)); b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
b.setDefaults(getDefaults()); if (mayAlertAgain) setAlertProperties(b);
b.setOnlyAlertOnce(true); setDeleteIntent(b, CONTACT_URI);
b.setAutoCancel(true);
if (contactCounts.size() == 1) { if (contactCounts.size() == 1) {
// Touching the notification shows the relevant conversation // Touching the notification shows the relevant conversation
Intent i = new Intent(appContext, ConversationActivity.class); Intent i = new Intent(appContext, ConversationActivity.class);
@@ -325,16 +318,25 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i); t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
} }
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build()); nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
} }
} }
@UiThread
private void setAlertProperties(BriarNotificationBuilder b) {
long currentTime = clock.currentTimeMillis();
if (currentTime - lastSound > SOUND_DELAY) {
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
lastSound = currentTime;
}
}
@UiThread @UiThread
private int getDefaults() { private int getDefaults() {
int defaults = DEFAULT_LIGHTS; int defaults = DEFAULT_LIGHTS;
@@ -347,13 +349,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
return defaults; return defaults;
} }
private void setDeleteIntent(BriarNotificationBuilder b, String uri) {
Intent i = new Intent(appContext, NotificationCleanupService.class);
i.setData(Uri.parse(uri));
b.setDeleteIntent(PendingIntent.getService(appContext, nextRequestId++,
i, 0));
}
@Override @Override
public void clearAllContactNotifications() { public void clearAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
clearContactNotification(); clearContactNotification();
clearIntroductionSuccessNotification();
} }
}); });
} }
@@ -369,7 +377,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) groupCounts.put(g, 1); if (count == null) groupCounts.put(g, 1);
else groupCounts.put(g, count + 1); else groupCounts.put(g, count + 1);
groupTotal++; groupTotal++;
updateGroupMessageNotification(); updateGroupMessageNotification(true);
} }
}); });
} }
@@ -382,31 +390,30 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = groupCounts.remove(g); Integer count = groupCounts.remove(g);
if (count == null) return; // Already cleared if (count == null) return; // Already cleared
groupTotal -= count; groupTotal -= count;
updateGroupMessageNotification(); updateGroupMessageNotification(false);
} }
}); });
} }
@UiThread @UiThread
private void updateGroupMessageNotification() { private void updateGroupMessageNotification(boolean mayAlertAgain) {
if (groupTotal == 0) { if (groupTotal == 0) {
clearGroupMessageNotification(); clearGroupMessageNotification();
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) { } else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
NotificationCompat.Builder b = BriarNotificationBuilder b =
new NotificationCompat.Builder(appContext); new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_private_group); b.setSmallIcon(R.drawable.notification_private_group);
b.setColor(ContextCompat.getColor(appContext, b.setColorRes(R.color.briar_primary);
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString( b.setContentText(appContext.getResources().getQuantityString(
R.plurals.group_message_notification_text, groupTotal, R.plurals.group_message_notification_text, groupTotal,
groupTotal)); groupTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI); b.setNumber(groupTotal);
if (!StringUtils.isNullOrEmpty(ringtoneUri)) boolean showOnLockScreen =
b.setSound(Uri.parse(ringtoneUri)); settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setDefaults(getDefaults()); b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
b.setOnlyAlertOnce(true); if (mayAlertAgain) setAlertProperties(b);
b.setAutoCancel(true); setDeleteIntent(b, GROUP_URI);
if (groupCounts.size() == 1) { if (groupCounts.size() == 1) {
// Touching the notification shows the relevant group // Touching the notification shows the relevant group
Intent i = new Intent(appContext, GroupActivity.class); Intent i = new Intent(appContext, GroupActivity.class);
@@ -430,10 +437,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i); t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
} }
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build()); nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
@@ -461,7 +464,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) forumCounts.put(g, 1); if (count == null) forumCounts.put(g, 1);
else forumCounts.put(g, count + 1); else forumCounts.put(g, count + 1);
forumTotal++; forumTotal++;
updateForumPostNotification(); updateForumPostNotification(true);
} }
}); });
} }
@@ -474,31 +477,30 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = forumCounts.remove(g); Integer count = forumCounts.remove(g);
if (count == null) return; // Already cleared if (count == null) return; // Already cleared
forumTotal -= count; forumTotal -= count;
updateForumPostNotification(); updateForumPostNotification(false);
} }
}); });
} }
@UiThread @UiThread
private void updateForumPostNotification() { private void updateForumPostNotification(boolean mayAlertAgain) {
if (forumTotal == 0) { if (forumTotal == 0) {
clearForumPostNotification(); clearForumPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) { } else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
NotificationCompat.Builder b = BriarNotificationBuilder b =
new NotificationCompat.Builder(appContext); new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_forum); b.setSmallIcon(R.drawable.notification_forum);
b.setColor(ContextCompat.getColor(appContext, b.setColorRes(R.color.briar_primary);
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString( b.setContentText(appContext.getResources().getQuantityString(
R.plurals.forum_post_notification_text, forumTotal, R.plurals.forum_post_notification_text, forumTotal,
forumTotal)); forumTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI); b.setNumber(forumTotal);
if (!StringUtils.isNullOrEmpty(ringtoneUri)) boolean showOnLockScreen =
b.setSound(Uri.parse(ringtoneUri)); settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setDefaults(getDefaults()); b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
b.setOnlyAlertOnce(true); if (mayAlertAgain) setAlertProperties(b);
b.setAutoCancel(true); setDeleteIntent(b, FORUM_URI);
if (forumCounts.size() == 1) { if (forumCounts.size() == 1) {
// Touching the notification shows the relevant forum // Touching the notification shows the relevant forum
Intent i = new Intent(appContext, ForumActivity.class); Intent i = new Intent(appContext, ForumActivity.class);
@@ -522,10 +524,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i); t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
} }
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(FORUM_POST_NOTIFICATION_ID, b.build()); nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
@@ -553,7 +551,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (count == null) blogCounts.put(g, 1); if (count == null) blogCounts.put(g, 1);
else blogCounts.put(g, count + 1); else blogCounts.put(g, count + 1);
blogTotal++; blogTotal++;
updateBlogPostNotification(); updateBlogPostNotification(true);
} }
}); });
} }
@@ -566,31 +564,30 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
Integer count = blogCounts.remove(g); Integer count = blogCounts.remove(g);
if (count == null) return; // Already cleared if (count == null) return; // Already cleared
blogTotal -= count; blogTotal -= count;
updateBlogPostNotification(); updateBlogPostNotification(false);
} }
}); });
} }
@UiThread @UiThread
private void updateBlogPostNotification() { private void updateBlogPostNotification(boolean mayAlertAgain) {
if (blogTotal == 0) { if (blogTotal == 0) {
clearBlogPostNotification(); clearBlogPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) { } else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
NotificationCompat.Builder b = BriarNotificationBuilder b =
new NotificationCompat.Builder(appContext); new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_blog); b.setSmallIcon(R.drawable.notification_blog);
b.setColor(ContextCompat.getColor(appContext, b.setColorRes(R.color.briar_primary);
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString( b.setContentText(appContext.getResources().getQuantityString(
R.plurals.blog_post_notification_text, blogTotal, R.plurals.blog_post_notification_text, blogTotal,
blogTotal)); blogTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI); b.setNumber(blogTotal);
if (!StringUtils.isNullOrEmpty(ringtoneUri)) boolean showOnLockScreen =
b.setSound(Uri.parse(ringtoneUri)); settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setDefaults(getDefaults()); b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
b.setOnlyAlertOnce(true); if (mayAlertAgain) setAlertProperties(b);
b.setAutoCancel(true); setDeleteIntent(b, BLOG_URI);
// Touching the notification shows the combined blog feed // Touching the notification shows the combined blog feed
Intent i = new Intent(appContext, NavDrawerActivity.class); Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_BLOGS, true); i.putExtra(INTENT_BLOGS, true);
@@ -600,10 +597,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class); t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i); t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(BLOG_POST_NOTIFICATION_ID, b.build()); nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
@@ -633,20 +627,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
@UiThread @UiThread
private void updateIntroductionNotification() { private void updateIntroductionNotification() {
NotificationCompat.Builder b = BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_introduction); b.setSmallIcon(R.drawable.notification_introduction);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary)); b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString( b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal, R.plurals.introduction_notification_text, introductionTotal,
introductionTotal)); introductionTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI); boolean showOnLockScreen =
if (!StringUtils.isNullOrEmpty(ringtoneUri)) settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setSound(Uri.parse(ringtoneUri)); b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
b.setDefaults(getDefaults()); setAlertProperties(b);
b.setOnlyAlertOnce(true); setDeleteIntent(b, INTRODUCTION_URI);
b.setAutoCancel(true);
// Touching the notification shows the contact list // Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class); Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true); i.putExtra(INTENT_CONTACTS, true);
@@ -656,15 +648,22 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class); t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i); t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build()); nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());
} }
@Override
public void clearAllIntroductionNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearIntroductionSuccessNotification();
}
});
}
@Override @Override
public void blockNotification(final GroupId g) { public void blockNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@@ -705,68 +704,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}); });
} }
@Override
public void blockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = true;
blockIntroductions = true;
}
});
}
@Override
public void unblockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = false;
blockIntroductions = false;
}
});
}
@Override
public void blockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = true;
}
});
}
@Override
public void unblockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = false;
}
});
}
@Override
public void blockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = true;
}
});
}
@Override
public void unblockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = false;
}
});
}
@Override @Override
public void blockAllBlogPostNotifications() { public void blockAllBlogPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@@ -786,5 +723,4 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
}); });
} }
} }

View File

@@ -38,8 +38,6 @@ public class AppModule {
static class EagerSingletons { static class EagerSingletons {
@Inject @Inject
AndroidNotificationManager androidNotificationManager; AndroidNotificationManager androidNotificationManager;
@Inject
ScreenFilterMonitor screenFilterMonitor;
} }
private final Application application; private final Application application;
@@ -171,10 +169,8 @@ public class AppModule {
} }
@Provides @Provides
@Singleton
ScreenFilterMonitor provideScreenFilterMonitor( ScreenFilterMonitor provideScreenFilterMonitor(
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) { ScreenFilterMonitorImpl screenFilterMonitor) {
lifecycleManager.registerService(sfm); return screenFilterMonitor;
return sfm;
} }
} }

View File

@@ -6,5 +6,9 @@ package org.briarproject.briar.android;
*/ */
public interface BriarApplication { public interface BriarApplication {
// This build expires on 31 December 2017
long EXPIRY_DATE = 1514761200 * 1000L;
AndroidComponent getApplicationComponent(); AndroidComponent getApplicationComponent();
} }

View File

@@ -2,6 +2,9 @@ package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.os.StrictMode.VmPolicy;
import org.acra.ACRA; import org.acra.ACRA;
import org.acra.ReportingInteractionMode; import org.acra.ReportingInteractionMode;
@@ -33,6 +36,8 @@ import static org.acra.ReportField.REPORT_ID;
import static org.acra.ReportField.STACK_TRACE; import static org.acra.ReportField.STACK_TRACE;
import static org.acra.ReportField.USER_APP_START_DATE; import static org.acra.ReportField.USER_APP_START_DATE;
import static org.acra.ReportField.USER_CRASH_DATE; import static org.acra.ReportField.USER_CRASH_DATE;
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@ReportsCrashes( @ReportsCrashes(
reportPrimerClass = BriarReportPrimer.class, reportPrimerClass = BriarReportPrimer.class,
@@ -72,6 +77,9 @@ public class BriarApplicationImpl extends Application
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
if (IS_DEBUG_BUILD) enableStrictMode();
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
LOG.info("Created"); LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder() applicationComponent = DaggerAndroidComponent.builder()
@@ -85,6 +93,17 @@ public class BriarApplicationImpl extends Application
AndroidEagerSingletons.initEagerSingletons(applicationComponent); AndroidEagerSingletons.initEagerSingletons(applicationComponent);
} }
private void enableStrictMode() {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
threadPolicy.detectAll();
threadPolicy.penaltyLog();
StrictMode.setThreadPolicy(threadPolicy.build());
VmPolicy.Builder vmPolicy = new VmPolicy.Builder();
vmPolicy.detectAll();
vmPolicy.penaltyLog();
StrictMode.setVmPolicy(vmPolicy.build());
}
@Override @Override
public AndroidComponent getApplicationComponent() { public AndroidComponent getApplicationComponent() {
return applicationComponent; return applicationComponent;

View File

@@ -0,0 +1,53 @@
package org.briarproject.briar.android;
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import javax.inject.Inject;
import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.GROUP_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.INTRODUCTION_URI;
public class NotificationCleanupService extends IntentService {
private static final String TAG =
NotificationCleanupService.class.getName();
@Inject
AndroidNotificationManager notificationManager;
public NotificationCleanupService() {
super(TAG);
}
@Override
public void onCreate() {
super.onCreate();
AndroidComponent applicationComponent =
((BriarApplication) getApplication()).getApplicationComponent();
applicationComponent.inject(this);
}
@Override
protected void onHandleIntent(@Nullable Intent i) {
if (i == null || i.getData() == null) return;
String uri = i.getData().toString();
if (uri.equals(CONTACT_URI)) {
notificationManager.clearAllContactNotifications();
} else if (uri.equals(GROUP_URI)) {
notificationManager.clearAllGroupMessageNotifications();
} else if (uri.equals(FORUM_URI)) {
notificationManager.clearAllForumPostNotifications();
} else if (uri.equals(BLOG_URI)) {
notificationManager.clearAllBlogPostNotifications();
} else if (uri.equals(INTRODUCTION_URI)) {
notificationManager.clearAllIntroductionNotifications();
}
}
}

View File

@@ -1,24 +1,13 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature; import android.content.pm.Signature;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
@@ -27,37 +16,26 @@ import java.io.InputStream;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.content.pm.PackageManager.GET_SIGNATURES;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault @NotNullByDefault
@ParametersNotNullByDefault class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
public class ScreenFilterMonitorImpl extends BroadcastReceiver
implements Service, ScreenFilterMonitor {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName()); Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
private static final String PREF_SCREEN_FILTER_APPS =
"shownScreenFilterApps";
/* /*
* Ignore Play Services if it uses this package name and public key - it's * Ignore Play Services if it uses this package name and public key - it's
@@ -78,124 +56,17 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" + "82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103"; "0B145B6AA192858E79020103";
private final Context appContext;
private final AndroidExecutor androidExecutor;
private final PackageManager pm; private final PackageManager pm;
private final SharedPreferences prefs;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the UI thread
private final Set<String> apps = new HashSet<>();
private final Set<String> shownApps;
private boolean serviceStarted = false;
@Inject @Inject
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) { ScreenFilterMonitorImpl(Application app) {
this.androidExecutor = executor; pm = app.getPackageManager();
this.appContext = app;
pm = appContext.getPackageManager();
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
shownApps = getShownScreenFilterApps();
}
@Override
public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException();
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package");
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
intentFilter);
apps.addAll(getInstalledScreenFilterApps());
serviceStarted = true;
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
serviceStarted = false;
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
private Set<String> getShownScreenFilterApps() {
// Result must not be modified
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
HashSet<String> result = new HashSet<>();
if (s != null) {
result.addAll(s);
}
return result;
}
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
final String packageName =
intent.getData().getEncodedSchemeSpecificPart();
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
String pkg = isOverlayApp(packageName);
if (pkg == null) {
return;
}
apps.add(pkg);
}
});
}
} }
@Override @Override
@UiThread @UiThread
public Set<String> getApps() { public Set<String> getApps() {
if (!serviceStarted) { Set<String> screenFilterApps = new TreeSet<>();
apps.addAll(getInstalledScreenFilterApps());
}
TreeSet<String> buf = new TreeSet<>();
if (apps.isEmpty()) {
return buf;
}
buf.addAll(apps);
buf.removeAll(shownApps);
return buf;
}
@Override
@UiThread
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
HashSet<String> buf = new HashSet<>(s);
shownApps.addAll(buf);
if (persistent && !s.isEmpty()) {
buf.addAll(getShownScreenFilterApps());
prefs.edit()
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
.apply();
}
}
private Set<String> getInstalledScreenFilterApps() {
HashSet<String> screenFilterApps = new HashSet<>();
List<PackageInfo> packageInfos = List<PackageInfo> packageInfos =
pm.getInstalledPackages(GET_PERMISSIONS); pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) { for (PackageInfo packageInfo : packageInfos) {
@@ -209,21 +80,6 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
return screenFilterApps; return screenFilterApps;
} }
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
// returns the app name.
@Nullable
private String isOverlayApp(String pkg) {
try {
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
if (isOverlayApp(pkgInfo)) {
return pkgToString(pkgInfo);
}
} catch (NameNotFoundException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
return null;
}
// Fetches the application name for a given package. // Fetches the application name for a given package.
@Nullable @Nullable
private String pkgToString(PackageInfo pkgInfo) { private String pkgToString(PackageInfo pkgInfo) {

View File

@@ -10,13 +10,21 @@ import static java.util.logging.Level.OFF;
public interface TestingConstants { public interface TestingConstants {
/** /**
* Whether this is an alpha or beta build. This should be set to false for * Whether this is a debug build.
*/
boolean IS_DEBUG_BUILD = BuildConfig.DEBUG;
/**
* Whether this is a beta build. This should be set to false for final
* release builds. * release builds.
*/ */
boolean TESTING = BuildConfig.DEBUG; boolean IS_BETA_BUILD = true;
/** Default log level. */ /**
Level DEFAULT_LOG_LEVEL = TESTING ? INFO : OFF; * Default log level. Disable logging for final release builds.
*/
@SuppressWarnings("ConstantConditions")
Level DEFAULT_LOG_LEVEL = IS_DEBUG_BUILD || IS_BETA_BUILD ? INFO : OFF;
/** /**
* Whether to prevent screenshots from being taken. Setting this to true * Whether to prevent screenshots from being taken. Setting this to true
@@ -24,5 +32,5 @@ public interface TestingConstants {
* Unfortunately this also prevents the user from taking screenshots * Unfortunately this also prevents the user from taking screenshots
* intentionally. * intentionally.
*/ */
boolean PREVENT_SCREENSHOTS = !TESTING; boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
} }

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