Compare commits

...

198 Commits

Author SHA1 Message Date
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
Ernir Erlingsson
6de539a62d Merge branch '791-permanent-input' into 'master'
Show text input permanently in threaded conversations

Closes #791

See merge request !526
2017-04-27 10:38:58 +00:00
Ernir Erlingsson
34704ec04d Merge branch '874-tree-indicator' into 'master'
Darken thread indicator

Closes #874

See merge request !525
2017-04-26 08:38:52 +00:00
akwizgran
9fd6d46583 Merge branch '871-increase-socket-timeout' into 'master'
Increase socket timeout for Tor sockets

See merge request !519
2017-04-19 16:53:59 +00: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
Torsten Grote
920f3581fa Show text input permanently in threaded conversations 2017-04-17 16:22:24 -03:00
Torsten Grote
45e7af31fe Darken thread indicator 2017-04-17 16:14:26 -03:00
Torsten Grote
67d5d8cdf1 Merge branch '941-reblogged-rss-post-has-wrong-icon' into 'master'
Store RSS flag for wrapped blog posts

Closes #941

See merge request !524
2017-04-17 18:23:41 +00:00
Torsten Grote
9d8cadb7a9 Merge branch 'use-original-timestamp-for-rss-posts' into 'master'
Use original timestamp for RSS posts, if available

See merge request !523
2017-04-17 18:22:10 +00:00
Torsten Grote
6425c49d04 Merge branch 'remove-single-top-flag' into 'master'
Don't use single top and clear top flags together

See merge request !522
2017-04-17 18:20:46 +00:00
Torsten Grote
68d98b50f2 Merge branch '938-ignore-play-services-overlay-permission' into 'master'
When checking for overlay apps, ignore Play Services

Closes #938

See merge request !521
2017-04-17 18:19:46 +00:00
akwizgran
84986d393f Added a test for #941, fixed some broken tests. 2017-04-13 17:28:45 +01:00
akwizgran
115d488bc3 Clamp the imported timestamp within reasonable limits. 2017-04-13 16:21:00 +01:00
akwizgran
2eeb2213e3 Store RSS flag for wrapped blog posts. 2017-04-13 15:23:08 +01:00
akwizgran
1b48d661e8 Use original timestamp for RSS posts, if available. 2017-04-13 14:43:43 +01:00
akwizgran
49ba66dee9 Don't use single top and clear top flags together. 2017-04-13 13:56:20 +01:00
akwizgran
46920f3bce Merge branch '892-separate-rss-blog' into 'master'
Separate RSS posts from personal blog posts

Closes #892

See merge request !520
2017-04-13 10:15:00 +00:00
Torsten Grote
4b955809f7 Address review comments 2017-04-12 15:18:27 -03:00
akwizgran
57d4d6546a When checking for overlay apps, ignore Play Services. 2017-04-12 14:24:37 +01:00
Torsten Grote
9bfb58a764 Show blog posts from RSS feeds with a dedicated icon
This adds a field to the post headers and some more tests.
2017-04-12 08:43:24 -03:00
Torsten Grote
0256ec0b8c Show reblog icon only for reblogged posts 2017-04-12 08:43:23 -03:00
Torsten Grote
b0b4a85d15 Add integration test for FeedManager
Attention: This factors out a DnsModule to be able to make actual
non-Tor DNS lookups for testing.
2017-04-12 08:43:23 -03:00
Torsten Grote
d40a058ef5 Change blog descriptor format to include RSS feed flag
This now also handles the case where an RSS blog is deleted via the blog
deletion option and not the feed management.
2017-04-12 08:43:22 -03:00
Torsten Grote
58b9efb24c Open feed's blog when clicking it in 'manage activity' 2017-04-12 08:43:22 -03:00
Torsten Grote
17de785c12 Remove blog as well when removing RSS feed
This also adds a confirmation dialog to the removal process.
2017-04-12 08:43:21 -03:00
Torsten Grote
c7ff1ba974 Store RSS feeds in a separate dedicated blog
A fake LocalAuthor is created for this new blog and stored in the feed's metadata.
2017-04-12 08:43:21 -03:00
akwizgran
d17669f131 Increase socket timeout for Tor sockets. 2017-04-11 14:53:03 +01:00
akwizgran
9755cd9ab4 Merge branch '891-messages-not-acked' into 'master'
Fix MessageId calculation for deprecated MessageQueue

Closes #891

See merge request !514
2017-04-11 12:49:44 +00:00
akwizgran
6d2b18facc Merge branch '799-explain-content-visibility' into 'master'
Show explanation about visibility in member lists

Closes #799

See merge request !516
2017-04-07 14:54:41 +00:00
Torsten Grote
f8cf7034db Show explanation about visibility in member lists 2017-04-07 11:38:33 -03:00
akwizgran
a1e65c9fa7 Merge branch '893-double-introduction-accept' into 'master'
Prevent conversation actions from being executed twice

Closes #893

See merge request !512
2017-04-07 14:03:40 +00:00
Torsten Grote
499d2fe677 Prevent conversation actions from being executed twice 2017-04-07 10:00:55 -03:00
Torsten Grote
fe963edd9d Merge branch '829-new-launcher-icon' into 'master'
Use the new launcher icon

Closes #829

See merge request !515
2017-04-07 12:59:35 +00:00
akwizgran
96f006068f Use the new launcher icon. 2017-04-07 13:57:13 +01:00
akwizgran
74f1fa5690 Merge branch '932-panic-button-terminate-process' into 'master'
Terminate the process after handling a panic trigger

Closes #932

See merge request !513
2017-04-07 12:49:52 +00:00
Torsten Grote
85c17b4cb0 Fix MessageId calculation for deprecated MessageQueue
This was preventing introduction messages from getting ACKed.
The introduction tests were modified to check for this.
2017-04-07 09:45:35 -03:00
akwizgran
6b3a1fd6d4 Merge branch 'fix-test-configuration' into 'master'
Fix "all tests" configuration

See merge request !509
2017-04-07 10:19:01 +00:00
akwizgran
bcabcfce8c Merge branch '925_panic_app_market' into 'master'
Refine the panic app list tap behavior

Closes #925

See merge request !511
2017-04-07 10:17:48 +00:00
goapunk
db0a3bf380 Refine the panic app list behavior
* Only open if a market is installed

Signed-off-by: goapunk <noobie@goapunks.net>
2017-04-07 12:07:10 +02:00
akwizgran
d5d9436e28 Terminate the process after handling a panic trigger. 2017-04-07 10:54:51 +01:00
Torsten Grote
0827b067ec Harmonize position of boolean message variables 2017-04-06 15:42:12 -03:00
akwizgran
9d0dbe9210 Merge branch '885_store_only_four_lan_ip' into 'master'
Store only 4 ip addresses because 5 exceed the maximum length.

Closes #885

See merge request !510
2017-04-06 14:38:49 +00:00
akwizgran
1f7d1bf515 Merge branch '675-polite-executor' into 'master'
Use a polite executor for validation tasks

Closes #675

See merge request !507
2017-04-06 14:37:59 +00:00
akwizgran
fb85ecf07b Added note about number of available processors changing. 2017-04-06 15:34:39 +01:00
akwizgran
a931e6b316 Merge branch '906_tapjacking' into 'master'
Add tapjacking protection

Closes #906

See merge request !502
2017-04-06 14:31:10 +00:00
akwizgran
3aa4644339 If we have multiple cores, leave one free from crypto tasks. 2017-04-06 11:36:02 +01:00
goapunk
9a638c804a Store only 4 ip addresses because 5 exceed the maximum length.
Signed-off-by: goapunk <noobie@goapunks.net>
2017-04-06 12:35:44 +02:00
akwizgran
df3254c634 Fix "all tests" configuration.
The last (empty) stage of this configuration used to
complain about not finding any tests. I replace the last
stage with a copy of the briar-android configuration
and removed briar-android from the list of prerequisites,
so all stages now contain tests.
2017-04-06 10:18:42 +01:00
akwizgran
ba353b9f2b List of wifi configs can be null. 2017-04-06 10:11:59 +01:00
goapunk
04c4e70dd1 Add tapjacking protection
* Set filterTouchesWhenObscured for all views
* Warn the user if Apps using the SYSTEM_ALERT_WINDOW permission are installed
* Warn the user if an App using the permission is installed while Briar is running

Signed-off-by: goapunk <noobie@goapunks.net>
2017-04-05 23:25:57 +02:00
akwizgran
d381e25e86 Limit the number of validation tasks on the crypto executor. 2017-04-05 17:34:21 +01:00
akwizgran
0c085f139a Added "polite" delegating executor. 2017-04-05 17:34:20 +01:00
akwizgran
4123f4a5ce Log time spent queueing and executing crypto and DB tasks. 2017-04-05 17:34:15 +01:00
akwizgran
7bc269fda4 Merge branch '914-simpler-secure-random' into 'master'
Remove Fortuna generator, fix Android SecureRandom bug

Closes #914

See merge request !500
2017-04-05 10:55:25 +00:00
akwizgran
a22931bae6 Merge branch '928-move-html-sanitation-to-dbthread' into 'master'
Move HTML Sanitation to DbThread

Closes #928

See merge request !506
2017-04-04 16:54:21 +00:00
akwizgran
403f886110 Merge branch '910-fix-intent-hijacking' into 'master'
Fix possible intent hijacking for implicit pending intents

Closes #910

See merge request !499
2017-04-04 16:34:38 +00:00
Torsten Grote
b7866be38d Move HTML Sanitation to DbThread 2017-04-04 13:27:06 -03:00
akwizgran
a1b415330e Merge branch '926-class-cast-exception' into 'master'
Don't cast Context to BaseActivity

Closes #926

See merge request !505
2017-04-04 12:44:38 +00:00
Torsten Grote
58318bb79f Remove pending intents for clearning notification counters
These counters are already reset when the user clicks the notification
or vists the area of the app the notifications are for.

This also removes a potential intent hijacking vulnerability.
2017-04-04 09:02:38 -03:00
akwizgran
10bb30e190 Don't assume Context is a BaseActivity. 2017-04-03 12:22:52 +01:00
akwizgran
199a2ffc46 Merge branch '909-prevent-multiple-password-screens' into 'master'
Prevent multiple instances of PasswordActivity

Closes #909

See merge request !504
2017-03-31 13:42:04 +00:00
akwizgran
f6ad2992f2 Prevent multiple instances of PasswordActivity. 2017-03-31 12:55:38 +01:00
akwizgran
f039bd1239 Merge branch '909-restrict-access-to-panic-prefs' into 'master'
Don't allow other apps to open the panic prefs activity

See merge request !503
2017-03-31 08:25:21 +00:00
Torsten Grote
da22d91ef3 Update expiry date and translations 2017-03-30 15:35:02 -03:00
Torsten Grote
cd360ec877 Merge branch '909-restrict-access-to-settings-activity' into 'master'
Require a system permission to open the settings activity

See merge request !501
2017-03-30 12:58:07 +00:00
akwizgran
8e1ada4cdc Don't allow other apps to open the panic prefs activity. 2017-03-30 12:26:33 +01:00
akwizgran
ac063b4c79 Require a system permission to open the settings activity. 2017-03-30 12:00:35 +01:00
akwizgran
10e6163e94 Merge branch '915-fix-forumactivitytest' into 'master'
Fix ForumActivityTest

Closes #915

See merge request !496
2017-03-30 08:19:30 +00:00
Torsten Grote
ebc3402307 Merge branch '912-validate-stream-encrypter-args' into 'master'
Validate arguments to StreamEncrypter#writeFrame()

Closes #912

See merge request !497
2017-03-29 16:52:54 +00:00
akwizgran
d9c63bbcfe Remove Fortuna generator, fix Android SecureRandom bug. 2017-03-29 16:31:59 +01:00
akwizgran
9c89e83c20 Merge branch '913-simpler-password-strength-estimation' into 'master'
Simpler password strength estimation

Closes #913

See merge request !495
2017-03-29 15:01:44 +00:00
akwizgran
adc9bdeb68 Merge branch 'run-configurations' into 'master'
Run configurations for tests

See merge request !498
2017-03-29 08:18:05 +00:00
akwizgran
ff7f0bdc63 Added run configurations for tests to git. 2017-03-28 15:36:18 +01:00
Torsten Grote
c5f6980c69 Fix ForumActivityTest 2017-03-28 09:34:42 -03:00
akwizgran
2574354997 Merge branch '905_set_testing_constant_on_debug' into 'master'
Set TESTING from BuildConfig

Closes #905

See merge request !494
2017-03-28 12:30:00 +00:00
akwizgran
c4e42949cf Simpler password strength estimation. 2017-03-28 13:27:04 +01:00
goapunk
1c5897f1cc Set TESTING from BuildConfig
Signed-off-by: goapunk <noobie@goapunks.net>
2017-03-27 23:39:39 +02:00
akwizgran
510f99c7da Validate arguments to StreamEncrypter#writeFrame(). 2017-03-27 16:26:49 +01:00
akwizgran
1918346ae8 Merge branch '911-link-sanitation' into 'master'
Sanitize all HTML before displaying it

Closes #911

See merge request !493
2017-03-27 09:45:17 +00:00
akwizgran
2a59515c72 Merge branch '907-panic-signout' into 'master'
Require a panic app to be set before executing any panic actions

Closes #907

See merge request !492
2017-03-27 09:43:30 +00:00
akwizgran
7161152b41 Merge branch '903_replace_Runtime_with_ProcessBuilder' into 'master'
Use ProcessBuilder instead of Runtime to start tor

See merge request !487
2017-03-27 09:37:00 +00:00
akwizgran
b42660edab Merge branch 'emoji-soft-reference' into 'master'
Fix potential NPE when getting soft reference

See merge request !490
2017-03-27 09:28:30 +00:00
akwizgran
b405bbf98e Merge branch 'setting-to-disable-tor' into 'master'
Add a setting to disable Tor

See merge request !489
2017-03-27 09:27:56 +00:00
akwizgran
c167938b61 Use constants for Tor network setting values. 2017-03-27 10:26:10 +01:00
Torsten Grote
24b531e6b2 Sanitize all HTML before displaying it 2017-03-24 16:45:36 -03:00
Torsten Grote
9cffff715a Require a panic app to be set before executing any panic actions 2017-03-24 16:19:09 -03:00
Torsten Grote
804e912e19 Merge branch 'remove-placeholder-tests' into 'master'
Remove placeholder tests

See merge request !491
2017-03-24 18:12:13 +00:00
akwizgran
d67e3900e3 Removed placeholder tests.
Evidently this way of nagging myself to write tests doesn't work.
2017-03-24 15:18:37 +00:00
akwizgran
e682f31898 Added a setting to disable Tor.
Also fixed a bug with settings namespaces.
2017-03-24 14:56:30 +00:00
akwizgran
a9053808b4 Merge branch '908-rss-import-dns-leak' into 'master'
Don't make DNS lookups during RSS import

Closes #908

See merge request !488
2017-03-24 10:02:19 +00:00
akwizgran
d9a62a0431 Merge branch 'print_tor_errors' into 'master'
Make Tor boot more verbose

See merge request !486
2017-03-24 10:01:29 +00:00
akwizgran
15ba73276d Merge branch '900-remove-error-state' into 'master'
Remove error state and reset session on error instead

Closes #900

See merge request !484
2017-03-24 09:57:03 +00:00
Torsten Grote
720dda784e Remove error state and reset session on error instead 2017-03-23 14:14:23 -03:00
akwizgran
0ae55404f5 Merge branch '900-simplify-sharing-client-state-machine' into 'master'
Remove REMOTE_LEFT state from sharing client state machine

See merge request !483
2017-03-23 16:13:44 +00:00
akwizgran
9c41437870 Prevent OkHttp from making local DNS lookups. 2017-03-23 15:13:15 +00:00
akwizgran
da9cde083f Include description of SOCKS error in exception. 2017-03-23 15:13:15 +00:00
goapunk
ce3156c9fe Use ProcessBuilder instead of Runtime to start tor
* ProcessBuilder copies the ENV from the current proc
  and preserves ANDROID_ROOT and ANDROID_DATA

Signed-off-by: goapunk <noobie@goapunks.net>
2017-03-18 09:46:24 +01:00
goapunk
be3752bf2f Set Android env vars
Signed-off-by: goapunk <noobie@goapunks.net>

(cherry picked from commit e26f663)
2017-03-17 16:00:34 +00:00
noobie
ef74db65aa Make Tor boot more verbose
Signed-off-by: noobie <noobie@goapunks.net>
2017-03-13 12:19:14 +01:00
Torsten Grote
867a233b6f Update expiry 2017-03-06 18:46:15 +01:00
Torsten Grote
59af25b2cd Remove REMOTE_LEFT state from sharing client state machine 2017-01-12 15:19:28 -02:00
akwizgran
79c78518fb Fix potential NPE when getting soft reference. 2016-12-28 13:47:33 +00:00
277 changed files with 6021 additions and 3486 deletions

9
.gitignore vendored
View File

@@ -9,17 +9,18 @@ Thumbs.db
.DS_Store
# Eclipse project files
#.classpath
#.project
.classpath
.project
.settings
# Local configuration file (sdk path, etc)
local.properties
# Android Studio
.idea/*
!.idea/runConfigurations/
!.idea/codeStyleSettings.xml
.gradle
build/
*.iml
.gitignore
src/test/
projectFilesBackup/

20
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,20 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- export GRADLE_USER_HOME=$PWD/.gradle
# - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
# - echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK}
test:
script:
- ./gradlew test
after_script:
# this file changes every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/

28
.idea/runConfigurations/All_tests.xml generated Normal file
View File

@@ -0,0 +1,28 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="briar-android" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method>
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-j2se" run_configuration_type="AndroidJUnit" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-api" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-core" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in bramble-j2se" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="bramble-j2se" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-j2se" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="briar-android" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="briar-core" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method />
</configuration>
</component>

View File

@@ -12,8 +12,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
versionCode 14
versionName "0.14"
consumerProguardFiles 'proguard-rules.txt'
}
@@ -25,38 +25,38 @@ android {
dependencies {
compile project(':bramble-core')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile fileTree(dir: 'libs', include: '*.jar')
provided 'javax.annotation:jsr250-api:1.0'
}
def torBinaryDir = 'src/main/res/raw'
task downloadTorGeoIp(type: Download) {
src 'https://briarproject.org/build/geoip-2015-12-01.zip'
src 'https://briarproject.org/build/geoip-2017-05-02.zip'
dest "$torBinaryDir/geoip.zip"
onlyIfNewer true
}
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.11-arm.zip'
dest "$torBinaryDir/tor_arm.zip"
onlyIfNewer true
}
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.11-arm-pie.zip'
dest "$torBinaryDir/tor_arm_pie.zip"
onlyIfNewer true
}
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.11-x86.zip'
dest "$torBinaryDir/tor_x86.zip"
onlyIfNewer true
}
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.11-x86-pie.zip'
dest "$torBinaryDir/tor_x86_pie.zip"
onlyIfNewer true
}
@@ -64,31 +64,31 @@ task downloadTorBinaryX86Pie(type: Download) {
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
src "$torBinaryDir/geoip.zip"
algorithm 'SHA-256'
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
checksum '51f4d1272fb867e1f3b36b67a584e2a33c40b40f62305457d799fd399cd77c9b'
}
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256'
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
checksum '1da6008663a8ad98b349e62acbbf42c379f65ec504fa467cb119c187cd5a4c6b'
}
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256'
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
checksum 'eb061f880829e05f104690ac744848133f2dacef04759d425a2cff0df32c271e'
}
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256'
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
checksum 'f5308aff8303daca082f82227d02b51ddedba4ab1d1420739ada0427ae5dbb41'
}
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256'
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
checksum '889a6c81ac73d05d35ed610ca5a913cee44d333e4ae1749c2a107f2f7dd8197b'
}
project.afterEvaluate {

View File

@@ -67,6 +67,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
@@ -164,7 +165,7 @@ class DroidtoothPlugin implements DuplexPlugin {
bind();
} else {
// Enable Bluetooth if settings allow
if (callback.getSettings().getBoolean("enable", false)) {
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
wasEnabledByUs = true;
if (adapter.enable()) LOG.info("Enabling Bluetooth");
else LOG.info("Could not enable Bluetooth");

View File

@@ -79,6 +79,12 @@ import static java.util.logging.Level.WARNING;
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_ALWAYS;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
@MethodsNotNullByDefault
@@ -182,19 +188,31 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
String torPath = torFile.getAbsolutePath();
String configPath = configFile.getAbsolutePath();
String pid = String.valueOf(android.os.Process.myPid());
String[] cmd = {torPath, "-f", configPath, OWNER, pid};
String[] env = {"HOME=" + torDirectory.getAbsolutePath()};
Process torProcess;
ProcessBuilder pb =
new ProcessBuilder(torPath, "-f", configPath, OWNER, pid);
Map<String, String> env = pb.environment();
env.put("HOME", torDirectory.getAbsolutePath());
pb.directory(torDirectory);
try {
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
torProcess = pb.start();
} catch (SecurityException | IOException e) {
throw new PluginException(e);
}
// Log the process's standard output until it detaches
if (LOG.isLoggable(INFO)) {
Scanner stdout = new Scanner(torProcess.getInputStream());
while (stdout.hasNextLine()) LOG.info(stdout.nextLine());
Scanner stderr = new Scanner(torProcess.getErrorStream());
while (stdout.hasNextLine() || stderr.hasNextLine()){
if(stdout.hasNextLine()) {
LOG.info(stdout.nextLine());
}
if(stderr.hasNextLine()){
LOG.info(stderr.nextLine());
}
}
stdout.close();
stderr.close();
}
try {
// Wait for the process to detach or exit
@@ -366,7 +384,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public void run() {
// If there's already a port number stored in config, reuse it
String portString = callback.getSettings().get("port");
String portString = callback.getSettings().get(PREF_TOR_PORT);
int port;
if (StringUtils.isNullOrEmpty(portString)) port = 0;
else port = Integer.parseInt(portString);
@@ -389,7 +407,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// Store the port number
final String localPort = String.valueOf(ss.getLocalPort());
Settings s = new Settings();
s.put("port", localPort);
s.put(PREF_TOR_PORT, localPort);
callback.mergeSettings(s);
// Create a hidden service if necessary
ioExecutor.execute(new Runnable() {
@@ -666,7 +684,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public void eventOccurred(Event e) {
if (e instanceof SettingsUpdatedEvent) {
if (((SettingsUpdatedEvent) e).getNamespace().equals("tor")) {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(ID.getString())) {
LOG.info("Tor settings updated");
updateConnectionStatus();
}
@@ -688,7 +707,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country);
Settings s = callback.getSettings();
boolean useMobileData = s.getBoolean("torOverMobile", true);
int network = s.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_ALWAYS);
if (LOG.isLoggable(INFO)) {
LOG.info("Online: " + online + ", wifi: " + wifi);
@@ -703,7 +723,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} else if (blocked) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
} else if (!wifi && !useMobileData) {
} else if (network == PREF_TOR_NETWORK_NEVER
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
LOG.info("Disabling network due to data setting");
enableNetwork(false);
} else {

View File

@@ -0,0 +1,93 @@
package org.briarproject.bramble.system;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Parcel;
import android.provider.Settings;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static android.content.Context.WIFI_SERVICE;
import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable
@NotNullByDefault
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
private static final int SEED_LENGTH = 32;
private final Context appContext;
@Inject
AndroidSecureRandomProvider(Application app) {
appContext = app.getApplicationContext();
}
@Override
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
super.writeToEntropyPool(out);
out.writeInt(android.os.Process.myPid());
out.writeInt(android.os.Process.myTid());
out.writeInt(android.os.Process.myUid());
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
ContentResolver contentResolver = appContext.getContentResolver();
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (id != null) out.writeUTF(id);
Parcel parcel = Parcel.obtain();
WifiManager wm =
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs)
parcel.writeParcelable(config, 0);
}
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt != null) {
for (BluetoothDevice device : bt.getBondedDevices())
parcel.writeParcelable(device, 0);
}
out.write(parcel.marshall());
parcel.recycle();
}
@Override
protected void writeSeed() {
super.writeSeed();
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix();
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void applyOpenSslFix() {
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
SEED_LENGTH);
try {
// Seed the OpenSSL PRNG
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, seed);
// Mix the output of the Linux PRNG into the OpenSSL PRNG
int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_load_file", String.class, long.class)
.invoke(null, "/dev/urandom", 1024);
if (bytesRead != 1024) throw new IOException();
} catch (Exception e) {
throw new SecurityException(e);
}
}
}

View File

@@ -1,42 +0,0 @@
package org.briarproject.bramble.system;
import android.app.Application;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable
@NotNullByDefault
class AndroidSeedProvider extends LinuxSeedProvider {
private final Context appContext;
@Inject
AndroidSeedProvider(Application app) {
appContext = app.getApplicationContext();
}
@Override
void writeToEntropyPool(DataOutputStream out) throws IOException {
out.writeInt(android.os.Process.myPid());
out.writeInt(android.os.Process.myTid());
out.writeInt(android.os.Process.myUid());
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
ContentResolver contentResolver = appContext.getContentResolver();
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (id != null) out.writeUTF(id);
super.writeToEntropyPool(out);
}
}

View File

@@ -4,7 +4,7 @@ import android.app.Application;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import javax.inject.Singleton;
@@ -16,8 +16,8 @@ public class AndroidSystemModule {
@Provides
@Singleton
SeedProvider provideSeedProvider(Application app) {
return new AndroidSeedProvider(app);
SecureRandomProvider provideSecureRandomProvider(Application app) {
return new AndroidSecureRandomProvider(app);
}
@Provides

View File

@@ -7,12 +7,12 @@ apply plugin: 'witness'
dependencies {
compile "com.google.dagger:dagger: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 "org.jmock:jmock:2.8.1"
testCompile "org.jmock:jmock-junit4:2.8.1"
testCompile "org.jmock:jmock-legacy:2.8.1"
testCompile "org.jmock:jmock:2.8.2"
testCompile "org.jmock:jmock-junit4:2.8.2"
testCompile "org.jmock:jmock-legacy:2.8.2"
testCompile "org.hamcrest:hamcrest-library:1.3"
testCompile "org.hamcrest:hamcrest-core:1.3"
}
@@ -21,7 +21,7 @@ dependencyVerification {
verify = [
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import java.util.Arrays;
import java.util.Comparator;
@@ -53,6 +54,12 @@ public class Bytes implements Comparable<Bytes> {
return aBytes.length - bBytes.length;
}
@Override
public String toString() {
return getClass().getSimpleName() +
"(" + StringUtils.toHexString(getBytes()) + ")";
}
public static class BytesComparator implements Comparator<Bytes> {
@Override

View File

@@ -137,7 +137,8 @@ public interface CryptoComponent {
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** 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.

View File

@@ -6,9 +6,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface PasswordStrengthEstimator {
float NONE = 0;
float WEAK = 0.4f;
float QUITE_WEAK = 0.6f;
float QUITE_STRONG = 0.8f;
float WEAK = 0.25f;
float QUITE_WEAK = 0.5f;
float QUITE_STRONG = 0.75f;
float STRONG = 1;
/**

View File

@@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault
public class Author {
public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES}
public enum Status {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
}
private final AuthorId id;
private final String name;

View File

@@ -9,4 +9,5 @@ public interface BluetoothConstants {
String PROP_ADDRESS = "address";
String PROP_UUID = "uuid";
String PREF_BT_ENABLE = "enable";
}

View File

@@ -4,4 +4,5 @@ public interface LanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.lan");
String PREF_LAN_IP_PORTS = "ipPorts";
}

View File

@@ -8,4 +8,12 @@ public interface TorConstants {
int CONTROL_PORT = 59051;
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
String PREF_TOR_NETWORK = "network";
String PREF_TOR_PORT = "port";
int PREF_TOR_NETWORK_NEVER = 0;
int PREF_TOR_NETWORK_WIFI = 1;
int PREF_TOR_NETWORK_ALWAYS = 2;
}

View File

@@ -0,0 +1,23 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.Provider;
import java.security.SecureRandom;
import javax.annotation.Nullable;
/**
* Wrapper for a platform-specific secure random number generator.
*/
@NotNullByDefault
public interface SecureRandomProvider {
/**
* Returns a {@link Provider} that provides a strong {@link SecureRandom}
* implementation, or null if the platform's default implementation should
* be used.
*/
@Nullable
Provider getProvider();
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Uses a platform-specific source to provide a seed for a pseudo-random
* number generator.
*/
@NotNullByDefault
public interface SeedProvider {
/**
* The length of the seed in bytes.
*/
int SEED_BYTES = 32;
byte[] getSeed();
}

View File

@@ -4,6 +4,11 @@ import org.briarproject.bramble.api.crypto.SecretKey;
public interface TransportConstants {
/**
* The current version of the transport protocol.
*/
int PROTOCOL_VERSION = 3;
/**
* The length of the pseudo-random tag in bytes.
*/
@@ -14,21 +19,22 @@ public interface TransportConstants {
*/
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.
*/
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.
*/
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
+ MAC_LENGTH;
int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
+ STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
/**
* The length of the frame nonce in bytes.

View File

@@ -19,7 +19,7 @@ public class PrivacyUtils {
@Nullable
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
if (address.equals("02:00:00:00:00:00")) return address;
// keep first and last octet of MAC address

View File

@@ -1,8 +1,9 @@
plugins {
id "java"
id "net.ltgt.apt" version "0.9"
id "idea"
id 'java'
id 'net.ltgt.apt' version '0.9'
id 'idea'
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
@@ -10,17 +11,18 @@ apply plugin: 'witness'
dependencies {
compile project(':bramble-api')
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.h2database:h2:1.4.190'
compile 'com.madgag.spongycastle:core:1.56.0.0'
compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
compile 'org.bitlet:weupnp:0.1.4'
testCompile project(path: ':bramble-api', configuration: 'testOutput')
}
dependencyVerification {
verify = [
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c'
'com.madgag.spongycastle:core:5e791b0eaa9e0c4594231b44f616a52adddb7dccedeb0ad9ad74887e19499a23',
'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
]
}

View File

@@ -0,0 +1,84 @@
package org.briarproject.bramble;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import static java.util.logging.Level.FINE;
/**
* An {@link Executor} that delegates its tasks to another {@link Executor}
* while limiting the number of tasks that are delegated concurrently. Tasks
* are delegated in the order they are submitted to this executor.
*/
@NotNullByDefault
public class PoliteExecutor implements Executor {
private static final Level LOG_LEVEL = FINE;
private final Object lock = new Object();
@GuardedBy("lock")
private final Queue<Runnable> queue = new LinkedList<Runnable>();
private final Executor delegate;
private final int maxConcurrentTasks;
private final Logger log;
@GuardedBy("lock")
private int concurrentTasks = 0;
/**
* @param tag the tag to be used for logging
* @param delegate the executor to which tasks will be delegated
* @param maxConcurrentTasks the maximum number of tasks that will be
* delegated concurrently. If this is set to 1, tasks submitted to this
* executor will run in the order they are submitted and will not run
* concurrently
*/
public PoliteExecutor(String tag, Executor delegate,
int maxConcurrentTasks) {
this.delegate = delegate;
this.maxConcurrentTasks = maxConcurrentTasks;
log = Logger.getLogger(tag);
}
@Override
public void execute(final Runnable r) {
final long submitted = System.currentTimeMillis();
Runnable wrapped = new Runnable() {
@Override
public void run() {
if (log.isLoggable(LOG_LEVEL)) {
long queued = System.currentTimeMillis() - submitted;
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
}
try {
r.run();
} finally {
scheduleNext();
}
}
};
synchronized (lock) {
if (concurrentTasks < maxConcurrentTasks) {
concurrentTasks++;
delegate.execute(wrapped);
} else {
queue.add(wrapped);
}
}
}
private void scheduleNext() {
synchronized (lock) {
Runnable next = queue.poll();
if (next == null) concurrentTasks--;
else delegate.execute(next);
}
}
}

View File

@@ -0,0 +1,49 @@
package org.briarproject.bramble;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.logging.Level.FINE;
@NotNullByDefault
public class TimeLoggingExecutor extends ThreadPoolExecutor {
private static final Level LOG_LEVEL = FINE;
private final Logger log;
public TimeLoggingExecutor(String tag, int corePoolSize, int maxPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue,
handler);
log = Logger.getLogger(tag);
}
@Override
public void execute(final Runnable r) {
if (log.isLoggable(LOG_LEVEL)) {
final long submitted = System.currentTimeMillis();
super.execute(new Runnable() {
@Override
public void run() {
long started = System.currentTimeMillis();
long queued = started - submitted;
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
r.run();
long executing = System.currentTimeMillis() - started;
log.log(LOG_LEVEL, "Execution time " + executing + " ms");
}
});
} else {
super.execute(r);
}
}
}

View File

@@ -1,62 +0,0 @@
package org.briarproject.bramble.crypto;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
/**
* A {@link SecureRandom} implementation that combines the outputs of two or
* more other implementations using XOR.
*/
class CombinedSecureRandom extends SecureRandom {
private static final Provider PROVIDER = new CombinedProvider();
CombinedSecureRandom(SecureRandom... randoms) {
super(new CombinedSecureRandomSpi(randoms), PROVIDER);
}
private static class CombinedSecureRandomSpi extends SecureRandomSpi {
private final SecureRandom[] randoms;
private CombinedSecureRandomSpi(SecureRandom... randoms) {
if (randoms.length < 2) throw new IllegalArgumentException();
this.randoms = randoms;
}
@Override
protected byte[] engineGenerateSeed(int numBytes) {
byte[] combined = new byte[numBytes];
for (SecureRandom random : randoms) {
byte[] b = random.generateSeed(numBytes);
int length = Math.min(numBytes, b.length);
for (int i = 0; i < length; i++)
combined[i] = (byte) (combined[i] ^ b[i]);
}
return combined;
}
@Override
protected void engineNextBytes(byte[] b) {
byte[] temp = new byte[b.length];
for (SecureRandom random : randoms) {
random.nextBytes(temp);
for (int i = 0; i < b.length; i++)
b[i] = (byte) (b[i] ^ temp[i]);
}
}
@Override
protected void engineSetSeed(byte[] seed) {
for (SecureRandom random : randoms) random.setSeed(seed);
}
}
private static class CombinedProvider extends Provider {
private CombinedProvider() {
super("Combined", 1.0, "");
}
}
}

View File

@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
@@ -29,7 +29,10 @@ import org.spongycastle.crypto.params.KeyParameter;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,8 +45,10 @@ import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_B
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.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_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent {
@@ -101,16 +106,26 @@ class CryptoComponentImpl implements CryptoComponent {
private final MessageEncrypter messageEncrypter;
@Inject
CryptoComponentImpl(SeedProvider seedProvider) {
if (!FortunaSecureRandom.selfTest()) throw new RuntimeException();
SecureRandom platformSecureRandom = new SecureRandom();
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
if (LOG.isLoggable(INFO)) {
String provider = platformSecureRandom.getProvider().getName();
String algorithm = platformSecureRandom.getAlgorithm();
LOG.info("Default SecureRandom: " + provider + " " + algorithm);
SecureRandom defaultSecureRandom = new SecureRandom();
String name = defaultSecureRandom.getProvider().getName();
String algorithm = defaultSecureRandom.getAlgorithm();
LOG.info("Default SecureRandom: " + name + " " + algorithm);
}
SecureRandom fortuna = new FortunaSecureRandom(seedProvider.getSeed());
secureRandom = new CombinedSecureRandom(platformSecureRandom, fortuna);
Provider provider = secureRandomProvider.getProvider();
if (provider == null) {
LOG.info("Using default");
} else {
installSecureRandomProvider(provider);
if (LOG.isLoggable(INFO)) {
SecureRandom installedSecureRandom = new SecureRandom();
String name = installedSecureRandom.getProvider().getName();
String algorithm = installedSecureRandom.getAlgorithm();
LOG.info("Installed SecureRandom: " + name + " " + algorithm);
}
}
secureRandom = new SecureRandom();
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
PARAMETERS, secureRandom);
agreementKeyPairGenerator = new ECKeyPairGenerator();
@@ -124,6 +139,31 @@ class CryptoComponentImpl implements CryptoComponent {
messageEncrypter = new MessageEncrypter(secureRandom);
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void installSecureRandomProvider(Provider provider) {
Provider[] providers = Security.getProviders("SecureRandom.SHA1PRNG");
if (providers == null || providers.length == 0
|| !provider.getClass().equals(providers[0].getClass())) {
Security.insertProviderAt(provider, 1);
}
// Check the new provider is the default when no algorithm is specified
SecureRandom random = new SecureRandom();
if (!provider.getClass().equals(random.getProvider().getClass())) {
throw new SecurityException("Wrong SecureRandom provider: "
+ random.getProvider().getClass());
}
// Check the new provider is the default when SHA1PRNG is specified
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
}
if (!provider.getClass().equals(random.getProvider().getClass())) {
throw new SecurityException("Wrong SHA1PRNG provider: "
+ random.getProvider().getClass());
}
}
@Override
public SecretKey generateSecretKey() {
byte[] b = new byte[SecretKey.LENGTH];
@@ -133,7 +173,10 @@ class CryptoComponentImpl implements CryptoComponent {
@Override
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
return new PseudoRandomImpl(seed1, 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
@@ -296,7 +339,7 @@ class CryptoComponentImpl implements CryptoComponent {
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
return deriveMasterSecret(deriveSharedSecret(
theirPublicKey,ourKeyPair, alice));
theirPublicKey, ourKeyPair, alice));
}
@Override
@@ -371,8 +414,11 @@ class CryptoComponentImpl implements CryptoComponent {
}
@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 (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
// Initialise the PRF
@@ -380,10 +426,14 @@ class CryptoComponentImpl implements CryptoComponent {
// The output of the PRF must be long enough to use as a tag
int macLength = prf.getDigestSize();
if (macLength < TAG_LENGTH) throw new IllegalStateException();
// The input is the stream number as a 64-bit integer
byte[] input = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, input, 0);
prf.update(input, 0, input.length);
// The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
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];
prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC
@@ -607,7 +657,7 @@ class CryptoComponentImpl implements CryptoComponent {
}
private long sampleRunningTime(int iterations) {
byte[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
byte[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
byte[] salt = new byte[PBKDF_SALT_BYTES];
int keyLengthInBits = SecretKey.LENGTH * 8;
long start = System.nanoTime();

View File

@@ -1,12 +1,13 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.security.SecureRandom;
import java.util.concurrent.BlockingQueue;
@@ -31,14 +32,17 @@ public class CryptoModule {
public static class EagerSingletons {
@Inject
@CryptoExecutor
Executor cryptoExecutor;
ExecutorService cryptoExecutor;
}
/**
* The maximum number of executor threads.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_EXECUTOR_THREADS =
Runtime.getRuntime().availableProcessors();
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
private final ExecutorService cryptoExecutor;
@@ -49,8 +53,8 @@ public class CryptoModule {
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
// Create a limited # of threads and keep them in the pool for 60 secs
cryptoExecutor = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
60, SECONDS, queue, policy);
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
}
@Provides
@@ -60,8 +64,9 @@ public class CryptoModule {
@Provides
@Singleton
CryptoComponent provideCryptoComponent(SeedProvider seedProvider) {
return new CryptoComponentImpl(seedProvider);
CryptoComponent provideCryptoComponent(
SecureRandomProvider secureRandomProvider) {
return new CryptoComponentImpl(secureRandomProvider);
}
@Provides
@@ -84,11 +89,18 @@ public class CryptoModule {
@Provides
@Singleton
@CryptoExecutor
Executor getCryptoExecutor(LifecycleManager lifecycleManager) {
ExecutorService getCryptoExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(cryptoExecutor);
return cryptoExecutor;
}
@Provides
@CryptoExecutor
Executor getCryptoExecutor() {
return cryptoExecutor;
}
@Provides
SecureRandom getSecureRandom(CryptoComponent crypto) {
return crypto.getSecureRandom();

View File

@@ -1,76 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.Digest;
import javax.annotation.concurrent.NotThreadSafe;
/**
* A message digest that prevents length extension attacks - see Ferguson and
* Schneier, <i>Practical Cryptography</i>, chapter 6.
* <p>
* "Let h be an interative hash function. The hash function h<sub>d</sub> is
* defined by h<sub>d</sub> := h(h(m)), and has a claimed security level of
* min(k, n/2) where k is the security level of h and n is the size of the hash
* result."
*/
@NotThreadSafe
@NotNullByDefault
class DoubleDigest implements Digest {
private final Digest delegate;
DoubleDigest(Digest delegate) {
this.delegate = delegate;
}
private byte[] digest() {
byte[] digest = new byte[delegate.getDigestSize()];
delegate.doFinal(digest, 0); // h(m)
delegate.update(digest, 0, digest.length);
delegate.doFinal(digest, 0); // h(h(m))
return digest;
}
public int digest(byte[] buf, int offset, int len) {
byte[] digest = digest();
len = Math.min(len, digest.length);
System.arraycopy(digest, 0, buf, offset, len);
return len;
}
@Override
public int getDigestSize() {
return delegate.getDigestSize();
}
@Override
public String getAlgorithmName() {
return "Double " + delegate.getAlgorithmName();
}
@Override
public void reset() {
delegate.reset();
}
@Override
public void update(byte input) {
delegate.update(input);
}
public void update(byte[] input) {
delegate.update(input, 0, input.length);
}
@Override
public void update(byte[] input, int offset, int len) {
delegate.update(input, offset, len);
}
@Override
public int doFinal(byte[] out, int outOff) {
return digest(out, outOff, delegate.getDigestSize());
}
}

View File

@@ -1,114 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESLightEngine;
import org.spongycastle.crypto.params.KeyParameter;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.concurrent.ThreadSafe;
/**
* Implements the Fortuna pseudo-random number generator, as described in
* Ferguson and Schneier, <i>Practical Cryptography</i>, chapter 9.
*/
@ThreadSafe
@NotNullByDefault
class FortunaGenerator {
private static final int MAX_BYTES_PER_REQUEST = 1024 * 1024;
private static final int KEY_BYTES = 32;
private static final int BLOCK_BYTES = 16;
private final Lock lock = new ReentrantLock();
// The following are locking: lock
private final DoubleDigest digest = new DoubleDigest(new SHA256Digest());
private final BlockCipher cipher = new AESLightEngine();
private final byte[] key = new byte[KEY_BYTES];
private final byte[] counter = new byte[BLOCK_BYTES];
private final byte[] buffer = new byte[BLOCK_BYTES];
private final byte[] newKey = new byte[KEY_BYTES];
FortunaGenerator(byte[] seed) {
reseed(seed);
}
void reseed(byte[] seed) {
lock.lock();
try {
digest.update(key);
digest.update(seed);
digest.digest(key, 0, KEY_BYTES);
incrementCounter();
} finally {
lock.unlock();
}
}
// Package access for testing
void incrementCounter() {
lock.lock();
try {
counter[0]++;
for (int i = 0; counter[i] == 0; i++) {
if (i + 1 == BLOCK_BYTES)
throw new RuntimeException("Counter exhausted");
counter[i + 1]++;
}
} finally {
lock.unlock();
}
}
// Package access for testing
byte[] getCounter() {
lock.lock();
try {
return counter;
} finally {
lock.unlock();
}
}
int nextBytes(byte[] dest, int off, int len) {
lock.lock();
try {
// Don't write more than the maximum number of bytes in one request
if (len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
cipher.init(true, new KeyParameter(key));
// Generate full blocks directly into the output buffer
int fullBlocks = len / BLOCK_BYTES;
for (int i = 0; i < fullBlocks; i++) {
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
incrementCounter();
}
// Generate a partial block if needed
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
if (remaining >= BLOCK_BYTES) throw new AssertionError();
if (remaining > 0) {
cipher.processBlock(counter, 0, buffer, 0);
incrementCounter();
// Copy the partial block to the output buffer and erase our copy
System.arraycopy(buffer, 0, dest, off + done, remaining);
for (int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
}
// Generate a new key
for (int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
incrementCounter();
}
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
for (int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
// Return the number of bytes written
return len;
} finally {
lock.unlock();
}
}
}

View File

@@ -1,81 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.util.StringUtils;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.util.Arrays;
/**
* A {@link java.security.SecureRandom SecureRandom} implementation based on a
* {@link FortunaGenerator}.
*/
class FortunaSecureRandom extends SecureRandom {
// Package access for testing
static final byte[] SELF_TEST_VECTOR_1 =
StringUtils.fromHexString("4BD6EA599D47E3EE9DD911833C29CA22");
static final byte[] SELF_TEST_VECTOR_2 =
StringUtils.fromHexString("10984D576E6850E505CA9F42A9BFD88A");
static final byte[] SELF_TEST_VECTOR_3 =
StringUtils.fromHexString("1E12DA166BD86DCECDE50A8296018DE2");
private static final Provider PROVIDER = new FortunaProvider();
FortunaSecureRandom(byte[] seed) {
super(new FortunaSecureRandomSpi(seed), PROVIDER);
}
/**
* Tests that the {@link #nextBytes(byte[])} and {@link #setSeed(byte[])}
* methods are passed through to the generator in the expected way.
*/
static boolean selfTest() {
byte[] seed = new byte[32];
SecureRandom r = new FortunaSecureRandom(seed);
byte[] output = new byte[16];
r.nextBytes(output);
if (!Arrays.equals(SELF_TEST_VECTOR_1, output)) return false;
r.nextBytes(output);
if (!Arrays.equals(SELF_TEST_VECTOR_2, output)) return false;
r.setSeed(seed);
r.nextBytes(output);
return Arrays.equals(SELF_TEST_VECTOR_3, output);
}
private static class FortunaSecureRandomSpi extends SecureRandomSpi {
private final FortunaGenerator generator;
private FortunaSecureRandomSpi(byte[] seed) {
generator = new FortunaGenerator(seed);
}
@Override
protected byte[] engineGenerateSeed(int numBytes) {
byte[] b = new byte[numBytes];
engineNextBytes(b);
return b;
}
@Override
protected void engineNextBytes(byte[] b) {
int offset = 0;
while (offset < b.length)
offset += generator.nextBytes(b, offset, b.length - offset);
}
@Override
protected void engineSetSeed(byte[] seed) {
generator.reseed(seed);
}
}
private static class FortunaProvider extends Provider {
private FortunaProvider() {
super("Fortuna", 1.0, "");
}
}
}

View File

@@ -11,31 +11,14 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault
class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
private static final int LOWER = 26;
private static final int UPPER = 26;
private static final int DIGIT = 10;
private static final int OTHER = 10;
private static final double STRONG = Math.log(Math.pow(LOWER + UPPER +
DIGIT + OTHER, 10));
// The minimum number of unique characters in a strong password
private static final int STRONG_UNIQUE_CHARS = 12;
@Override
public float estimateStrength(String password) {
HashSet<Character> unique = new HashSet<Character>();
int length = password.length();
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
boolean lower = false, upper = false, digit = false, other = false;
for (char c : unique) {
if (Character.isLowerCase(c)) lower = true;
else if (Character.isUpperCase(c)) upper = true;
else if (Character.isDigit(c)) digit = true;
else other = true;
}
int alphabetSize = 0;
if (lower) alphabetSize += LOWER;
if (upper) alphabetSize += UPPER;
if (digit) alphabetSize += DIGIT;
if (other) alphabetSize += OTHER;
double score = Math.log(Math.pow(alphabetSize, unique.size()));
return Math.min(1, (float) (score / STRONG));
return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS);
}
}

View File

@@ -2,30 +2,34 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.ByteUtils;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.engines.Salsa20Engine;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
@NotThreadSafe
@NotNullByDefault
class PseudoRandomImpl implements PseudoRandom {
private final FortunaGenerator generator;
private final Salsa20Engine cipher = new Salsa20Engine();
PseudoRandomImpl(int seed1, int seed2) {
byte[] seed = new byte[INT_32_BYTES * 2];
ByteUtils.writeUint32(seed1, seed, 0);
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
generator = new FortunaGenerator(seed);
PseudoRandomImpl(byte[] seed) {
// Hash the seed to produce a 32-byte key
byte[] key = new byte[32];
Digest digest = new Blake2sDigest();
digest.update(seed, 0, seed.length);
digest.doFinal(key, 0);
// Initialise the stream cipher with an all-zero nonce
byte[] nonce = new byte[8];
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
}
@Override
public byte[] nextBytes(int length) {
byte[] b = new byte[length];
int offset = 0;
while (offset < length) offset += generator.nextBytes(b, offset, length);
return b;
byte[] in = new byte[length], out = new byte[length];
cipher.processBytes(in, 0, length, out, 0);
return out;
}
}

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.MAX_FRAME_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_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;
@NotThreadSafe
@@ -117,7 +119,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
private void readStreamHeader() throws IOException {
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
int offset = 0;
while (offset < STREAM_HEADER_LENGTH) {
@@ -126,21 +128,35 @@ class StreamDecrypterImpl implements StreamDecrypter {
if (read == -1) throw new EOFException();
offset += read;
}
// The nonce consists of the stream number followed by the IV
// Extract the nonce
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce,
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
STREAM_HEADER_NONCE_LENGTH);
// Decrypt and authenticate the stream header
try {
cipher.init(false, streamHeaderKey, streamHeaderNonce);
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);
if (decrypted != SecretKey.LENGTH) throw new RuntimeException();
if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
throw new RuntimeException();
} catch (GeneralSecurityException e) {
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.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;
@Immutable
@@ -36,22 +37,22 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber);
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv);
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
streamHeaderIv, ctx.getHeaderKey(), frameKey);
streamHeaderNonce, ctx.getHeaderKey(), frameKey);
}
@Override
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey) {
AuthenticatedCipher cipher = cipherProvider.get();
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv,
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
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.MAX_FRAME_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_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;
@NotThreadSafe
@@ -33,7 +35,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
private final long streamNumber;
@Nullable
private final byte[] tag;
private final byte[] streamHeaderIv;
private final byte[] streamHeaderNonce;
private final byte[] frameNonce, frameHeader;
private final byte[] framePlaintext, frameCiphertext;
@@ -41,13 +43,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
private boolean writeTag, writeStreamHeader;
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
SecretKey streamHeaderKey, SecretKey frameKey) {
this.out = out;
this.cipher = cipher;
this.streamNumber = streamNumber;
this.tag = tag;
this.streamHeaderIv = streamHeaderIv;
this.streamHeaderNonce = streamHeaderNonce;
this.streamHeaderKey = streamHeaderKey;
this.frameKey = frameKey;
frameNonce = new byte[FRAME_NONCE_LENGTH];
@@ -62,6 +64,8 @@ class StreamEncrypterImpl implements StreamEncrypter {
@Override
public void writeFrame(byte[] payload, int payloadLength,
int paddingLength, boolean finalFrame) throws IOException {
if (payloadLength < 0 || paddingLength < 0)
throw new IllegalArgumentException();
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
throw new IllegalArgumentException();
// Don't allow the frame counter to wrap
@@ -112,22 +116,23 @@ class StreamEncrypterImpl implements StreamEncrypter {
}
private void writeStreamHeader() throws IOException {
// The nonce consists of the stream number followed by the IV
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES,
STREAM_HEADER_IV_LENGTH);
byte[] streamHeaderPlaintext = frameKey.getBytes();
// The header contains the protocol version, stream number and frame key
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
INT_16_BYTES);
System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_IV_LENGTH);
// Encrypt and authenticate the frame key
System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_NONCE_LENGTH);
// Encrypt and authenticate the stream header key
try {
cipher.init(true, streamHeaderKey, streamHeaderNonce);
int encrypted = cipher.process(streamHeaderPlaintext, 0,
SecretKey.LENGTH, streamHeaderCiphertext,
STREAM_HEADER_IV_LENGTH);
if (encrypted != SecretKey.LENGTH + MAC_LENGTH)
STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
STREAM_HEADER_NONCE_LENGTH);
if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
throw new RuntimeException();
} catch (GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);

View File

@@ -70,25 +70,7 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
byte[] subKey = new byte[SUBKEY_LENGTH];
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
// Reverse the order of the Poly130 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
// Clamp the subkey
Poly1305KeyGenerator.clamp(subKey);
// Initialize Poly1305 with the subkey

View File

@@ -67,6 +67,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@@ -130,8 +131,14 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
// Don't allow reentrant locking
if (lock.getReadHoldCount() > 0) throw new IllegalStateException();
if (lock.getWriteHoldCount() > 0) throw new IllegalStateException();
long start = System.currentTimeMillis();
if (readOnly) lock.readLock().lock();
else lock.writeLock().lock();
if (LOG.isLoggable(FINE)) {
long duration = System.currentTimeMillis() - start;
if (readOnly) LOG.fine("Waited " + duration + " ms for read lock");
else LOG.fine("Waited " + duration + " ms for write lock");
}
try {
return new Transaction(db.startTransaction(), readOnly);
} catch (DbException e) {
@@ -661,7 +668,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
acked.add(m);
}
}
transaction.attach(new MessagesAckedEvent(c, acked));
if (acked.size() > 0) {
transaction.attach(new MessagesAckedEvent(c, acked));
}
}
@Override

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
@@ -36,8 +37,8 @@ public class DatabaseExecutorModule {
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
// Use a single thread and keep it in the pool for 60 secs
databaseExecutor = new ThreadPoolExecutor(0, 1, 60, SECONDS, queue,
policy);
databaseExecutor = new TimeLoggingExecutor("DatabaseExecutor", 0, 1,
60, SECONDS, queue, policy);
}
@Provides

View File

@@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
@NotNullByDefault
abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 29;
private static final int MIN_SCHEMA_VERSION = 29;
private static final int SCHEMA_VERSION = 30;
private static final int MIN_SCHEMA_VERSION = 30;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"

View File

@@ -34,6 +34,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
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.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
@@ -43,7 +44,7 @@ class LanTcpPlugin extends TcpPlugin {
private static final Logger LOG =
Logger.getLogger(LanTcpPlugin.class.getName());
private static final int MAX_ADDRESSES = 5;
private static final int MAX_ADDRESSES = 4;
private static final String PROP_IP_PORTS = "ipPorts";
private static final String SEPARATOR = ",";
@@ -82,19 +83,19 @@ class LanTcpPlugin extends TcpPlugin {
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
String[] split = ipPorts.split(SEPARATOR);
List<InetSocketAddress> remotes = new ArrayList<InetSocketAddress>();
List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>();
for (String ipPort : split) {
InetSocketAddress a = parseSocketAddress(ipPort);
if (a != null) remotes.add(a);
if (a != null) addresses.add(a);
}
return remotes;
return addresses;
}
@Override
protected void setLocalSocketAddress(InetSocketAddress a) {
String ipPort = getIpPortString(a);
// Get the list of recently used addresses
String setting = callback.getSettings().get(PROP_IP_PORTS);
String setting = callback.getSettings().get(PREF_LAN_IP_PORTS);
List<String> recent = new ArrayList<String>();
if (!StringUtils.isNullOrEmpty(setting))
Collections.addAll(recent, setting.split(SEPARATOR));
@@ -120,7 +121,7 @@ class LanTcpPlugin extends TcpPlugin {
}
// Save the setting
Settings settings = new Settings();
settings.put(PROP_IP_PORTS, setting);
settings.put(PREF_LAN_IP_PORTS, setting);
callback.mergeSettings(settings);
}

View File

@@ -8,6 +8,7 @@ import dagger.Module;
import dagger.Provides;
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_SOCKET_TIMEOUT;
import static org.briarproject.bramble.api.plugin.TorConstants.SOCKS_PORT;
@Module
@@ -17,6 +18,7 @@ public class SocksModule {
SocketFactory provideTorSocketFactory() {
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
SOCKS_PORT);
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT);
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT,
EXTRA_SOCKET_TIMEOUT);
}
}

View File

@@ -6,18 +6,36 @@ import org.briarproject.bramble.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Arrays;
class SocksSocket extends Socket {
private final SocketAddress proxy;
private final int connectToProxyTimeout;
private static final String[] ERRORS = {
"Succeeded",
"General SOCKS server failure",
"Connection not allowed by ruleset",
"Network unreachable",
"Host unreachable",
"Connection refused",
"TTL expired",
"Command not supported",
"Address type not supported"
};
SocksSocket(SocketAddress proxy, int connectToProxyTimeout) {
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
private final SocketAddress proxy;
private final int connectToProxyTimeout, extraSocketTimeout;
SocksSocket(SocketAddress proxy, int connectToProxyTimeout,
int extraSocketTimeout) {
this.proxy = proxy;
this.connectToProxyTimeout = connectToProxyTimeout;
this.extraSocketTimeout = extraSocketTimeout;
}
@Override
@@ -28,6 +46,11 @@ class SocksSocket extends Socket {
if (!(endpoint instanceof InetSocketAddress))
throw new IllegalArgumentException();
InetSocketAddress inet = (InetSocketAddress) endpoint;
InetAddress address = inet.getAddress();
if (address != null
&& !Arrays.equals(address.getAddress(), UNSPECIFIED_ADDRESS)) {
throw new IllegalArgumentException();
}
String host = inet.getHostName();
if (host.length() > 255) throw new IllegalArgumentException();
int port = inet.getPort();
@@ -41,16 +64,16 @@ class SocksSocket extends Socket {
sendMethodRequest(out);
receiveMethodResponse(in);
// Use the supplied timeout temporarily
// Use the supplied timeout temporarily, plus any configured extra
int oldTimeout = getSoTimeout();
setSoTimeout(timeout);
setSoTimeout(timeout + extraSocketTimeout);
// Connect to the endpoint via the proxy
sendConnectRequest(out, host, port);
receiveConnectResponse(in);
// Restore the old timeout
setSoTimeout(oldTimeout);
// Restore the old timeout, plus any configured extra
setSoTimeout(oldTimeout + extraSocketTimeout);
}
private void sendMethodRequest(OutputStream out) throws IOException {
@@ -93,13 +116,16 @@ class SocksSocket extends Socket {
private void receiveConnectResponse(InputStream in) throws IOException {
byte[] connectResponse = new byte[4];
IoUtils.read(in, connectResponse);
byte version = connectResponse[0];
byte reply = connectResponse[1];
byte addressType = connectResponse[3];
int version = connectResponse[0] & 0xFF;
int reply = connectResponse[1] & 0xFF;
int addressType = connectResponse[3] & 0xFF;
if (version != 5)
throw new IOException("Unsupported SOCKS version: " + version);
if (reply != 0)
throw new IOException("Connection failed: " + reply);
if (reply != 0) {
if (reply < ERRORS.length)
throw new IOException("Connection failed: " + ERRORS[reply]);
else throw new IOException("Connection failed: " + reply);
}
if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
else throw new IOException("Unsupported address type: " + addressType);

View File

@@ -11,16 +11,18 @@ import javax.net.SocketFactory;
class SocksSocketFactory extends SocketFactory {
private final SocketAddress proxy;
private final int connectToProxyTimeout;
private final int connectToProxyTimeout, extraSocketTimeout;
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout) {
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout,
int extraSocketTimeout) {
this.proxy = proxy;
this.connectToProxyTimeout = connectToProxyTimeout;
this.extraSocketTimeout = extraSocketTimeout;
}
@Override
public Socket createSocket() {
return new SocksSocket(proxy, connectToProxyTimeout);
return new SocksSocket(proxy, connectToProxyTimeout, extraSocketTimeout);
}
@Override

View File

@@ -1,6 +1,8 @@
package org.briarproject.bramble.sync;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus;
@@ -29,6 +31,16 @@ public class SyncModule {
ValidationManager validationManager;
}
/**
* The maximum number of validation tasks to delegate to the crypto
* executor concurrently.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_CONCURRENT_VALIDATION_TASKS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
@Provides
GroupFactory provideGroupFactory(CryptoComponent crypto) {
return new GroupFactoryImpl(crypto);
@@ -62,10 +74,20 @@ public class SyncModule {
@Provides
@Singleton
ValidationManager getValidationManager(LifecycleManager lifecycleManager,
EventBus eventBus, ValidationManagerImpl validationManager) {
ValidationManager provideValidationManager(
LifecycleManager lifecycleManager, EventBus eventBus,
ValidationManagerImpl validationManager) {
lifecycleManager.registerService(validationManager);
eventBus.addListener(validationManager);
return validationManager;
}
@Provides
@Singleton
@ValidationExecutor
Executor provideValidationExecutor(
@CryptoExecutor Executor cryptoExecutor) {
return new PoliteExecutor("ValidationExecutor", cryptoExecutor,
MAX_CONCURRENT_VALIDATION_TASKS);
}
}

View File

@@ -0,0 +1,25 @@
package org.briarproject.bramble.sync;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the executor for validation tasks. Also used for
* annotating methods that should run on the validation executor.
* <p>
* The contract of this executor is that tasks may be run concurrently, and
* submitting a task will never block. Tasks must not run indefinitely. Tasks
* submitted during shutdown are discarded.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
@interface ValidationExecutor {
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.sync;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
@@ -50,8 +49,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
Logger.getLogger(ValidationManagerImpl.class.getName());
private final DatabaseComponent db;
private final Executor dbExecutor;
private final Executor cryptoExecutor;
private final Executor dbExecutor, validationExecutor;
private final MessageFactory messageFactory;
private final Map<ClientId, MessageValidator> validators;
private final Map<ClientId, IncomingMessageHook> hooks;
@@ -60,11 +58,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
@Inject
ValidationManagerImpl(DatabaseComponent db,
@DatabaseExecutor Executor dbExecutor,
@CryptoExecutor Executor cryptoExecutor,
@ValidationExecutor Executor validationExecutor,
MessageFactory messageFactory) {
this.db = db;
this.dbExecutor = dbExecutor;
this.cryptoExecutor = cryptoExecutor;
this.validationExecutor = validationExecutor;
this.messageFactory = messageFactory;
validators = new ConcurrentHashMap<ClientId, MessageValidator>();
hooks = new ConcurrentHashMap<ClientId, IncomingMessageHook>();
@@ -104,6 +102,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void validateOutstandingMessages(ClientId c) {
try {
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
@@ -130,6 +129,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void validateNextMessage(Queue<MessageId> unvalidated) {
try {
Message m;
@@ -167,6 +167,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void deliverOutstandingMessages(ClientId c) {
try {
Queue<MessageId> pending = new LinkedList<MessageId>();
@@ -194,6 +195,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void deliverNextPendingMessage(Queue<MessageId> pending) {
try {
boolean anyInvalid = false, allDelivered = true;
@@ -220,8 +222,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
Message m = messageFactory.createMessage(id, raw);
Group g = db.getGroup(txn, m.getGroupId());
ClientId c = g.getClientId();
Metadata meta = db.getMessageMetadataForValidator(txn,
id);
Metadata meta =
db.getMessageMetadataForValidator(txn, id);
DeliveryResult result = deliverMessage(txn, m, c, meta);
if (result.valid) {
pending.addAll(getPendingDependents(txn, id));
@@ -240,8 +242,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
db.endTransaction(txn);
}
if (invalidate != null) invalidateNextMessageAsync(invalidate);
deliverNextPendingMessageAsync(pending);
if (toShare != null) shareNextMessageAsync(toShare);
deliverNextPendingMessageAsync(pending);
} catch (NoSuchMessageException e) {
LOG.info("Message removed before delivery");
deliverNextPendingMessageAsync(pending);
@@ -249,13 +251,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
LOG.info("Group removed before delivery");
deliverNextPendingMessageAsync(pending);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private void validateMessageAsync(final Message m, final Group g) {
cryptoExecutor.execute(new Runnable() {
validationExecutor.execute(new Runnable() {
@Override
public void run() {
validateMessage(m, g);
@@ -263,10 +264,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@ValidationExecutor
private void validateMessage(Message m, Group g) {
MessageValidator v = validators.get(g.getClientId());
if (v == null) {
LOG.warning("No validator");
if (LOG.isLoggable(WARNING))
LOG.warning("No validator for " + g.getClientId().getString());
} else {
try {
MessageContext context = v.validateMessage(m, g);
@@ -291,6 +294,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void storeMessageContext(Message m, ClientId c,
MessageContext context) {
try {
@@ -353,6 +357,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
}
}
@DatabaseExecutor
private DeliveryResult deliverMessage(Transaction txn, Message m,
ClientId c, Metadata meta) throws DbException {
// Deliver the message to the client if it's registered a hook
@@ -362,10 +367,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
try {
shareMsg = hook.incomingMessage(txn, m, meta);
} catch (InvalidMessageException e) {
// message is invalid, mark it as such and delete it
db.setMessageState(txn, m.getId(), INVALID);
db.deleteMessageMetadata(txn, m.getId());
db.deleteMessage(txn, m.getId());
invalidateMessage(txn, m.getId());
return new DeliveryResult(false, false);
}
}
@@ -373,6 +375,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
return new DeliveryResult(true, shareMsg);
}
@DatabaseExecutor
private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m)
throws DbException {
Queue<MessageId> pending = new LinkedList<MessageId>();
@@ -392,6 +395,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void shareOutstandingMessages(ClientId c) {
try {
Queue<MessageId> toShare = new LinkedList<MessageId>();
@@ -424,6 +428,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void shareNextMessage(Queue<MessageId> toShare) {
try {
Transaction txn = db.startTransaction(false);
@@ -457,6 +462,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void invalidateNextMessage(Queue<MessageId> invalidate) {
try {
Transaction txn = db.startTransaction(false);
@@ -479,6 +485,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
}
}
@DatabaseExecutor
private void invalidateMessage(Transaction txn, MessageId m)
throws DbException {
db.setMessageState(txn, m, INVALID);
@@ -486,6 +493,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
db.deleteMessageMetadata(txn, m);
}
@DatabaseExecutor
private Queue<MessageId> getDependentsToInvalidate(Transaction txn,
MessageId m) throws DbException {
Queue<MessageId> invalidate = new LinkedList<MessageId>();
@@ -515,6 +523,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
});
}
@DatabaseExecutor
private void loadGroupAndValidate(final Message m) {
try {
Group g;
@@ -534,6 +543,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
}
private static class DeliveryResult {
private final boolean valid, share;
private DeliveryResult(boolean valid, boolean share) {

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
abstract class AbstractSecureRandomProvider implements SecureRandomProvider {
// Contribute whatever slightly unpredictable info we have to the pool
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
out.writeLong(System.currentTimeMillis());
out.writeLong(System.nanoTime());
out.writeLong(Runtime.getRuntime().freeMemory());
List<NetworkInterface> ifaces =
Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface i : ifaces) {
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
for (InetAddress a : addrs) out.write(a.getAddress());
byte[] hardware = i.getHardwareAddress();
if (hardware != null) out.write(hardware);
}
for (Entry<String, String> e : System.getenv().entrySet()) {
out.writeUTF(e.getKey());
out.writeUTF(e.getValue());
}
Properties properties = System.getProperties();
for (String key : properties.stringPropertyNames())
out.writeUTF(properties.getProperty(key));
}
}

View File

@@ -0,0 +1,69 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Provider;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomProvider.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final AtomicBoolean seeded = new AtomicBoolean(false);
private final File outputDevice;
LinuxSecureRandomProvider() {
this(RANDOM_DEVICE);
}
LinuxSecureRandomProvider(File outputDevice) {
this.outputDevice = outputDevice;
}
@Override
public Provider getProvider() {
if (!seeded.getAndSet(true)) writeSeed();
return new LinuxProvider();
}
protected void writeSeed() {
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream(outputDevice));
writeToEntropyPool(out);
out.flush();
out.close();
} catch (IOException e) {
// On some devices /dev/urandom isn't writable - this isn't fatal
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private static class LinuxProvider extends Provider {
private LinuxProvider() {
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some callers
// explicitly request a SHA1PRNG SecureRandom and we need to
// prevent them from getting the default implementation whose
// output may have low entropy.
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}
}

View File

@@ -0,0 +1,64 @@
package org.briarproject.bramble.system;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandomSpi;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
public class LinuxSecureRandomSpi extends SecureRandomSpi {
private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomSpi.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final File inputDevice, outputDevice;
public LinuxSecureRandomSpi() {
this(RANDOM_DEVICE, RANDOM_DEVICE);
}
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
this.inputDevice = inputDevice;
this.outputDevice = outputDevice;
}
@Override
protected void engineSetSeed(byte[] seed) {
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream(outputDevice));
out.write(seed);
out.flush();
out.close();
} catch (IOException e) {
// On some devices /dev/urandom isn't writable - this isn't fatal
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
protected void engineNextBytes(byte[] bytes) {
try {
DataInputStream in = new DataInputStream(
new FileInputStream(inputDevice));
in.readFully(bytes);
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected byte[] engineGenerateSeed(int len) {
byte[] seed = new byte[len];
engineNextBytes(seed);
return seed;
}
}

View File

@@ -1,75 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SeedProvider;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
class LinuxSeedProvider implements SeedProvider {
private static final Logger LOG =
Logger.getLogger(LinuxSeedProvider.class.getName());
private final String outputFile, inputFile;
LinuxSeedProvider() {
this("/dev/urandom", "/dev/urandom");
}
LinuxSeedProvider(String outputFile, String inputFile) {
this.outputFile = outputFile;
this.inputFile = inputFile;
}
@Override
public byte[] getSeed() {
byte[] seed = new byte[SEED_BYTES];
// Contribute whatever slightly unpredictable info we have to the pool
try {
DataOutputStream out = new DataOutputStream(
new FileOutputStream(outputFile));
writeToEntropyPool(out);
out.flush();
out.close();
} catch (IOException e) {
// On some devices /dev/urandom isn't writable - this isn't fatal
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
// Read the seed from the pool
try {
DataInputStream in = new DataInputStream(
new FileInputStream(inputFile));
in.readFully(seed);
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return seed;
}
void writeToEntropyPool(DataOutputStream out) throws IOException {
out.writeLong(System.currentTimeMillis());
out.writeLong(System.nanoTime());
List<NetworkInterface> ifaces =
Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface i : ifaces) {
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
for (InetAddress a : addrs) out.write(a.getAddress());
}
}
}

View File

@@ -29,6 +29,7 @@ import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
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.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
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()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
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);
}
}
@@ -242,7 +244,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) {
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(
tagCtx.contactId, inKeys, streamNumber));
}
@@ -250,7 +253,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
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));
}
// Write the window back to the DB

View File

@@ -0,0 +1,142 @@
package org.briarproject.bramble;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class PoliteExecutorTest extends BrambleTestCase {
private static final String TAG = "Test";
private static final int TASKS = 10;
@Test
public void testTasksAreDelegatedInOrderOfSubmission() throws Exception {
// Delegate to a single-threaded executor
Executor delegate = Executors.newSingleThreadExecutor();
// Allow all the tasks to be delegated straight away
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
final List<Integer> list = new Vector<Integer>();
final CountDownLatch latch = new CountDownLatch(TASKS);
for (int i = 0; i < TASKS; i++) {
final int result = i;
polite.execute(new Runnable() {
@Override
public void run() {
list.add(result);
latch.countDown();
}
});
}
// Wait for all the tasks to finish
latch.await();
// The tasks should have run in the order they were submitted
assertEquals(ascendingOrder(), list);
}
@Test
public void testQueuedTasksAreDelegatedInOrderOfSubmission()
throws Exception {
// Delegate to a single-threaded executor
Executor delegate = Executors.newSingleThreadExecutor();
// Allow two tasks to be delegated at a time
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 2);
final List<Integer> list = new Vector<Integer>();
final CountDownLatch latch = new CountDownLatch(TASKS);
for (int i = 0; i < TASKS; i++) {
final int result = i;
polite.execute(new Runnable() {
@Override
public void run() {
list.add(result);
latch.countDown();
}
});
}
// Wait for all the tasks to finish
latch.await();
// The tasks should have run in the order they were submitted
assertEquals(ascendingOrder(), list);
}
@Test
public void testTasksRunInParallelOnDelegate() throws Exception {
// Delegate to a multi-threaded executor
Executor delegate = Executors.newCachedThreadPool();
// Allow all the tasks to be delegated straight away
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
final List<Integer> list = new Vector<Integer>();
final CountDownLatch[] latches = new CountDownLatch[TASKS];
for (int i = 0; i < TASKS; i++) latches[i] = new CountDownLatch(1);
for (int i = 0; i < TASKS; i++) {
final int result = i;
polite.execute(new Runnable() {
@Override
public void run() {
try {
// Each task waits for the next task, if any, to finish
if (result < TASKS - 1) latches[result + 1].await();
list.add(result);
} catch (InterruptedException e) {
fail();
}
latches[result].countDown();
}
});
}
// Wait for all the tasks to finish
for (int i = 0; i < TASKS; i++) latches[i].await();
// The tasks should have finished in reverse order
assertEquals(descendingOrder(), list);
}
@Test
public void testTasksDoNotRunInParallelOnDelegate() throws Exception {
// Delegate to a multi-threaded executor
Executor delegate = Executors.newCachedThreadPool();
// Allow one task to be delegated at a time
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 1);
final List<Integer> list = new Vector<Integer>();
final CountDownLatch latch = new CountDownLatch(TASKS);
for (int i = 0; i < TASKS; i++) {
final int result = i;
polite.execute(new Runnable() {
@Override
public void run() {
try {
// Each task runs faster than the previous task
Thread.sleep(TASKS - result);
list.add(result);
} catch (InterruptedException e) {
fail();
}
latch.countDown();
}
});
}
// Wait for all the tasks to finish
latch.await();
// The tasks should have finished in the order they were submitted
assertEquals(ascendingOrder(), list);
}
private List<Integer> ascendingOrder() {
Integer[] array = new Integer[TASKS];
for (int i = 0; i < TASKS; i++) array[i] = i;
return Arrays.asList(array);
}
private List<Integer> descendingOrder() {
Integer[] array = new Integer[TASKS];
for (int i = 0; i < TASKS; i++) array[i] = TASKS - 1 - i;
return Arrays.asList(array);
}
}

View File

@@ -45,7 +45,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
byte[] seed = new byte[32];
new SecureRandom().nextBytes(seed);
// Montgomery ladder multiplier
SecureRandom random = new FortunaSecureRandom(seed);
SecureRandom random = new PseudoSecureRandom(seed);
ECKeyGenerationParameters montgomeryGeneratorParams =
new ECKeyGenerationParameters(PARAMETERS, random);
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
@@ -63,7 +63,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
ECPublicKeyParameters montgomeryPublic2 =
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
// Default multiplier
random = new FortunaSecureRandom(seed);
random = new PseudoSecureRandom(seed);
ECKeyGenerationParameters defaultGeneratorParams =
new ECKeyGenerationParameters(defaultParameters, random);
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();

View File

@@ -1,99 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.engines.AESLightEngine;
import org.spongycastle.crypto.params.KeyParameter;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class FortunaGeneratorTest extends BrambleTestCase {
@Test
public void testCounterInitialisedToOne() {
FortunaGenerator f = new FortunaGenerator(new byte[32]);
// The counter is little-endian
byte[] expected = new byte[16];
expected[0] = 1;
assertArrayEquals(expected, f.getCounter());
}
@Test
public void testIncrementCounter() {
FortunaGenerator f = new FortunaGenerator(new byte[32]);
// Increment the counter until it reaches 255
for (int i = 1; i < 255; i++) f.incrementCounter();
byte[] expected = new byte[16];
expected[0] = (byte) 255;
assertArrayEquals(expected, f.getCounter());
// Increment the counter again - it should carry into the next byte
f.incrementCounter();
expected[0] = 0;
expected[1] = 1;
assertArrayEquals(expected, f.getCounter());
// Increment the counter until it carries into the next byte
for (int i = 256; i < 65536; i++) f.incrementCounter();
expected[0] = 0;
expected[1] = 0;
expected[2] = 1;
assertArrayEquals(expected, f.getCounter());
}
@Test
public void testNextBytes() {
// Generate several outputs with the same seed - they should all match
byte[] seed = new byte[32];
byte[] out1 = new byte[48];
new FortunaGenerator(seed).nextBytes(out1, 0, 48);
// One byte longer than a block, with an offset of one
byte[] out2 = new byte[49];
new FortunaGenerator(seed).nextBytes(out2, 1, 48);
for (int i = 0; i < 48; i++) assertEquals(out1[i], out2[i + 1]);
// One byte shorter than a block
byte[] out3 = new byte[47];
new FortunaGenerator(seed).nextBytes(out3, 0, 47);
for (int i = 0; i < 47; i++) assertEquals(out1[i], out3[i]);
// Less than a block, with an offset greater than a block
byte[] out4 = new byte[32];
new FortunaGenerator(seed).nextBytes(out4, 17, 15);
for (int i = 0; i < 15; i++) assertEquals(out1[i], out4[i + 17]);
}
@Test
public void testRekeying() {
byte[] seed = new byte[32];
FortunaGenerator f = new FortunaGenerator(seed);
// Generate three blocks of output
byte[] out1 = new byte[48];
f.nextBytes(out1, 0, 48);
// Create another generator with the same seed and generate one block
f = new FortunaGenerator(seed);
byte[] out2 = new byte[16];
f.nextBytes(out2, 0, 16);
// The generator should have rekeyed with the 2nd and 3rd blocks
byte[] expectedKey = new byte[32];
System.arraycopy(out1, 16, expectedKey, 0, 32);
// The generator's counter should have been incremented 3 times
byte[] expectedCounter = new byte[16];
expectedCounter[0] = 4;
// The next expected output block is the counter encrypted with the key
byte[] expectedOutput = new byte[16];
BlockCipher c = new AESLightEngine();
c.init(true, new KeyParameter(expectedKey));
c.processBlock(expectedCounter, 0, expectedOutput, 0);
// Check that the generator produces the expected output block
byte[] out3 = new byte[16];
f.nextBytes(out3, 0, 16);
assertArrayEquals(expectedOutput, out3);
}
@Test
public void testMaximumRequestLength() {
int expectedMax = 1024 * 1024;
byte[] output = new byte[expectedMax + 123];
FortunaGenerator f = new FortunaGenerator(new byte[32]);
assertEquals(expectedMax, f.nextBytes(output, 0, output.length));
}
}

View File

@@ -1,67 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.engines.AESLightEngine;
import org.spongycastle.crypto.params.KeyParameter;
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_1;
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_2;
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_3;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
public class FortunaSecureRandomTest extends BrambleTestCase {
@Test
public void testClassPassesSelfTest() {
assertTrue(FortunaSecureRandom.selfTest());
}
@Test
public void testSelfTestVectorsAreReproducible() {
byte[] key = new byte[32], seed = new byte[32];
byte[] counter = new byte[16], output = new byte[16];
byte[] newKey = new byte[32];
// Calculate the initial key
DoubleDigest digest = new DoubleDigest(new SHA256Digest());
digest.update(key);
digest.update(seed);
digest.digest(key, 0, 32);
// Calculate the first output block and the new key
BlockCipher c = new AESLightEngine();
c.init(true, new KeyParameter(key));
counter[0] = 1;
c.processBlock(counter, 0, output, 0);
counter[0] = 2;
c.processBlock(counter, 0, newKey, 0);
counter[0] = 3;
c.processBlock(counter, 0, newKey, 16);
System.arraycopy(newKey, 0, key, 0, 32);
// The first self-test vector should match the first output block
assertArrayEquals(SELF_TEST_VECTOR_1, output);
// Calculate the second output block and the new key before reseeding
c.init(true, new KeyParameter(key));
counter[0] = 4;
c.processBlock(counter, 0, output, 0);
counter[0] = 5;
c.processBlock(counter, 0, newKey, 0);
counter[0] = 6;
c.processBlock(counter, 0, newKey, 16);
System.arraycopy(newKey, 0, key, 0, 32);
// The second self-test vector should match the second output block
assertArrayEquals(SELF_TEST_VECTOR_2, output);
// Calculate the new key after reseeding
digest.update(key);
digest.update(seed);
digest.digest(key, 0, 32);
// Calculate the third output block
c.init(true, new KeyParameter(key));
counter[0] = 8;
c.processBlock(counter, 0, output, 0);
// The third self-test vector should match the third output block
assertArrayEquals(SELF_TEST_VECTOR_3, output);
}
}

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
@@ -21,7 +21,7 @@ public class HashTest extends BrambleTestCase {
private final byte[] inputBytes2 = new byte[0];
public HashTest() {
crypto = new CryptoComponentImpl(new TestSeedProvider());
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
}
@Test

View File

@@ -3,9 +3,9 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
@@ -14,8 +14,9 @@ public class KeyAgreementTest extends BrambleTestCase {
@Test
public void testDeriveMasterSecret() throws Exception {
SeedProvider seedProvider = new TestSeedProvider();
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
SecureRandomProvider
secureRandomProvider = new TestSecureRandomProvider();
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
KeyPair aPair = crypto.generateAgreementKeyPair();
byte[] aPub = aPair.getPublic().getEncoded();
KeyPair bPair = crypto.generateAgreementKeyPair();
@@ -27,8 +28,9 @@ public class KeyAgreementTest extends BrambleTestCase {
@Test
public void testDeriveSharedSecret() throws Exception {
SeedProvider seedProvider = new TestSeedProvider();
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
SecureRandomProvider
secureRandomProvider = new TestSecureRandomProvider();
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
KeyPair aPair = crypto.generateAgreementKeyPair();
byte[] aPub = aPair.getPublic().getEncoded();
KeyPair bPair = crypto.generateAgreementKeyPair();

View File

@@ -1,20 +1,24 @@
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.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class KeyDerivationTest extends BrambleTestCase {
@@ -23,7 +27,7 @@ public class KeyDerivationTest extends BrambleTestCase {
private final SecretKey master;
public KeyDerivationTest() {
crypto = new CryptoComponentImpl(new TestSeedProvider());
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
master = TestUtils.getSecretKey();
}
@@ -156,11 +160,7 @@ public class KeyDerivationTest extends BrambleTestCase {
}
private void assertAllDifferent(List<SecretKey> keys) {
for (SecretKey ki : keys) {
for (SecretKey kj : keys) {
if (ki == kj) assertArrayEquals(ki.getBytes(), kj.getBytes());
else assertFalse(Arrays.equals(ki.getBytes(), kj.getBytes()));
}
}
Set<Bytes> set = new HashSet<Bytes>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
public class KeyEncodingAndParsingTest extends BrambleTestCase {
private final CryptoComponentImpl crypto =
new CryptoComponentImpl(new TestSeedProvider());
new CryptoComponentImpl(new TestSecureRandomProvider());
@Test
public void testAgreementPublicKeyLength() throws Exception {

View File

@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
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.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
@@ -22,7 +22,7 @@ public class MacTest extends BrambleTestCase {
private final byte[] inputBytes2 = new byte[0];
public MacTest() {
crypto = new CryptoComponentImpl(new TestSeedProvider());
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
}
@Test

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
public class PasswordBasedKdfTest extends BrambleTestCase {
private final CryptoComponentImpl crypto =
new CryptoComponentImpl(new TestSeedProvider());
new CryptoComponentImpl(new TestSecureRandomProvider());
@Test
public void testEncryptionAndDecryption() {

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
import static org.junit.Assert.assertTrue;
@@ -12,7 +13,7 @@ public class PasswordStrengthEstimatorImplTest extends BrambleTestCase {
@Test
public void testWeakPasswords() {
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
assertTrue(e.estimateStrength("") < QUITE_STRONG);
assertTrue(e.estimateStrength("") == NONE);
assertTrue(e.estimateStrength("password") < QUITE_STRONG);
assertTrue(e.estimateStrength("letmein") < QUITE_STRONG);
assertTrue(e.estimateStrength("123456") < QUITE_STRONG);

View File

@@ -0,0 +1,48 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
class PseudoSecureRandom extends SecureRandom {
private static final Provider PROVIDER = new PseudoSecureRandomProvider();
PseudoSecureRandom(byte[] seed) {
super(new PseudoSecureRandomSpi(seed), PROVIDER);
}
private static class PseudoSecureRandomSpi extends SecureRandomSpi {
private final PseudoRandom pseudoRandom;
private PseudoSecureRandomSpi(byte[] seed) {
pseudoRandom = new PseudoRandomImpl(seed);
}
@Override
protected byte[] engineGenerateSeed(int length) {
return pseudoRandom.nextBytes(length);
}
@Override
protected void engineNextBytes(byte[] b) {
byte[] random = pseudoRandom.nextBytes(b.length);
System.arraycopy(random, 0, b, 0, b.length);
}
@Override
protected void engineSetSeed(byte[] seed) {
// Thank you for your input
}
}
private static class PseudoSecureRandomProvider extends Provider {
private PseudoSecureRandomProvider() {
super("PseudoSecureRandom", 1.0, "Only for testing");
}
}
}

View File

@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSeedProvider;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
@@ -22,7 +22,7 @@ public class SignatureTest extends BrambleTestCase {
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
public SignatureTest() {
crypto = new CryptoComponentImpl(new TestSeedProvider());
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
KeyPair k = crypto.generateSignatureKeyPair();
publicKey = k.getPublic().getEncoded();
privateKey = k.getPrivate().getEncoded();

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.MAC_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.junit.Assert.assertArrayEquals;
@@ -22,7 +23,8 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher;
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 long streamNumber = 1234;
@@ -30,7 +32,12 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
cipher = new TestAuthenticatedCipher(); // Null cipher
streamHeaderKey = 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);
}
@@ -47,7 +54,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv);
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -76,6 +85,85 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
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)
public void testTruncatedFrameThrowsException() throws Exception {
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -83,7 +171,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv);
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -111,7 +201,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload = TestUtils.getRandomBytes(payloadLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv);
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -138,7 +230,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
padding[paddingLength - 1] = 1;
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv);
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -162,7 +256,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv);
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);

View File

@@ -3,21 +3,28 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
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.STREAM_HEADER_IV_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.PROTOCOL_VERSION;
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.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class StreamEncrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher;
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 int payloadLength = 123, paddingLength = 234;
@@ -26,22 +33,87 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey();
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);
}
@Test(expected = IllegalArgumentException.class)
public void testRejectsNegativePayloadLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, -1, 0, false);
}
@Test(expected = IllegalArgumentException.class)
public void testRejectsNegativePaddingLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, 0, -1, false);
}
@Test(expected = IllegalArgumentException.class)
public void testRejectsMaxPayloadPlusPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
}
@Test
public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
assertEquals(TAG_LENGTH + STREAM_HEADER_LENGTH + MAX_FRAME_LENGTH,
out.size());
}
@Test
public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
assertEquals(TAG_LENGTH + STREAM_HEADER_LENGTH + MAX_FRAME_LENGTH,
out.size());
}
@Test
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, false);
// Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -57,14 +129,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, true);
// Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -80,13 +155,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, false);
// Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -102,13 +180,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, true);
// Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -124,14 +205,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -149,14 +233,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -174,13 +261,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -198,13 +288,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -222,7 +315,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteTwoFramesWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
int payloadLength1 = 345, paddingLength1 = 456;
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
@@ -233,7 +327,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// MAC, second frame header, payload, padding, MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -259,7 +355,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream once
s.flush();
@@ -267,7 +364,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
@@ -279,7 +378,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream twice
s.flush();
@@ -288,7 +388,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
@@ -299,14 +401,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testFlushDoesNotWriteTagIfNull() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream once
s.flush();
// Expect the stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes());
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

@@ -1,14 +0,0 @@
package org.briarproject.bramble.properties;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import static org.junit.Assert.fail;
public class TransportPropertyManagerImplTest extends BrambleTestCase {
@Test
public void testUnitTestsExist() {
fail(); // FIXME: Write tests
}
}

View File

@@ -34,6 +34,7 @@ import java.util.Collection;
import javax.inject.Inject;
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.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -115,7 +116,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
private void read(byte[] connectionData) throws Exception {
// Calculate the expected tag
byte[] expectedTag = new byte[TAG_LENGTH];
crypto.encodeTag(expectedTag, tagKey, streamNumber);
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
// Read the tag
InputStream in = new ByteArrayInputStream(connectionData);

View File

@@ -19,12 +19,12 @@ import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.sync.ValidationManager.MessageValidator;
import org.briarproject.bramble.api.sync.ValidationManager.State;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
@@ -38,8 +38,18 @@ import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
public class ValidationManagerImplTest extends BrambleTestCase {
public class ValidationManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final MessageFactory messageFactory =
context.mock(MessageFactory.class);
private final MessageValidator validator =
context.mock(MessageValidator.class);
private final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
private final Executor dbExecutor = new ImmediateExecutor();
private final Executor validationExecutor = new ImmediateExecutor();
private final ClientId clientId =
new ClientId(TestUtils.getRandomString(5));
private final MessageId messageId = new MessageId(TestUtils.getRandomId());
@@ -63,23 +73,58 @@ public class ValidationManagerImplTest extends BrambleTestCase {
private final MessageContext validResultWithDependencies =
new MessageContext(metadata, Collections.singletonList(messageId1));
private ValidationManagerImpl vm;
public ValidationManagerImplTest() {
// Encode the messages
System.arraycopy(groupId.getBytes(), 0, raw, 0, UniqueId.LENGTH);
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
}
@Before
public void setUp() {
vm = new ValidationManagerImpl(db, dbExecutor, validationExecutor,
messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
}
@Test
public void testStartAndStop() throws Exception {
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, true);
context.checking(new Expectations() {{
// validateOutstandingMessages()
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Collections.emptyList()));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
// deliverOutstandingMessages()
oneOf(db).startTransaction(true);
will(returnValue(txn1));
oneOf(db).getPendingMessages(txn1, clientId);
will(returnValue(Collections.emptyList()));
oneOf(db).commitTransaction(txn1);
oneOf(db).endTransaction(txn1);
// shareOutstandingMessages()
oneOf(db).startTransaction(true);
will(returnValue(txn2));
oneOf(db).getMessagesToShare(txn2, clientId);
will(returnValue(Collections.emptyList()));
oneOf(db).commitTransaction(txn2);
oneOf(db).endTransaction(txn2);
}});
vm.startService();
vm.stopService();
}
@Test
public void testMessagesAreValidatedAtStartup() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, false);
@@ -87,6 +132,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
final Transaction txn4 = new Transaction(null, false);
final Transaction txn5 = new Transaction(null, true);
final Transaction txn6 = new Transaction(null, true);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction(true);
@@ -165,26 +211,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn6);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.startService();
context.assertIsSatisfied();
}
@Test
public void testPendingMessagesAreDeliveredAtStartup() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, false);
@@ -266,26 +297,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn4);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.startService();
context.assertIsSatisfied();
}
@Test
public void testMessagesAreSharedAtStartup() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, true);
@@ -333,29 +349,15 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn4);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.startService();
context.assertIsSatisfied();
}
@Test
public void testIncomingMessagesAreShared() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
final Transaction txn2 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -396,33 +398,19 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn2);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testValidationContinuesAfterNoSuchMessageException()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null, false);
final Transaction txn4 = new Transaction(null, true);
final Transaction txn5 = new Transaction(null, true);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction(true);
@@ -481,33 +469,19 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn5);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.startService();
context.assertIsSatisfied();
}
@Test
public void testValidationContinuesAfterNoSuchGroupException()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, true);
final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null, false);
final Transaction txn4 = new Transaction(null, true);
final Transaction txn5 = new Transaction(null, true);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction(true);
@@ -571,28 +545,14 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn5);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.startService();
context.assertIsSatisfied();
}
@Test
public void testNonLocalMessagesAreValidatedWhenAdded() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -619,51 +579,20 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn1);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testLocalMessagesAreNotValidatedWhenAdded() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, null));
context.assertIsSatisfied();
}
@Test
public void testMessagesWithUndeliveredDependenciesArePending()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -688,29 +617,15 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn1);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testMessagesWithDeliveredDependenciesGetDelivered()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -741,30 +656,16 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn1);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testMessagesWithInvalidDependenciesAreInvalid()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
final Transaction txn2 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -809,26 +710,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn2);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testRecursiveInvalidation() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final MessageId messageId3 = new MessageId(TestUtils.getRandomId());
final MessageId messageId4 = new MessageId(TestUtils.getRandomId());
final Map<MessageId, State> twoDependents =
@@ -842,6 +728,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
final Transaction txn4 = new Transaction(null, false);
final Transaction txn5 = new Transaction(null, false);
final Transaction txn6 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -927,26 +814,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn6);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testPendingDependentsGetDelivered() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final MessageId messageId3 = new MessageId(TestUtils.getRandomId());
final MessageId messageId4 = new MessageId(TestUtils.getRandomId());
final Message message3 = new Message(messageId3, groupId, timestamp,
@@ -968,6 +840,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
final Transaction txn4 = new Transaction(null, false);
final Transaction txn5 = new Transaction(null, false);
final Transaction txn6 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -1100,26 +973,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn6);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testOnlyReadyPendingDependentsGetDelivered() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Map<MessageId, State> twoDependencies =
new LinkedHashMap<MessageId, State>();
twoDependencies.put(messageId, DELIVERED);
@@ -1127,6 +985,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
final Transaction txn2 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
@@ -1162,86 +1021,6 @@ public class ValidationManagerImplTest extends BrambleTestCase {
oneOf(db).endTransaction(txn2);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testMessageDependencyCycle() throws Exception {
final MessageContext cycleContext = new MessageContext(metadata,
Collections.singletonList(messageId));
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageFactory messageFactory =
context.mock(MessageFactory.class);
final MessageValidator validator = context.mock(MessageValidator.class);
final IncomingMessageHook hook =
context.mock(IncomingMessageHook.class);
final Transaction txn = new Transaction(null, true);
final Transaction txn1 = new Transaction(null, false);
final Transaction txn2 = new Transaction(null, true);
final Transaction txn3 = new Transaction(null, false);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getGroup(txn, groupId);
will(returnValue(group));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
// Validate the message: valid
oneOf(validator).validateMessage(message, group);
will(returnValue(validResultWithDependencies));
// Store the validation result
oneOf(db).startTransaction(false);
will(returnValue(txn1));
oneOf(db).addMessageDependencies(txn1, message,
validResultWithDependencies.getDependencies());
oneOf(db).getMessageDependencies(txn1, messageId);
will(returnValue(Collections.singletonMap(messageId1, UNKNOWN)));
oneOf(db).mergeMessageMetadata(txn1, messageId, metadata);
oneOf(db).setMessageState(txn1, messageId, PENDING);
oneOf(db).commitTransaction(txn1);
oneOf(db).endTransaction(txn1);
// Second message is coming in
oneOf(db).startTransaction(true);
will(returnValue(txn2));
oneOf(db).getGroup(txn2, groupId);
will(returnValue(group));
oneOf(db).commitTransaction(txn2);
oneOf(db).endTransaction(txn2);
// Validate the message: valid
oneOf(validator).validateMessage(message1, group);
will(returnValue(cycleContext));
// Store the validation result
oneOf(db).startTransaction(false);
will(returnValue(txn3));
oneOf(db).addMessageDependencies(txn3, message1,
cycleContext.getDependencies());
oneOf(db).getMessageDependencies(txn3, messageId1);
will(returnValue(Collections.singletonMap(messageId, PENDING)));
oneOf(db).mergeMessageMetadata(txn3, messageId1, metadata);
oneOf(db).setMessageState(txn3, messageId1, PENDING);
oneOf(db).commitTransaction(txn3);
oneOf(db).endTransaction(txn3);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor, messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
vm.eventOccurred(new MessageAddedEvent(message1, contactId));
context.assertIsSatisfied();
}
}

View File

@@ -0,0 +1,57 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.security.Provider;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class LinuxSecureRandomProviderTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory();
@Before
public void setUp() {
testDir.mkdirs();
}
@Test
public void testGetProviderWritesToRandomDeviceOnFirstCall()
throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the provider's output to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length());
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(urandom);
// Getting a provider should write entropy to the file
Provider provider = p.getProvider();
assertNotNull(provider);
assertEquals("LinuxPRNG", provider.getName());
// There should be at least 16 bytes from the clock, 8 from the runtime
long length = urandom.length();
assertTrue(length >= 24);
// Getting another provider should not write to the file again
provider = p.getProvider();
assertNotNull(provider);
assertEquals("LinuxPRNG", provider.getName());
assertEquals(length, urandom.length());
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
}

View File

@@ -0,0 +1,128 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class LinuxSecureRandomSpiTest extends BrambleTestCase {
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private static final int SEED_BYTES = 32;
private final File testDir = TestUtils.getTestDirectory();
@Before
public void setUp() {
testDir.mkdirs();
}
@Test
public void testSeedsAreDistinct() {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
Set<Bytes> seeds = new HashSet<Bytes>();
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi();
for (int i = 0; i < 1000; i++) {
byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
assertEquals(SEED_BYTES, seed.length);
assertTrue(seeds.add(new Bytes(seed)));
}
}
@Test
public void testEngineSetSeedWritesToRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the engine's output to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length());
// Generate a seed
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
// Check that the engine writes the seed to the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE,
urandom);
engine.engineSetSeed(seed);
assertEquals(SEED_BYTES, urandom.length());
byte[] written = new byte[SEED_BYTES];
FileInputStream in = new FileInputStream(urandom);
IoUtils.read(in, written);
in.close();
assertArrayEquals(seed, written);
}
@Test
public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate some entropy
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
// Write the entropy to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
FileOutputStream out = new FileOutputStream(urandom);
out.write(entropy);
out.flush();
out.close();
assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length());
// Check that the engine reads from the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
RANDOM_DEVICE);
byte[] b = new byte[SEED_BYTES];
engine.engineNextBytes(b);
assertArrayEquals(entropy, b);
}
@Test
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate some entropy
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
// Write the entropy to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
FileOutputStream out = new FileOutputStream(urandom);
out.write(entropy);
out.flush();
out.close();
assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length());
// Check that the engine reads from the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
RANDOM_DEVICE);
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
assertArrayEquals(entropy, b);
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
}

View File

@@ -1,90 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.util.HashSet;
import java.util.Set;
import static org.briarproject.bramble.api.system.SeedProvider.SEED_BYTES;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class LinuxSeedProviderTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory();
@Before
public void setUp() {
testDir.mkdirs();
}
@Test
public void testSeedAppearsSane() {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
Set<Bytes> seeds = new HashSet<Bytes>();
LinuxSeedProvider p = new LinuxSeedProvider();
for (int i = 0; i < 1000; i++) {
byte[] seed = p.getSeed();
assertEquals(SEED_BYTES, seed.length);
assertTrue(seeds.add(new Bytes(seed)));
}
}
@Test
public void testEntropyIsWrittenToPool() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the provider's entropy to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length());
String path = urandom.getAbsolutePath();
LinuxSeedProvider p = new LinuxSeedProvider(path, "/dev/urandom");
p.getSeed();
// There should be 16 bytes from the clock, plus network interfaces
assertTrue(urandom.length() > 20);
}
@Test
public void testSeedIsReadFromPool() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate a seed
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
// Write the seed to a file
File urandom = new File(testDir, "urandom");
urandom.delete();
FileOutputStream out = new FileOutputStream(urandom);
out.write(seed);
out.flush();
out.close();
assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length());
// Check that the provider reads the seed from the file
String path = urandom.getAbsolutePath();
LinuxSeedProvider p = new LinuxSeedProvider("/dev/urandom", path);
assertArrayEquals(seed, p.getSeed());
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.security.Provider;
@NotNullByDefault
public class TestSecureRandomProvider implements SecureRandomProvider {
@Override
public Provider getProvider() {
// Use the default provider
return null;
}
}

View File

@@ -1,13 +0,0 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SeedProvider;
@NotNullByDefault
public class TestSeedProvider implements SeedProvider {
@Override
public byte[] getSeed() {
return TestUtils.getRandomBytes(32);
}
}

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import javax.inject.Singleton;
@@ -12,7 +12,7 @@ public class TestSeedProviderModule {
@Provides
@Singleton
SeedProvider provideSeedProvider() {
return new TestSeedProvider();
SecureRandomProvider provideSeedProvider() {
return new TestSecureRandomProvider();
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.test;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class TestSocksModule {
@Provides
SocketFactory provideSocketFactory() {
return SocketFactory.getDefault();
}
}

View File

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

View File

@@ -7,10 +7,19 @@ apply plugin: 'witness'
dependencies {
compile project(':bramble-core')
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')
}
dependencyVerification {
verify = [
'net.java.dev.jna:jna:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
'net.java.dev.jna:jna-platform:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
]
}
tasks.withType(Test) {
systemProperty 'java.library.path', 'libs'
}

Binary file not shown.

View File

@@ -0,0 +1,19 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.OsUtils;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class DesktopSecureRandomModule {
@Provides
@Singleton
SecureRandomProvider provideSecureRandomProvider() {
return OsUtils.isLinux() ? new LinuxSecureRandomProvider() : null;
}
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.system.SeedProvider;
import org.briarproject.bramble.util.OsUtils;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class DesktopSeedProviderModule {
@Provides
@Singleton
SeedProvider provideSeedProvider() {
return OsUtils.isLinux() ? new LinuxSeedProvider() : null;
}
}

View File

@@ -5,13 +5,10 @@ dependencies {
def supportVersion = '23.2.1'
compile project(':briar-core')
compile project(':bramble-android')
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
}
@@ -20,7 +17,7 @@ dependencies {
exclude module: 'recyclerview-v7'
}
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') {
exclude module: 'support-v4'
exclude module: 'support-annotations'
@@ -28,15 +25,16 @@ dependencies {
compile 'info.guardianproject.panic:panic:0.5'
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.google.zxing:core:3.2.1'
provided 'javax.annotation:jsr250-api:1.0'
compile 'com.google.zxing:core:3.3.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0'
compile 'com.github.bumptech.glide:glide:3.8.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 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.mockito:mockito-core:2.8.9'
}
dependencyVerification {
@@ -45,20 +43,18 @@ dependencyVerification {
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'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:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'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:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
'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,7 +78,10 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
resValue "string", "app_package", "org.briarproject.briar"
versionCode 1601
versionName "0.16.1"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
}
@@ -107,5 +106,6 @@ android {
lintOptions {
warning 'MissingTranslation'
warning 'ImpliedQuantity'
warning 'ExtraTranslation'
}
}

View File

@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="org.briarproject.briar"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="13"
android:versionName="0.13">
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.bluetooth"/>
<uses-feature android:name="android.hardware.camera" />
@@ -21,15 +19,15 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name=".android.BriarApplicationImpl"
android:name="org.briarproject.briar.android.BriarApplicationImpl"
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher_round"
android:theme="@style/BriarTheme">
<service
android:name=".android.BriarService"
android:name="org.briarproject.briar.android.BriarService"
android:exported="false">
<intent-filter>
<action android:name="org.briarproject.briar.android.BriarService"/>
@@ -37,7 +35,7 @@
</service>
<activity
android:name=".android.reporting.DevReportActivity"
android:name="org.briarproject.briar.android.reporting.DevReportActivity"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="true"
@@ -49,24 +47,24 @@
</activity>
<activity
android:name=".android.splash.ExpiredActivity"
android:name="org.briarproject.briar.android.splash.ExpiredActivity"
android:label="@string/app_name">
</activity>
<activity
android:name=".android.login.PasswordActivity"
android:name="org.briarproject.briar.android.login.PasswordActivity"
android:label="@string/app_name"
android:windowSoftInputMode="stateVisible">
</activity>
<activity
android:name=".android.login.SetupActivity"
android:name="org.briarproject.briar.android.login.SetupActivity"
android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".android.splash.SplashScreenActivity"
android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:label="@string/app_name">
<intent-filter>
@@ -76,267 +74,268 @@
</activity>
<activity
android:name=".android.navdrawer.NavDrawerActivity"
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:launchMode="singleTop">
</activity>
<activity
android:name=".android.contact.ConversationActivity"
android:name="org.briarproject.briar.android.contact.ConversationActivity"
android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</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:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.privategroup.conversation.GroupActivity"
android:name="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:label="@string/app_name"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.privategroup.invitation.GroupInvitationActivity"
android:name="org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity"
android:label="@string/groups_invitations_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/>
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name=".android.privategroup.memberlist.GroupMemberListActivity"
android:name="org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity"
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">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name=".android.privategroup.reveal.RevealContactsActivity"
android:name="org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity"
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">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name=".android.privategroup.creation.GroupInviteActivity"
android:name="org.briarproject.briar.android.privategroup.creation.GroupInviteActivity"
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">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"/>
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/>
</activity>
<activity
android:name=".android.sharing.ForumInvitationActivity"
android:name="org.briarproject.briar.android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.sharing.BlogInvitationActivity"
android:name="org.briarproject.briar.android.sharing.BlogInvitationActivity"
android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName=".android.contact.ConversationActivity">
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity"
android:value="org.briarproject.briar.android.contact.ConversationActivity"
/>
</activity>
<activity
android:name=".android.forum.CreateForumActivity"
android:name="org.briarproject.briar.android.forum.CreateForumActivity"
android:label="@string/create_forum_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible">
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.forum.ForumActivity"
android:name="org.briarproject.briar.android.forum.ForumActivity"
android:label="@string/app_name"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.sharing.ShareForumActivity"
android:name="org.briarproject.briar.android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.forum.ForumActivity"
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.forum.ForumActivity"
android:value="org.briarproject.briar.android.forum.ForumActivity"
/>
</activity>
<activity
android:name=".android.sharing.ShareBlogActivity"
android:name="org.briarproject.briar.android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.blog.BlogActivity"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity"
android:value="org.briarproject.briar.android.blog.BlogActivity"
/>
</activity>
<activity
android:name=".android.sharing.ForumSharingStatusActivity"
android:name="org.briarproject.briar.android.sharing.ForumSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.forum.ForumActivity">
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.forum.ForumActivity"
android:value="org.briarproject.briar.android.forum.ForumActivity"
/>
</activity>
<activity
android:name=".android.sharing.BlogSharingStatusActivity"
android:name="org.briarproject.briar.android.sharing.BlogSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.blog.BlogActivity">
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity"
android:value="org.briarproject.briar.android.blog.BlogActivity"
/>
</activity>
<activity
android:name=".android.blog.BlogActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:name="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/>
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name=".android.blog.WriteBlogPostActivity"
android:name="org.briarproject.briar.android.blog.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post"
android:parentActivityName=".android.blog.BlogActivity"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity"
android:value="org.briarproject.briar.android.blog.BlogActivity"
/>
</activity>
<activity
android:name=".android.blog.ReblogActivity"
android:name="org.briarproject.briar.android.blog.ReblogActivity"
android:label="@string/blogs_reblog_button"
android:parentActivityName=".android.blog.BlogActivity"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity"
android:value="org.briarproject.briar.android.blog.BlogActivity"
/>
</activity>
<activity
android:name=".android.blog.RssFeedImportActivity"
android:name="org.briarproject.briar.android.blog.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.blog.RssFeedManageActivity"
android:name="org.briarproject.briar.android.blog.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.invitation.AddContactActivity"
android:name="org.briarproject.briar.android.invitation.AddContactActivity"
android:label="@string/add_contact_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.keyagreement.KeyAgreementActivity"
android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/>
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name=".android.introduction.IntroductionActivity"
android:name="org.briarproject.briar.android.introduction.IntroductionActivity"
android:label="@string/introduction_activity_title"
android:parentActivityName=".android.contact.ConversationActivity"
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity"
android:value="org.briarproject.briar.android.contact.ConversationActivity"
/>
</activity>
<activity
android:name=".android.StartupFailureActivity"
android:name="org.briarproject.briar.android.StartupFailureActivity"
android:label="@string/startup_failed_activity_title">
</activity>
<activity
android:name=".android.settings.SettingsActivity"
android:name="org.briarproject.briar.android.settings.SettingsActivity"
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">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
@@ -345,28 +344,27 @@
</activity>
<activity
android:name=".android.login.ChangePasswordActivity"
android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
android:label="@string/change_password"
android:parentActivityName=".android.settings.SettingsActivity">
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.settings.SettingsActivity"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
/>
</activity>
<activity
android:name=".android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting">
<intent-filter>
<action android:name="info.guardianproject.panic.action.CONNECT"/>
<action android:name="info.guardianproject.panic.action.DISCONNECT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
/>
</activity>
<activity
android:name=".android.panic.PanicResponderActivity"
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<!-- this can never have launchMode singleTask or singleInstance! -->
@@ -377,7 +375,7 @@
</activity>
<activity
android:name=".android.panic.ExitActivity"
android:name="org.briarproject.briar.android.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay">
</activity>

View File

@@ -28,9 +28,11 @@ import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.reporting.BriarReportSender;
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.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory;
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.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumSharingManager;
@@ -43,6 +45,8 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
import java.util.concurrent.Executor;
@@ -75,6 +79,8 @@ public interface AndroidComponent
@DatabaseExecutor
Executor databaseExecutor();
MessageTracker messageTracker();
LifecycleManager lifecycleManager();
IdentityManager identityManager();
@@ -87,6 +93,8 @@ public interface AndroidComponent
AndroidNotificationManager androidNotificationManager();
ScreenFilterMonitor screenFilterMonitor();
ConnectionRegistry connectionRegistry();
ContactManager contactManager();
@@ -138,10 +146,14 @@ public interface AndroidComponent
@IoExecutor
Executor ioExecutor();
void inject(BriarService activity);
void inject(BriarService briarService);
void inject(BriarReportSender briarReportSender);
void inject(EmojiProvider emojiProvider);
void inject(RecentEmojiPageModel recentEmojiPageModel);
// Eager singleton load
void inject(AppModule.EagerSingletons init);
}

View File

@@ -2,11 +2,8 @@ package org.briarproject.briar.android;
import android.app.Application;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.UiThread;
@@ -62,9 +59,9 @@ import static android.app.Notification.DEFAULT_SOUND;
import static android.app.Notification.DEFAULT_VIBRATE;
import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
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.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@@ -73,8 +70,6 @@ import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_CONTACTS;
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_FORUMS;
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_GROUPS;
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_NOTIFY_BLOG;
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_NOTIFY_GROUP;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
@ThreadSafe
@@ -92,25 +87,13 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
// Content URIs to differentiate between pending intents
private static final String CONTACT_URI =
"content://org.briarproject/contact";
"content://org.briarproject.briar/contact";
private static final String GROUP_URI =
"content://org.briarproject/group";
"content://org.briarproject.briar/group";
private static final String FORUM_URI =
"content://org.briarproject/forum";
"content://org.briarproject.briar/forum";
private static final String BLOG_URI =
"content://org.briarproject/blog";
// Actions for intents that are broadcast when notifications are dismissed
private static final String CLEAR_PRIVATE_MESSAGE_ACTION =
"org.briarproject.briar.CLEAR_PRIVATE_MESSAGE_NOTIFICATION";
private static final String CLEAR_GROUP_ACTION =
"org.briarproject.briar.CLEAR_GROUP_NOTIFICATION";
private static final String CLEAR_FORUM_ACTION =
"org.briarproject.briar.CLEAR_FORUM_NOTIFICATION";
private static final String CLEAR_BLOG_ACTION =
"org.briarproject.briar.CLEAR_BLOG_NOTIFICATION";
private static final String CLEAR_INTRODUCTION_ACTION =
"org.briarproject.briar.CLEAR_INTRODUCTION_NOTIFICATION";
"content://org.briarproject.briar/blog";
private static final Logger LOG =
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
@@ -119,7 +102,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private final SettingsManager settingsManager;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final BroadcastReceiver receiver = new DeleteIntentReceiver();
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the main UI thread
@@ -157,30 +139,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} catch (DbException e) {
throw new ServiceException(e);
}
// Register a broadcast receiver for notifications being dismissed
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
IntentFilter filter = new IntentFilter();
filter.addAction(CLEAR_PRIVATE_MESSAGE_ACTION);
filter.addAction(CLEAR_GROUP_ACTION);
filter.addAction(CLEAR_FORUM_ACTION);
filter.addAction(CLEAR_BLOG_ACTION);
filter.addAction(CLEAR_INTRODUCTION_ACTION);
appContext.registerReceiver(receiver, filter);
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
// Clear all notifications and unregister the broadcast receiver
// Clear all notifications
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
@@ -189,7 +152,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
clearForumPostNotification();
clearBlogPostNotification();
clearIntroductionSuccessNotification();
appContext.unregisterReceiver(receiver);
return null;
}
});
@@ -325,34 +287,30 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private void updateContactNotification() {
if (contactTotal == 0) {
clearContactNotification();
} else if (settings.getBoolean("notifyPrivateMessages", true)) {
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_private_message);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.private_message_notification_text, contactTotal,
contactTotal));
boolean sound = settings.getBoolean("notifySound", true);
String ringtoneUri = settings.get("notifyRingtoneUri");
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());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Clear the counters if the notification is dismissed
Intent clear = new Intent(CLEAR_PRIVATE_MESSAGE_ACTION);
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
clear, 0);
b.setDeleteIntent(delete);
if (contactCounts.size() == 1) {
// Touching the notification shows the relevant conversation
Intent i = new Intent(appContext, ConversationActivity.class);
ContactId c = contactCounts.keySet().iterator().next();
i.putExtra(CONTACT_ID, c.getInt());
i.setData(Uri.parse(CONTACT_URI + "/" + c.getInt()));
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(ConversationActivity.class);
t.addNextIntent(i);
@@ -361,7 +319,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
// Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i.setData(Uri.parse(CONTACT_URI));
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(NavDrawerActivity.class);
@@ -370,7 +328,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
b.setVisibility(VISIBILITY_SECRET);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -381,26 +344,15 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
@UiThread
private int getDefaults() {
int defaults = DEFAULT_LIGHTS;
boolean sound = settings.getBoolean("notifySound", true);
String ringtoneUri = settings.get("notifyRingtoneUri");
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (sound && StringUtils.isNullOrEmpty(ringtoneUri))
defaults |= DEFAULT_SOUND;
if (settings.getBoolean("notifyVibration", true))
if (settings.getBoolean(PREF_NOTIFY_VIBRATION, true))
defaults |= DEFAULT_VIBRATE;
return defaults;
}
@Override
public void clearAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearContactNotification();
clearIntroductionSuccessNotification();
}
});
}
@UiThread
private void showGroupMessageNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -438,22 +390,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_private_group);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.group_message_notification_text, groupTotal,
groupTotal));
String ringtoneUri = settings.get("notifyRingtoneUri");
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Clear the counters if the notification is dismissed
Intent clear = new Intent(CLEAR_GROUP_ACTION);
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
clear, 0);
b.setDeleteIntent(delete);
if (groupCounts.size() == 1) {
// Touching the notification shows the relevant group
Intent i = new Intent(appContext, GroupActivity.class);
@@ -461,7 +409,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
i.putExtra(GROUP_ID, g.getBytes());
String idHex = StringUtils.toHexString(g.getBytes());
i.setData(Uri.parse(GROUP_URI + "/" + idHex));
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(GroupActivity.class);
t.addNextIntent(i);
@@ -470,7 +418,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
// Touching the notification shows the group list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_GROUPS, true);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i.setData(Uri.parse(GROUP_URI));
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(NavDrawerActivity.class);
@@ -479,7 +427,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -487,16 +440,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
}
@Override
public void clearAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearGroupMessageNotification();
}
});
}
@UiThread
private void showForumPostNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -530,26 +473,22 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private void updateForumPostNotification() {
if (forumTotal == 0) {
clearForumPostNotification();
} else if (settings.getBoolean("notifyForumPosts", true)) {
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_forum);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.forum_post_notification_text, forumTotal,
forumTotal));
String ringtoneUri = settings.get("notifyRingtoneUri");
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Clear the counters if the notification is dismissed
Intent clear = new Intent(CLEAR_FORUM_ACTION);
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
clear, 0);
b.setDeleteIntent(delete);
if (forumCounts.size() == 1) {
// Touching the notification shows the relevant forum
Intent i = new Intent(appContext, ForumActivity.class);
@@ -557,7 +496,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
i.putExtra(GROUP_ID, g.getBytes());
String idHex = StringUtils.toHexString(g.getBytes());
i.setData(Uri.parse(FORUM_URI + "/" + idHex));
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(ForumActivity.class);
t.addNextIntent(i);
@@ -566,7 +505,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
// Touching the notification shows the forum list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_FORUMS, true);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i.setData(Uri.parse(FORUM_URI));
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(NavDrawerActivity.class);
@@ -575,7 +514,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -583,16 +527,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
}
@Override
public void clearAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearForumPostNotification();
}
});
}
@UiThread
private void showBlogPostNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -630,26 +564,22 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
b.setSmallIcon(R.drawable.notification_blog);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.blog_post_notification_text, blogTotal,
blogTotal));
String ringtoneUri = settings.get("notifyRingtoneUri");
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Clear the counters if the notification is dismissed
Intent clear = new Intent(CLEAR_BLOG_ACTION);
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
clear, 0);
b.setDeleteIntent(delete);
// Touching the notification shows the combined blog feed
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_BLOGS, true);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i.setData(Uri.parse(BLOG_URI));
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(NavDrawerActivity.class);
@@ -657,7 +587,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
b.setVisibility(VISIBILITY_SECRET);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -696,21 +631,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal,
introductionTotal));
String ringtoneUri = settings.get("notifyRingtoneUri");
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
// Clear the counter if the notification is dismissed
Intent clear = new Intent(CLEAR_INTRODUCTION_ACTION);
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
clear, 0);
b.setDeleteIntent(delete);
// Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i.setData(Uri.parse(CONTACT_URI));
TaskStackBuilder t = TaskStackBuilder.create(appContext);
t.addParentStack(NavDrawerActivity.class);
@@ -718,7 +648,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
b.setVisibility(VISIBILITY_SECRET);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -765,68 +700,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
public void blockAllBlogPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@@ -846,28 +719,4 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
});
}
private class DeleteIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
if (CLEAR_PRIVATE_MESSAGE_ACTION.equals(action)) {
clearContactNotification();
} else if (CLEAR_GROUP_ACTION.equals(action)) {
clearGroupMessageNotification();
} else if (CLEAR_FORUM_ACTION.equals(action)) {
clearForumPostNotification();
} else if (CLEAR_BLOG_ACTION.equals(action)) {
clearBlogPostNotification();
} else if (CLEAR_INTRODUCTION_ACTION.equals(action)) {
clearIntroductionSuccessNotification();
}
}
});
}
}
}

View File

@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.ui.UiCallback;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.ReferenceManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.File;
import java.security.GeneralSecurityException;
@@ -166,4 +167,10 @@ public class AppModule {
eventBus.addListener(notificationManager);
return notificationManager;
}
@Provides
ScreenFilterMonitor provideScreenFilterMonitor(
ScreenFilterMonitorImpl screenFilterMonitor) {
return screenFilterMonitor;
}
}

View File

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

View File

@@ -28,7 +28,6 @@ import javax.inject.Inject;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
@@ -83,8 +82,7 @@ public class BriarService extends Service {
b.setWhen(0); // Don't show the time
b.setOngoing(true);
Intent i = new Intent(this, NavDrawerActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
FLAG_ACTIVITY_SINGLE_TOP);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SERVICE);

View File

@@ -0,0 +1,138 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
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_SIGNATURES;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
/*
* Ignore Play Services if it uses this package name and public key - it's
* effectively a system app, but not flagged as such on older systems
*/
private static final String PLAY_SERVICES_PACKAGE =
"com.google.android.gms";
private static final String PLAY_SERVICES_PUBLIC_KEY =
"30820120300D06092A864886F70D01010105000382010D0030820108" +
"0282010100AB562E00D83BA208AE0A966F124E29DA11F2AB56D08F58" +
"E2CCA91303E9B754D372F640A71B1DCB130967624E4656A7776A9219" +
"3DB2E5BFB724A91E77188B0E6A47A43B33D9609B77183145CCDF7B2E" +
"586674C9E1565B1F4C6A5955BFF251A63DABF9C55C27222252E875E4" +
"F8154A645F897168C0B1BFC612EABF785769BB34AA7984DC7E2EA276" +
"4CAE8307D8C17154D7EE5F64A51A44A602C249054157DC02CD5F5C0E" +
"55FBEF8519FBE327F0B1511692C5A06F19D18385F5C4DBC2D6B93F68" +
"CC2979C70E18AB93866B3BD5DB8999552A0E3B4C99DF58FB918BEDC1" +
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103";
private final PackageManager pm;
@Inject
ScreenFilterMonitorImpl(Application app) {
pm = app.getPackageManager();
}
@Override
@UiThread
public Set<String> getApps() {
Set<String> screenFilterApps = new TreeSet<>();
List<PackageInfo> packageInfos =
pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) {
if (isOverlayApp(packageInfo)) {
String name = pkgToString(packageInfo);
if (name != null) {
screenFilterApps.add(name);
}
}
}
return screenFilterApps;
}
// Fetches the application name for a given package.
@Nullable
private String pkgToString(PackageInfo pkgInfo) {
CharSequence seq = pm.getApplicationLabel(pkgInfo.applicationInfo);
if (seq != null) {
return seq.toString();
}
return null;
}
// Checks if an installed package is a user app using the permission.
private boolean isOverlayApp(PackageInfo packageInfo) {
int mask = FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP;
// Ignore system apps
if ((packageInfo.applicationInfo.flags & mask) != 0) {
return false;
}
// Ignore Play Services, it's effectively a system app
if (isPlayServices(packageInfo.packageName)) {
return false;
}
// Get permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (String requestedPermission : requestedPermissions) {
if (requestedPermission.equals(SYSTEM_ALERT_WINDOW)) {
return true;
}
}
}
return false;
}
private boolean isPlayServices(String pkg) {
if (!PLAY_SERVICES_PACKAGE.equals(pkg)) return false;
try {
PackageInfo sigs = pm.getPackageInfo(pkg, GET_SIGNATURES);
// The genuine Play Services app should have a single signature
Signature[] signatures = sigs.signatures;
if (signatures == null || signatures.length != 1) return false;
// Extract the public key from the signature
CertificateFactory certFactory =
CertificateFactory.getInstance("X509");
byte[] signatureBytes = signatures[0].toByteArray();
InputStream in = new ByteArrayInputStream(signatureBytes);
X509Certificate cert =
(X509Certificate) certFactory.generateCertificate(in);
byte[] publicKeyBytes = cert.getPublicKey().getEncoded();
String publicKey = StringUtils.toHexString(publicKeyBytes);
return PLAY_SERVICES_PUBLIC_KEY.equals(publicKey);
} catch (NameNotFoundException | CertificateException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false;
}
}
}

View File

@@ -1,5 +1,7 @@
package org.briarproject.briar.android;
import org.briarproject.briar.BuildConfig;
import java.util.logging.Level;
import static java.util.logging.Level.INFO;
@@ -11,7 +13,7 @@ public interface TestingConstants {
* Whether this is an alpha or beta build. This should be set to false for
* release builds.
*/
boolean TESTING = true;
boolean TESTING = BuildConfig.DEBUG;
/** Default log level. */
Level DEFAULT_LOG_LEVEL = TESTING ? INFO : OFF;

View File

@@ -39,7 +39,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupConversatio
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.briar.android.privategroup.creation.GroupCreateModule;
import org.briarproject.briar.android.privategroup.creation.CreateGroupModule;
import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
@@ -64,8 +64,6 @@ import org.briarproject.briar.android.sharing.ShareForumFragment;
import org.briarproject.briar.android.sharing.ShareForumMessageFragment;
import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.splash.SplashScreenActivity;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
import dagger.Component;
@@ -73,7 +71,7 @@ import dagger.Component;
@Component(
modules = {ActivityModule.class, ForumModule.class, SharingModule.class,
BlogModule.class, ContactModule.class, GroupListModule.class,
GroupCreateModule.class, GroupInvitationModule.class,
CreateGroupModule.class, GroupInvitationModule.class,
GroupConversationModule.class, GroupMemberModule.class,
GroupRevealModule.class},
dependencies = AndroidComponent.class)
@@ -151,10 +149,6 @@ public interface ActivityComponent {
void inject(RssFeedManageActivity activity);
void inject(EmojiProvider emojiProvider);
void inject(RecentEmojiPageModel recentEmojiPageModel);
// Fragments
void inject(ContactListFragment fragment);

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