Compare commits

...

132 Commits

Author SHA1 Message Date
akwizgran
3c290a320e Remove local author from contacts. 2019-04-23 15:50:04 +01:00
Torsten Grote
421c9c44d6 Merge branch 'bump-schema-version' into 'master'
Bump schema version to match migrations

See merge request briar/briar!1084
2019-04-23 14:41:18 +00:00
akwizgran
29d3ee2439 Bump schema version to match migrations. 2019-04-23 15:31:30 +01:00
akwizgran
06d4f85768 Merge branch 'add-handshake-key-pairs-to-db' into 'master'
Add handshake key pairs to DB, remove inactive contacts

Closes #1276

See merge request briar/briar!1080
2019-04-23 12:31:59 +00:00
Torsten Grote
9685462242 Merge branch 'static-transport-keys' into 'master'
Add database support for pending contacts and handshake keys

See merge request briar/briar!1078
2019-04-22 14:00:52 +00:00
akwizgran
84f2c29c76 Remove unnecessary call to replaceTypes(). 2019-04-22 14:43:47 +01:00
akwizgran
9c8125d77a Rename 'alice' flags to clarify usage, add comments. 2019-04-19 11:57:55 +01:00
akwizgran
1a1a010ee7 Update key derivation labels for handshake mode. 2019-04-19 11:36:21 +01:00
akwizgran
56fb20f257 Small code cleanups. 2019-04-18 13:47:31 +01:00
akwizgran
f82294527f Fix column index in getLocalAuthor(). 2019-04-18 13:35:42 +01:00
akwizgran
456f25b701 Revert unintended change to javadoc. 2019-04-18 13:31:00 +01:00
akwizgran
0587fdc54c Add handshake key pairs to DB, remove inactive contacts. 2019-04-18 13:15:25 +01:00
akwizgran
ece083026e Merge branch '1534-rss-notification' into 'master'
Make RSS blog posts not local: re-enables notification

Closes #1534

See merge request briar/briar!1079
2019-04-18 08:01:16 +00:00
Torsten Grote
0e5bb3e9de [core] RSS blog posts are not local: re-enables notification 2019-04-17 20:32:09 -03:00
akwizgran
dcebd5a81c Update terminology from static keys to handshake keys. 2019-04-17 17:28:22 +01:00
akwizgran
e9a3685bfd Fix spurious line wrapping. 2019-04-17 17:22:49 +01:00
akwizgran
3aadcc17dd Add public key to pending contacts. 2019-04-17 15:30:15 +01:00
akwizgran
296ce080e2 Add unit tests for pending contact exception. 2019-04-17 15:14:53 +01:00
akwizgran
724e6643bd Add DB methods for handshake keys and pending contacts. 2019-04-17 15:07:58 +01:00
akwizgran
fafd0c7ff9 Rename static transport keys to handshake keys. 2019-04-17 14:52:52 +01:00
akwizgran
e91a7c64d8 Add unit tests for DB pending contact methods. 2019-04-17 13:06:41 +01:00
akwizgran
f08e3a58e6 Add database methods for pending contacts. 2019-04-17 12:44:43 +01:00
akwizgran
94de1834b8 Add unit tests for DB static key methods. 2019-04-17 12:06:47 +01:00
akwizgran
6b24eeb84c Add method to set reordering window for static keys. 2019-04-17 09:58:36 +01:00
akwizgran
f72ff9f812 Add database methods for static keys. 2019-04-16 17:51:31 +01:00
akwizgran
0f5f440f1c Add key set and key set ID classes for static keys. 2019-04-16 16:59:07 +01:00
akwizgran
7acbe56197 Add abstract superclass for transport keys. 2019-04-16 16:34:27 +01:00
akwizgran
fccf735a89 Add unit tests for static key derivation. 2019-04-16 16:34:27 +01:00
akwizgran
d5ac2c9ead Fix master secret/master key/root key terminology.
In the key agreement, contact exchange and introduction protocols we
refer to the master key. In the transport protocol we refer to the root
key. When adding a contact in person, the key agreement protocol's
master key is used as the transport root key. When a contact is
introduced, the introduction protocol's master key is used as the
transport root key.
2019-04-16 16:34:26 +01:00
akwizgran
d4b929fc6c Add key derivation for static keys. 2019-04-16 16:34:26 +01:00
akwizgran
b568405f59 Create DB tables for static keys. 2019-04-16 16:34:19 +01:00
Torsten Grote
ff2f710495 Merge branch 'crypto-api-code-cleanup' into 'master'
Minor code cleanups for crypto API

See merge request briar/briar!1076
2019-04-06 14:20:26 +00:00
Torsten Grote
d00094edab Merge branch '1504-nokia-wake-lock' into 'master'
Use an appropriate wake lock tag for the device

Closes #1504

See merge request briar/briar!1077
2019-04-06 14:18:17 +00:00
akwizgran
9ca854473f Use an appropriate wake lock tag for the device. 2019-04-06 10:38:27 +01:00
Torsten Grote
8603fd3257 Merge branch 'inject-dialog-fragments-early' into 'master'
Inject remaining fragments in onAttach()

See merge request briar/briar!1075
2019-04-05 16:43:27 +00:00
Torsten Grote
648fc6e65c Merge branch 'async-events-refactoring' into 'master'
Refactor UI event listeners

See merge request briar/briar!1074
2019-04-05 16:41:19 +00:00
akwizgran
0c65e97fcf Inject remaining fragments in onAttach(). 2019-04-05 16:57:11 +01:00
akwizgran
16d2154c73 Add a couple of code cleanups. 2019-04-05 16:49:46 +01:00
akwizgran
b8e390db21 Refactor UI event listeners. 2019-04-05 16:36:54 +01:00
Torsten Grote
b2702062bc Merge branch 'async-events-commit-actions' into 'master'
Allow actions to be attached to transactions

See merge request briar/briar!1073
2019-04-05 15:12:36 +00:00
akwizgran
f11b32f188 Add unit test for commit actions. 2019-04-05 16:02:37 +01:00
akwizgran
d603607a90 Allow event executor tasks to be attached to transactions. 2019-04-05 15:57:20 +01:00
Torsten Grote
6c0dffff56 Merge branch 'db-code-cleanup' into 'master'
Clean up some database code

See merge request briar/briar!1071
2019-04-05 14:38:54 +00:00
Torsten Grote
9f3394aa1d Merge branch 'async-events' into 'master'
Broadcast events asynchronously

See merge request briar/briar!1072
2019-04-05 14:29:35 +00:00
akwizgran
74710664e3 Reduce scope of @SuppressWarnings. 2019-04-05 15:21:46 +01:00
akwizgran
0d0197fd2d Construct EventBusImpl by injection. 2019-04-05 15:19:31 +01:00
akwizgran
c3b5b04b71 Broadcast events asynchronously. 2019-04-05 15:00:49 +01:00
akwizgran
8b3164e107 Merge branch '1529-request-buttons' into 'master'
Ensure that conversation request buttons always work

Closes #1529

See merge request briar/briar!1070
2019-04-05 12:55:26 +00:00
Torsten Grote
79ff5aa148 [android] ensure that conversation request buttons always work 2019-04-05 09:38:08 -03:00
akwizgran
652ce4a53d Merge branch '1514-introduction-message' into 'master'
Fix Introduction Issues

Closes #1516 and #1514

See merge request briar/briar!1067
2019-04-04 16:47:09 +00:00
akwizgran
df0d6594b6 Merge branch '1522-contact-alias-length' into 'master'
Check contact alias for maximum length in UI

Closes #1522

See merge request briar/briar!1069
2019-04-03 10:17:54 +00:00
Torsten Grote
f73ecc6066 [android] Check contact alias for maximum length before proceeding 2019-04-02 15:06:32 -03:00
akwizgran
0f614e8460 Merge branch '1492-send-after-previews-loaded' into 'master'
Show progress bar while image previews are loading

Closes #1510, #1509, and #1492

See merge request briar/briar!1033
2019-04-01 10:27:38 +00:00
Torsten Grote
f4bdd201a3 [android] fix bug where onboarding is shown again when activity resumes 2019-03-28 08:45:43 -03:00
Torsten Grote
5130c83556 [android] Show progress bar while image previews are loading
This refactors the send buttons out into their own composite view
2019-03-28 08:45:43 -03:00
Torsten Grote
423ecc003b [android] Get notified when all image previews have been loaded
Also fix crash when attaching image fails
2019-03-28 08:45:43 -03:00
Torsten Grote
419f37a4a9 Merge branch '1517-scroll-listener-npe' into 'master'
Don't try to get item at NO_POSITION

Closes #1517

See merge request briar/briar!1068
2019-03-28 11:30:42 +00:00
akwizgran
3d94ffb714 Don't try to get item at NO_POSITION. 2019-03-28 11:06:13 +00:00
Torsten Grote
d40cfd30a2 Let IntroductionResponse know if introduction can succeed
and use this information in the android UI for showing that the user
needs to wait or not.
2019-03-26 16:18:25 -03:00
Torsten Grote
3b4a92f66c Fix introduction after one was declined
When we received a remote decline we always went into the REMOTE_DECLINED state
while there's two cases where we need to go into the START state instead.
So when the new request arrived, we weren't in START and thus aborted the protocol.
This commit fixes this.

Fixes #1516
2019-03-26 16:18:25 -03:00
Torsten Grote
f9dfbe3fa5 Don't show remote introduction responses after declining locally
Fixes #1514
2019-03-26 16:18:11 -03:00
Torsten Grote
bc8bb08853 Merge branch '1488-do-not-witness-aapt' into 'master'
Exclude AAPT dependency from gradle-witness

See merge request briar/briar!1066
2019-03-26 17:19:40 +00:00
akwizgran
cc67a8fcdd Exclude AAPT dependency from gradle-witness. 2019-03-26 17:06:46 +00:00
akwizgran
f8cf88e6cd Merge branch '1421-contact-list-during-migration' into 'master'
Don't start BriarActivities when lifecycle did not start

Closes #1421

See merge request briar/briar!1058
2019-03-26 14:25:10 +00:00
akwizgran
bc58c47a22 Merge branch 're-add-objective-c-code-style' into 'master'
Revert "Remove Objective C from code styles"

See merge request briar/briar!1065
2019-03-26 14:23:50 +00:00
Torsten Grote
aa6879c48e Revert "Remove Objective C from code styles"
This reverts commit a20e868970.
2019-03-22 15:18:02 -03:00
akwizgran
4d26628f2a Bump version numbers for 1.1.6 release. 2019-03-22 16:56:56 +00:00
akwizgran
abaa70da99 Merge branch '1501-new-contacts-at-top' into 'master'
Display new contacts at the top of the contact list

Closes #1501

See merge request briar/briar!1063
2019-03-22 16:53:52 +00:00
Torsten Grote
6435c3520c [android] Update translations, add Azerbaijani 2019-03-22 13:09:38 -03:00
akwizgran
b5c4c7ae61 Merge branch '1077-save-threaded-discussion-position' into 'master'
Save list position in threaded conversations and main blog feed

Closes #1077

See merge request briar/briar!1054
2019-03-22 15:42:53 +00:00
Torsten Grote
5d96da3547 Merge branch '1508-check-android-paths-for-null' into 'master'
Check external storage paths for null

Closes #1508

See merge request briar/briar!1064
2019-03-22 14:53:41 +00:00
akwizgran
ed842f781a Don't create extra activity instances from splash screen. 2019-03-22 13:37:58 +00:00
akwizgran
5e30e5e1de Check external storage paths for null. 2019-03-22 11:36:07 +00:00
Torsten Grote
ce52a36db1 Display new contacts at the top of the contact list
by initializing their latest message time with the current time
2019-03-21 11:45:27 -03:00
akwizgran
f5ef87b34b Merge branch '1289-recycler-view-visible-detection' into 'master'
Prevent RecyclerView's pre-rendering from marking invisible messages as read

Closes #1289

See merge request briar/briar!1061
2019-03-21 13:48:44 +00:00
Torsten Grote
4c6f68c255 [android] optimize method to update unread counts 2019-03-21 09:59:33 -03:00
Torsten Grote
ae09b4c607 [android] remove complicated logic for detecting new visible items
notify after every scroll for all visible items instead
2019-03-19 12:35:15 -03:00
Torsten Grote
880d77922e [android] use ScrollListener to mark messages read in private conversation 2019-03-19 11:42:59 -03:00
Torsten Grote
1c227e81e4 [android] update unread counts with a ScrollListener in threaded conversations 2019-03-19 11:42:59 -03:00
akwizgran
541acad29a Merge branch '1357-proper-panic-deletion' into 'master'
Stop lifecycle before deleting app data and exit cleanly

Closes #1380 and #1357

See merge request briar/briar!1060
2019-03-19 14:15:49 +00:00
Torsten Grote
60f71648f3 [android] Don't start NavDrawerActivity directly from foreground notification
It might be that the lifecycle didn't start, so we need to show the
OpenDatabaseActivity first.
2019-03-19 11:14:01 -03:00
Torsten Grote
270b8af39f [android] add review comments for panic induced account deletion 2019-03-19 10:57:28 -03:00
Torsten Grote
31d3324701 [android] stop livecycle before delete app data and exit cleanly
Fixes #1380
2019-03-19 10:50:51 -03:00
akwizgran
dbe46d60fd Merge branch '830-text-input-landscape-send' into 'master'
Make Text Input Fields Work In Landscape Mode

Closes #830

See merge request briar/briar!1053
2019-03-19 10:38:17 +00:00
akwizgran
d10ab96955 Merge branch '1370-block-block-notification' into 'master'
Block blog notifications when this blog is open

Closes #1370

See merge request briar/briar!1057
2019-03-19 10:35:42 +00:00
akwizgran
b2841e245a Merge branch 'gradle-plugin-3.3.2' into 'master'
Upgrade android gradle plugin to 3.3.2

See merge request briar/briar!1062
2019-03-19 10:15:37 +00:00
akwizgran
68c40f0c46 Minor code cleanups for crypto API. 2019-03-15 13:54:04 +00:00
Torsten Grote
9ccd8d1602 Upgrade android gradle plugin to 3.3.2
This also updates some briar-headless dependencies
2019-03-14 14:27:53 -03:00
Torsten Grote
ac3942975e [android] add SendAction for RSS feed import 2019-03-12 17:10:52 -03:00
Torsten Grote
b6455d40a7 [android] add SendAction to EmojiTextInputView 2019-03-12 16:05:53 -03:00
Torsten Grote
2815ad042d [android] don't show blog post notifications for own blog posts 2019-03-08 16:45:31 -03:00
Torsten Grote
2055961534 [android] remember scroll position in individual blogs
across configuration changes
2019-03-08 16:33:15 -03:00
Torsten Grote
741eae34e9 [android] save list position of main blog feed 2019-03-08 16:08:11 -03:00
Torsten Grote
50bd4cce6b [android] Save list position in threaded conversations 2019-03-08 16:08:11 -03:00
akwizgran
0a5a8310fc Merge branch '1210-contact-list-duplicates' into 'master'
Small improvements for contact list, hunting duplicates

See merge request briar/briar!1056
2019-03-08 14:26:56 +00:00
akwizgran
cc43d5982a Merge branch '1196-remove-thread-sent-snackbars' into 'master'
Remove unnecessary snackbars in threaded conversation

Closes #1196

See merge request briar/briar!1055
2019-03-08 14:23:19 +00:00
akwizgran
50675473ce Merge branch '1126-link-warning-buttons' into 'master'
Make link warning dialog scrollable

Closes #1126

See merge request briar/briar!1050
2019-03-08 13:38:06 +00:00
akwizgran
de852b2a9f Merge branch '1413-empty-state-fix' into 'master'
Always show empty state messages

Closes #1413

See merge request briar/briar!1059
2019-03-08 10:00:41 +00:00
Torsten Grote
b7c712116b [android] Always show empty state messages
This works around an upstream ConstraintLayout Group visiblity bug:
https://issuetracker.google.com/issues/117485026
2019-03-01 15:34:29 -03:00
Torsten Grote
7dd4897c8c [android] small improvements for contact list 2019-02-28 15:16:36 -03:00
Torsten Grote
7469c0f5e3 [android] remove unnecessary snackbars in threaded conversation
that appear after posting a new message there
2019-02-28 14:28:37 -03:00
akwizgran
144ea0c2fc Merge branch '875-sharing-status-screen-updates' into 'master'
Update memberlists while they are open

Closes #875

See merge request briar/briar!1048
2019-02-28 13:27:19 +00:00
Torsten Grote
a917ebdc76 [android] Close memberlist or sharing status screen when group was left 2019-02-28 09:25:18 -03:00
Torsten Grote
2a389c74dc [android] when sharing a forum or blog, add peers to list while it is open 2019-02-28 09:25:08 -03:00
Torsten Grote
ef16d096f1 [android] add group members to memberlist when they join 2019-02-28 09:25:08 -03:00
akwizgran
679455888b Merge branch '833-ui-reference' into 'master'
Don't pass UI classes to the core, use events instead

See merge request briar/briar!1044
2019-02-28 11:11:14 +00:00
akwizgran
d4372ddae7 Merge branch 'headless-document-build' into 'master'
Briar Headless: Document build process

See merge request briar/briar!1042
2019-02-28 11:10:24 +00:00
Nico Alt
c3ef990a94 Briar Headless: Document build process 2019-02-27 21:27:04 +01:00
Torsten Grote
8ae9b7f5a2 [android] Ensure that buttons of link warning are always visible 2019-02-27 17:01:50 -03:00
Torsten Grote
106d80ef76 [android] Make link warning dialog scrollable 2019-02-27 14:03:20 -03:00
Torsten Grote
9422ba2718 Don't pass UI classes to the core, use events instead
This removed the ContactExchangeListener in favor of new events
2019-02-27 13:55:33 -03:00
akwizgran
8343f5c2db Merge branch 'objective-c' into 'master'
Remove Objective C from code styles

See merge request briar/briar!1051
2019-02-27 13:42:08 +00:00
akwizgran
371c7efb04 Merge branch '1106-memberlist-button' into 'master'
Move group memberlist button to overflow menu

See merge request briar/briar!1052
2019-02-27 13:40:19 +00:00
Torsten Grote
92d67645ab [android] move group memberlist button to overflow menu 2019-02-27 10:25:41 -03:00
Torsten Grote
a20e868970 Remove Objective C from code styles 2019-02-27 10:14:22 -03:00
akwizgran
dd853f6718 Merge branch '1475-status-bar-return-transition' into 'master'
Show the status bar when finishing ImageActivity

See merge request briar/briar!1036
2019-02-27 13:11:29 +00:00
akwizgran
16a8ad996a Merge branch '869-remove-group-button' into 'master'
[android] Fix private group status text over remove button

Closes #869

See merge request briar/briar!1047
2019-02-27 11:36:06 +00:00
akwizgran
e27885f0c8 Merge branch '850-initial-group-sharing-status' into 'master'
Update group sharing status when creator joins group

Closes #850

See merge request briar/briar!1046
2019-02-27 11:34:55 +00:00
Torsten Grote
f6ef48bf90 [android] Fix private group status text over remove button 2019-02-26 11:38:17 -03:00
Torsten Grote
e282ca763d [android] Update group sharing status when creator joins group 2019-02-26 11:29:23 -03:00
Torsten Grote
71016382dc Merge branch 'tor-0.3.5.8' into 'master'
Upgrade Tor to 0.3.5.8

See merge request briar/briar!1045
2019-02-26 13:15:22 +00:00
akwizgran
d004933fae Upgrade Tor to 0.3.5.8. 2019-02-26 12:39:47 +00:00
akwizgran
37512c50d8 Merge branch '1497-foreground-permission' into 'master'
Add FOREGROUND_SERVICE permission (needed when targeting higher API level)

See merge request briar/briar!1041
2019-02-21 10:30:10 +00:00
Torsten Grote
0b61a5d40a Add FOREGROUND_SERVICE permission (needed when targeting higher API level) 2019-02-20 11:00:15 -03:00
akwizgran
5dd320f282 Merge branch '1498-meek' into 'master'
Use the pluggable transport meek lite where obfs4 is blocked

Closes #1498 and #1418

See merge request briar/briar!1040
2019-02-19 17:37:13 +00:00
akwizgran
2a21db5fb6 Merge branch 'tor-0.3.5.7' into 'master'
Upgrade Tor to 0.3.5.7

See merge request briar/briar!1039
2019-02-19 16:37:30 +00:00
Torsten Grote
b023593a2c Use the pluggable transport meek lite where obfs4 is blocked 2019-02-19 12:49:22 -03:00
Torsten Grote
5ccf2cae1f Upgrade Tor to 0.3.5.7 2019-02-19 11:09:45 -03:00
Torsten Grote
c2cb89ab73 [android] show the status bar when finishing ImageActivity
to prevent visible jump in exit transition.
2019-02-13 16:54:16 -02:00
akwizgran
d4f8abfac1 Suppress warning about parameter used by subclasses. 2018-12-06 15:24:09 +00:00
akwizgran
d07c144316 Remove unnecessary null check. 2018-12-06 15:24:09 +00:00
akwizgran
dcd5189910 Remove unused DB code for managing disk space. 2018-12-06 15:24:08 +00:00
286 changed files with 6296 additions and 3276 deletions

View File

@@ -11,8 +11,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 10105 versionCode 10106
versionName "1.1.5" versionName "1.1.6"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -30,8 +30,8 @@ configurations {
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.4.8@zip' tor 'org.briarproject:tor-android:0.3.5.8@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.7@zip' tor 'org.briarproject:obfs4proxy-android:0.0.9@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.19' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'

View File

@@ -12,11 +12,15 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import java.io.File; import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
class AndroidAccountManager extends AccountManagerImpl class AndroidAccountManager extends AccountManagerImpl
implements AccountManager { implements AccountManager {
@@ -89,20 +93,42 @@ class AndroidAccountManager extends AccountManagerImpl
LOG.warning("Could not clear shared preferences"); LOG.warning("Could not clear shared preferences");
} }
// Delete files, except lib and shared_prefs directories // Delete files, except lib and shared_prefs directories
Set<File> files = new HashSet<>();
File dataDir = new File(appContext.getApplicationInfo().dataDir); File dataDir = new File(appContext.getApplicationInfo().dataDir);
File[] children = dataDir.listFiles(); @Nullable
if (children == null) { File[] fileArray = dataDir.listFiles();
if (fileArray == null) {
LOG.warning("Could not list files in app data dir"); LOG.warning("Could not list files in app data dir");
} else { } else {
for (File child : children) { for (File file : fileArray) {
String name = child.getName(); String name = file.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) { if (!name.equals("lib") && !name.equals("shared_prefs")) {
IoUtils.deleteFileOrDir(child); files.add(file);
} }
} }
} }
files.add(appContext.getFilesDir());
files.add(appContext.getCacheDir());
addIfNotNull(files, appContext.getExternalCacheDir());
if (SDK_INT >= 19) {
for (File file : appContext.getExternalCacheDirs()) {
addIfNotNull(files, file);
}
}
if (SDK_INT >= 21) {
for (File file : appContext.getExternalMediaDirs()) {
addIfNotNull(files, file);
}
}
for (File file : files) {
IoUtils.deleteFileOrDir(file);
}
// Recreate the cache dir as some OpenGL drivers expect it to exist // Recreate the cache dir as some OpenGL drivers expect it to exist
if (!new File(dataDir, "cache").mkdir()) if (!new File(dataDir, "cache").mkdirs())
LOG.warning("Could not recreate cache dir"); LOG.warning("Could not recreate cache dir");
} }
private void addIfNotNull(Set<File> files, @Nullable File file) {
if (file != null) files.add(file);
}
} }

View File

@@ -32,9 +32,6 @@ import static java.util.concurrent.TimeUnit.MINUTES;
@ParametersNotNullByDefault @ParametersNotNullByDefault
class AndroidTorPlugin extends TorPlugin { class AndroidTorPlugin extends TorPlugin {
// This tag may prevent Huawei's power manager from killing us
private static final String WAKE_LOCK_TAG = "LocationManagerService";
private final Context appContext; private final Context appContext;
private final RenewableWakeLock wakeLock; private final RenewableWakeLock wakeLock;
@@ -55,7 +52,7 @@ class AndroidTorPlugin extends TorPlugin {
appContext.getSystemService(POWER_SERVICE); appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError(); if (pm == null) throw new AssertionError();
wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK, wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
WAKE_LOCK_TAG, 1, MINUTES); getWakeLockTag(), 1, MINUTES);
} }
@Override @Override
@@ -87,4 +84,17 @@ class AndroidTorPlugin extends TorPlugin {
super.stop(); super.stop();
wakeLock.release(); wakeLock.release();
} }
private String getWakeLockTag() {
PackageManager pm = appContext.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return getClass().getSimpleName();
}
} }

View File

@@ -1,10 +1,13 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.util.concurrent.Executor;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
@@ -32,6 +35,13 @@ public class AndroidSystemModule {
return androidExecutor; return androidExecutor;
} }
@Provides
@Singleton
@EventExecutor
Executor provideEventExecutor(AndroidExecutor androidExecutor) {
return androidExecutor::runOnUiThread;
}
@Provides @Provides
@Singleton @Singleton
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) { ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {

View File

@@ -112,6 +112,8 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
// Other directories should be deleted // Other directories should be deleted
File potatoDir = new File(testDir, ".potato"); File potatoDir = new File(testDir, ".potato");
File potatoFile = new File(potatoDir, "file"); File potatoFile = new File(potatoDir, "file");
File filesDir = new File(testDir, "filesDir");
File externalCacheDir = new File(testDir, "externalCacheDir");
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(prefs).edit(); oneOf(prefs).edit();
@@ -128,6 +130,12 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(app).getApplicationInfo(); oneOf(app).getApplicationInfo();
will(returnValue(applicationInfo)); will(returnValue(applicationInfo));
oneOf(app).getFilesDir();
will(returnValue(filesDir));
oneOf(app).getCacheDir();
will(returnValue(cacheDir));
oneOf(app).getExternalCacheDir();
will(returnValue(externalCacheDir));
}}); }});
assertTrue(dbDir.mkdirs()); assertTrue(dbDir.mkdirs());
@@ -140,6 +148,8 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertTrue(cacheFile.createNewFile()); assertTrue(cacheFile.createNewFile());
assertTrue(potatoDir.mkdirs()); assertTrue(potatoDir.mkdirs());
assertTrue(potatoFile.createNewFile()); assertTrue(potatoFile.createNewFile());
assertTrue(filesDir.mkdirs());
assertTrue(externalCacheDir.mkdirs());
accountManager.deleteAccount(); accountManager.deleteAccount();
@@ -153,6 +163,8 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertFalse(cacheFile.exists()); assertFalse(cacheFile.exists());
assertFalse(potatoDir.exists()); assertFalse(potatoDir.exists());
assertFalse(potatoFile.exists()); assertFalse(potatoFile.exists());
assertFalse(filesDir.exists());
assertFalse(externalCacheDir.exists());
} }
@After @After

View File

@@ -1,46 +1,44 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f', 'com.android.tools.analytics-library:protos:26.3.2:protos-26.3.2.jar:50238fb4298b297217b184b9cd93c14f83536fcee829eb0ca850bdb5b53251f0',
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a', 'com.android.tools.analytics-library:shared:26.3.2:shared-26.3.2.jar:ddd80dcf21905018b7c0583ba72b7282f446084d4952422609a09fbf8237ef71',
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75', 'com.android.tools.analytics-library:tracker:26.3.2:tracker-26.3.2.jar:28c575d2d1af003e96d7b375a668ee10b2673a2dd0f6438750aa8c3b42e7d0ad',
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e', 'com.android.tools.build:apksig:3.3.2:apksig-3.3.2.jar:84c4aaa20127c6c1fe6bdd334b3f5df71f54ad080be9029c8a10f43b6a908acd',
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5', 'com.android.tools.build:apkzlib:3.3.2:apkzlib-3.3.2.jar:d34e523278e5dff565eba3ef3c089d515b2b5cc7b47dc77e2f3465e5e47176ac',
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa', 'com.android.tools.build:builder-model:3.3.2:builder-model-3.3.2.jar:055e3db0ecee9e06b9f024034999a29cd92cb1885207b37542126bd8bcc57f46',
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff', 'com.android.tools.build:builder-test-api:3.3.2:builder-test-api-3.3.2.jar:0b2e4cd7615bbcad14a3c91fe45ae26693508d06e40ba06c5968b8bc24416618',
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be', 'com.android.tools.build:builder:3.3.2:builder-3.3.2.jar:65649704da7aef0487235fd326f0f2e99ed5cf958e80f204496e6e08a42bd9f5',
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83', 'com.android.tools.build:gradle-api:3.3.2:gradle-api-3.3.2.jar:3cbd47e41bb70330dd72ec2c9fe51e6173554b484a03829b5a2de9e00841e040',
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d', 'com.android.tools.build:manifest-merger:26.3.2:manifest-merger-26.3.2.jar:05c4a6d8b02fb9f08744876477d0a68547c03a8a9069b1f086684fa04af97c33',
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6', 'com.android.tools.ddms:ddmlib:26.3.2:ddmlib-26.3.2.jar:d248da8a563d6e46d2c7ebbf371a4877e00510f4ca763c0bb272d5a281bf8b85',
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b', 'com.android.tools.external.com-intellij:intellij-core:26.3.2:intellij-core-26.3.2.jar:6c5ecc968230e9f4dcd0fef28885379feace1f0cd8130de6f61d649c86139bf3',
'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe', 'com.android.tools.external.com-intellij:kotlin-compiler:26.3.2:kotlin-compiler-26.3.2.jar:1007d9b07ccb49cd8eaf30fda10ed4681d4714f2f9ab2ecda39b4e539cc51bbe',
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae', 'com.android.tools.external.org-jetbrains:uast:26.3.2:uast-26.3.2.jar:5d1833e562ea4f38a89708dfde695f0a162cbd39d003d3dde818c3fdc2b05317',
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b', 'com.android.tools.layoutlib:layoutlib-api:26.3.2:layoutlib-api-26.3.2.jar:d7e61e874ab95f5c350dd38b6a95b5c9dbe0083a02001884264cdb390cb255b8',
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade', 'com.android.tools.lint:lint-api:26.3.2:lint-api-26.3.2.jar:5867dfd7fb4a4e161a816a5d29d045f9b542d34594c00a1efec46fb4cd0e1033',
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e', 'com.android.tools.lint:lint-checks:26.3.2:lint-checks-26.3.2.jar:4b163b9c93790d2771e92ba8de58a0d9e0671ffcf2ccef3cf496efd442e27517',
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6', 'com.android.tools.lint:lint-gradle-api:26.3.2:lint-gradle-api-26.3.2.jar:54cb282e0c054f9bed3f51302ce08b003c8ab7961dfd5a4f6de26c23cc23062f',
'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f', 'com.android.tools.lint:lint-gradle:26.3.2:lint-gradle-26.3.2.jar:bb139615f4ce97d42cc394b9389b49b76a6eb85be6785a5d272991543b519013',
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb', 'com.android.tools.lint:lint:26.3.2:lint-26.3.2.jar:ef7b369f8a56a92ccb0f4c1c357666b9339e4a711a9d84747d446441746cfe4e',
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877', 'com.android.tools:annotations:26.3.2:annotations-26.3.2.jar:5bcce8e98b6a2f5ccf13ebcefd8f734e0b35f8b19e456575665631442ce1f7a1',
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5', 'com.android.tools:common:26.3.2:common-26.3.2.jar:d9f8e7f0669e9a701568e3db6a87c89cf12d8fa6811c9991e969f950215ecfac',
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24', 'com.android.tools:dvlib:26.3.2:dvlib-26.3.2.jar:d84aad56161c7773579303d69714ded6897c64c6ddfd7d456e453231e4dfe811',
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5', 'com.android.tools:repository:26.3.2:repository-26.3.2.jar:da611eeb06e9ab8750d25b9e2901e10db8e5ec6304eb4c8b7103d39e0921ea40',
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa', 'com.android.tools:sdk-common:26.3.2:sdk-common-26.3.2.jar:82823a3bf25e64fac33a286490f0cf5ac50c2cdb3c540149b030896bb44bf96c',
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1', 'com.android.tools:sdklib:26.3.2:sdklib-26.3.2.jar:424d15492af67321900963238646d27495ab60de2a5b19e6a416963bc5d6932b',
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825', 'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc', 'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc',
'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768', 'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768',
'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1', 'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939', 'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:guava:23.0:guava-23.0.jar:7baa80df284117e5b945b19b98d367a85ea7b7801bd358ff657946c3bd1b6596',
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4', 'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
@@ -68,21 +66,22 @@ dependencyVerification {
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.7:obfs4proxy-android-0.0.7.zip:abdfb5d889d848de9bf214f9276abbf454808a505b870819eccc9a9e985bf617', 'org.briarproject:obfs4proxy-android:0.0.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0',
'org.briarproject:tor-android:0.3.4.8:tor-android-0.3.4.8.zip:989a0352d9d8d8172cd6c2137654e165e5d2beb10ed1211bab3814e224ad1926', 'org.briarproject:tor-android:0.3.5.8:tor-android-0.3.5.8.zip:42a13a6f185be1a62f42e3f30ce66a3c099ac5ec890a65e7593111b65b44a54a',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa', 'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70', 'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea', 'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd', 'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

@@ -18,11 +18,10 @@ public interface ContactGroupFactory {
* Creates a group for the given client to share with the given contact. * Creates a group for the given client to share with the given contact.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact); Contact contact, AuthorId local);
/** /**
* Creates a group for the given client to share between the given authors * Creates a group for the given client to share between the given authors.
* identified by their AuthorIds.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2); AuthorId authorId1, AuthorId authorId2);

View File

@@ -1,13 +1,13 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.util.StringUtils.toUtf8; import static org.briarproject.bramble.util.StringUtils.toUtf8;
@Immutable @Immutable
@@ -16,24 +16,28 @@ public class Contact {
private final ContactId id; private final ContactId id;
private final Author author; private final Author author;
private final AuthorId localAuthorId;
@Nullable @Nullable
private final String alias; private final String alias;
private final boolean verified, active; @Nullable
private final byte[] handshakePublicKey;
private final boolean verified;
public Contact(ContactId id, Author author, AuthorId localAuthorId, public Contact(ContactId id, Author author, @Nullable String alias,
@Nullable String alias, boolean verified, boolean active) { @Nullable byte[] handshakePublicKey, boolean verified) {
if (alias != null) { if (alias != null) {
int aliasLength = toUtf8(alias).length; int aliasLength = toUtf8(alias).length;
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH) if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (handshakePublicKey != null && (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH)) {
throw new IllegalArgumentException();
}
this.id = id; this.id = id;
this.author = author; this.author = author;
this.localAuthorId = localAuthorId;
this.alias = alias; this.alias = alias;
this.handshakePublicKey = handshakePublicKey;
this.verified = verified; this.verified = verified;
this.active = active;
} }
public ContactId getId() { public ContactId getId() {
@@ -44,21 +48,18 @@ public class Contact {
return author; return author;
} }
public AuthorId getLocalAuthorId() {
return localAuthorId;
}
@Nullable @Nullable
public String getAlias() { public String getAlias() {
return alias; return alias;
} }
public boolean isVerified() { @Nullable
return verified; public byte[] getHandshakePublicKey() {
return handshakePublicKey;
} }
public boolean isActive() { public boolean isVerified() {
return active; return verified;
} }
@Override @Override

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface ContactExchangeListener {
void contactExchangeSucceeded(Author remoteAuthor);
/**
* The exchange failed because the contact already exists.
*/
void duplicateContact(Author remoteAuthor);
/**
* A general failure.
*/
void contactExchangeFailed();
}

View File

@@ -18,31 +18,30 @@ public interface ContactExchangeTask {
byte PROTOCOL_VERSION = 1; byte PROTOCOL_VERSION = 1;
/** /**
* Label for deriving Alice's header key from the master secret. * Label for deriving Alice's header key from the master key.
*/ */
String ALICE_KEY_LABEL = String ALICE_KEY_LABEL =
"org.briarproject.bramble.contact/ALICE_HEADER_KEY"; "org.briarproject.bramble.contact/ALICE_HEADER_KEY";
/** /**
* Label for deriving Bob's header key from the master secret. * Label for deriving Bob's header key from the master key.
*/ */
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY"; String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
/** /**
* Label for deriving Alice's key binding nonce from the master secret. * Label for deriving Alice's key binding nonce from the master key.
*/ */
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE"; String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
/** /**
* Label for deriving Bob's key binding nonce from the master secret. * Label for deriving Bob's key binding nonce from the master key.
*/ */
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE"; String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
/** /**
* Exchanges contact information with a remote peer. * Exchanges contact information with a remote peer.
*/ */
void startExchange(ContactExchangeListener listener, void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
LocalAuthor localAuthor, SecretKey masterSecret,
DuplexTransportConnection conn, TransportId transportId, DuplexTransportConnection conn, TransportId transportId,
boolean alice); boolean alice);
} }

View File

@@ -13,8 +13,6 @@ import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.bramble.api.contact.PendingContact.PendingContactState.FAILED;
@NotNullByDefault @NotNullByDefault
public interface ContactManager { public interface ContactManager {
@@ -26,34 +24,31 @@ public interface ContactManager {
void registerContactHook(ContactHook hook); void registerContactHook(ContactHook hook);
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact with the given pseudonym, derives and stores transport
* derives and stores transport keys for each transport, and returns an ID * keys for each transport, and returns an ID for the contact.
* for the contact.
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID
* for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException; throws DbException;
/**
* Stores a contact with the given pseudonym and returns an ID for the
* contact.
*/
ContactId addContact(Transaction txn, Author a, boolean verified)
throws DbException;
/**
* Stores a contact with the given pseudonym, derives and stores transport
* keys for each transport, and returns an ID for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author a, SecretKey rootKey, long timestamp,
boolean alice, boolean verified, boolean active) throws DbException;
/** /**
* Returns the static link that needs to be sent to the contact to be added. * Returns the static link that needs to be sent to the contact to be added.
*/ */
@@ -79,7 +74,8 @@ public interface ContactManager {
Collection<PendingContact> getPendingContacts(); Collection<PendingContact> getPendingContacts();
/** /**
* Removes a {@link PendingContact} that is in state {@link FAILED}. * Removes a {@link PendingContact} that is in state
* {@link PendingContactState FAILED}.
*/ */
void removePendingContact(PendingContact pendingContact); void removePendingContact(PendingContact pendingContact);
@@ -89,27 +85,19 @@ public interface ContactManager {
Contact getContact(ContactId c) throws DbException; Contact getContact(ContactId c) throws DbException;
/** /**
* Returns the contact with the given remoteAuthorId * Returns the contact with the given ID.
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/ */
Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId) Contact getContact(AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns the contact with the given remoteAuthorId * Returns the contact with the given ID.
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/ */
Contact getContact(Transaction txn, AuthorId remoteAuthorId, Contact getContact(Transaction txn, AuthorId a) throws DbException;
AuthorId localAuthorId) throws DbException;
/** /**
* Returns all active contacts. * Returns all active contacts.
*/ */
Collection<Contact> getActiveContacts() throws DbException; Collection<Contact> getContacts() throws DbException;
/** /**
* Removes a contact and all associated state. * Removes a contact and all associated state.
@@ -121,12 +109,6 @@ public interface ContactManager {
*/ */
void removeContact(Transaction txn, ContactId c) throws DbException; void removeContact(Transaction txn, ContactId c) throws DbException;
/**
* Marks a contact as active or inactive.
*/
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/** /**
* Sets an alias name for the contact or unsets it if alias is null. * Sets an alias name for the contact or unsets it if alias is null.
*/ */
@@ -140,16 +122,14 @@ public interface ContactManager {
throws DbException; throws DbException;
/** /**
* Return true if a contact with this name and public key already exists * Returns true if a contact with this pseudonym already exists.
*/ */
boolean contactExists(Transaction txn, AuthorId remoteAuthorId, boolean contactExists(Transaction txn, AuthorId a) throws DbException;
AuthorId localAuthorId) throws DbException;
/** /**
* Return true if a contact with this name and public key already exists * Returns true if a contact with this pseudonym already exists.
*/ */
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) boolean contactExists(AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns the {@link AuthorInfo} for the given author. * Returns the {@link AuthorInfo} for the given author.

View File

@@ -8,26 +8,29 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class PendingContact { public class PendingContact {
public enum PendingContactState {
WAITING_FOR_CONNECTION,
CONNECTED,
ADDING_CONTACT,
FAILED
}
private final PendingContactId id; private final PendingContactId id;
private final byte[] publicKey;
private final String alias; private final String alias;
private final PendingContactState state; private final PendingContactState state;
private final long timestamp; private final long timestamp;
public PendingContact(PendingContactId id, String alias, public PendingContact(PendingContactId id, byte[] publicKey,
PendingContactState state, long timestamp) { String alias, PendingContactState state, long timestamp) {
this.id = id; this.id = id;
this.publicKey = publicKey;
this.alias = alias; this.alias = alias;
this.state = state; this.state = state;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public PendingContactId getId() {
return id;
}
public byte[] getPublicKey() {
return publicKey;
}
public String getAlias() { public String getAlias() {
return alias; return alias;
} }
@@ -50,5 +53,4 @@ public class PendingContact {
return o instanceof PendingContact && return o instanceof PendingContact &&
id.equals(((PendingContact) o).id); id.equals(((PendingContact) o).id);
} }
} }

View File

@@ -1,11 +1,25 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a
* {@link PendingContact}.
*/
@ThreadSafe
@NotNullByDefault
public class PendingContactId extends UniqueId { public class PendingContactId extends UniqueId {
public PendingContactId(byte[] id) { public PendingContactId(byte[] id) {
super(id); super(id);
} }
@Override
public boolean equals(Object o) {
return o instanceof PendingContactId && super.equals(o);
}
} }

View File

@@ -0,0 +1,30 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public enum PendingContactState {
WAITING_FOR_CONNECTION(0),
CONNECTED(1),
ADDING_CONTACT(2),
FAILED(3);
private final int value;
PendingContactState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static PendingContactState fromValue(int value) {
for (PendingContactState s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
}

View File

@@ -14,18 +14,12 @@ import javax.annotation.concurrent.Immutable;
public class ContactAddedEvent extends Event { public class ContactAddedEvent extends Event {
private final ContactId contactId; private final ContactId contactId;
private final boolean active;
public ContactAddedEvent(ContactId contactId, boolean active) { public ContactAddedEvent(ContactId contactId) {
this.contactId = contactId; this.contactId = contactId;
this.active = active;
} }
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
public boolean isActive() {
return active;
}
} }

View File

@@ -0,0 +1,32 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
public class ContactExchangeFailedEvent extends Event {
@Nullable
private final Author duplicateRemoteAuthor;
public ContactExchangeFailedEvent(@Nullable Author duplicateRemoteAuthor) {
this.duplicateRemoteAuthor = duplicateRemoteAuthor;
}
public ContactExchangeFailedEvent() {
this(null);
}
@Nullable
public Author getDuplicateRemoteAuthor() {
return duplicateRemoteAuthor;
}
public boolean wasDuplicateContact() {
return duplicateRemoteAuthor != null;
}
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class ContactExchangeSucceededEvent extends Event {
private final Author remoteAuthor;
public ContactExchangeSucceededEvent(Author remoteAuthor) {
this.remoteAuthor = remoteAuthor;
}
public Author getRemoteAuthor() {
return remoteAuthor;
}
}

View File

@@ -1,31 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a contact is marked active or inactive.
*/
@Immutable
@NotNullByDefault
public class ContactStatusChangedEvent extends Event {
private final ContactId contactId;
private final boolean active;
public ContactStatusChangedEvent(ContactId contactId, boolean active) {
this.contactId = contactId;
this.active = active;
}
public ContactId getContactId() {
return contactId;
}
public boolean isActive() {
return active;
}
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api.contact.event; package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContact.PendingContactState;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.crypto;
/** /**
* Crypto operations for the key agreement protocol - see * Crypto operations for the key agreement protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md * https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BQP.md
*/ */
public interface KeyAgreementCrypto { public interface KeyAgreementCrypto {

View File

@@ -1,29 +1,45 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
/** /**
* Crypto operations for the transport security protocol - see * Crypto operations for the transport security protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md * https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BTP.md
*/ */
public interface TransportCrypto { public interface TransportCrypto {
/** /**
* Derives initial transport keys for the given transport in the given * Derives initial transport keys for the given transport in the given
* rotation period from the given master secret. * time period from the given root key.
* *
* @param alice whether the keys are for use by Alice or Bob. * @param alice whether the keys are for use by Alice or Bob.
* @param active whether the keys are usable for outgoing streams. * @param active whether the keys are usable for outgoing streams.
*/ */
TransportKeys deriveTransportKeys(TransportId t, SecretKey master, TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey,
long rotationPeriod, boolean alice, boolean active); long timePeriod, boolean alice, boolean active);
/** /**
* Rotates the given transport keys to the given rotation period. If the * Rotates the given transport keys to the given time period. If the keys
* keys are for the given period or any later period they are not rotated. * are for the given period or any later period they are not rotated.
*/ */
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod);
/**
* Derives handshake keys for the given transport in the given time period
* from the given root key.
*
* @param alice whether the keys are for use by Alice or Bob.
*/
HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean alice);
/**
* Updates the given handshake keys to the given time period. If the keys
* are for the given period or any later period they are not updated.
*/
HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod);
/** /**
* Encodes the pseudo-random tag that is used to recognise a stream. * Encodes the pseudo-random tag that is used to recognise a stream.

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.EventExecutor;
/**
* An action that's taken when a {@link Transaction} is committed.
*/
public interface CommitAction {
void accept(Visitor visitor);
interface Visitor {
@EventExecutor
void visit(EventAction a);
@EventExecutor
void visit(TaskAction a);
}
}

View File

@@ -2,6 +2,8 @@ package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -20,8 +22,11 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -97,17 +102,31 @@ public interface DatabaseComponent {
NullableDbCallable<R, E> task) throws DbException, E; NullableDbCallable<R, E> task) throws DbException, E;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact with the given pseudonym and returns an ID for the
* and returns an ID for the contact. * contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author a, boolean verified)
boolean verified, boolean active) throws DbException; throws DbException;
/** /**
* Stores a group. * Stores a group.
*/ */
void addGroup(Transaction txn, Group g) throws DbException; void addGroup(Transaction txn, Group g) throws DbException;
/**
* Stores the given handshake keys for the given contact and returns a
* key set ID.
*/
HandshakeKeySetId addHandshakeKeys(Transaction txn, ContactId c,
HandshakeKeys k) throws DbException;
/**
* Stores the given handshake keys for the given pending contact and
* returns a key set ID.
*/
HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/** /**
* Stores a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -119,6 +138,12 @@ public interface DatabaseComponent {
void addLocalMessage(Transaction txn, Message m, Metadata meta, void addLocalMessage(Transaction txn, Message m, Metadata meta,
boolean shared) throws DbException; boolean shared) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(Transaction txn, PendingContact p)
throws DbException;
/** /**
* Stores a transport. * Stores a transport.
*/ */
@@ -129,27 +154,39 @@ public interface DatabaseComponent {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(Transaction txn, ContactId c, TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact.
* local pseudonym. * <p/>
* Read-only.
*/ */
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local) boolean containsContact(Transaction txn, AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns true if the database contains the given group. * Returns true if the database contains the given group.
* <p/>
* Read-only.
*/ */
boolean containsGroup(Transaction txn, GroupId g) throws DbException; boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/** /**
* Returns true if the database contains the given local author. * Returns true if the database contains the given local author.
* <p/>
* Read-only.
*/ */
boolean containsLocalAuthor(Transaction txn, AuthorId local) boolean containsLocalAuthor(Transaction txn, AuthorId local)
throws DbException; throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Deletes the message with the given ID. Unlike * Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID, * {@link #removeMessage(Transaction, MessageId)}, the message ID,
@@ -215,6 +252,13 @@ public interface DatabaseComponent {
*/ */
Contact getContact(Transaction txn, ContactId c) throws DbException; Contact getContact(Transaction txn, ContactId c) throws DbException;
/**
* Returns the contact with the given author ID.
* <p/>
* Read-only.
*/
Contact getContact(Transaction txn, AuthorId a) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/> * <p/>
@@ -222,22 +266,6 @@ public interface DatabaseComponent {
*/ */
Collection<Contact> getContacts(Transaction txn) throws DbException; Collection<Contact> getContacts(Transaction txn) throws DbException;
/**
* Returns a possibly empty collection of contacts with the given author ID.
* <p/>
* Read-only.
*/
Collection<Contact> getContactsByAuthorId(Transaction txn, AuthorId remote)
throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -269,6 +297,14 @@ public interface DatabaseComponent {
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g) Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -417,6 +453,14 @@ public interface DatabaseComponent {
*/ */
long getNextSendTime(Transaction txn, ContactId c) throws DbException; long getNextSendTime(Transaction txn, ContactId c) throws DbException;
/**
* Returns all pending contacts.
* <p/>
* Read-only.
*/
Collection<PendingContact> getPendingContacts(Transaction txn)
throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
* <p/> * <p/>
@@ -429,14 +473,20 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t) Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
throws DbException; throws DbException;
/**
* Increments the outgoing stream counter for the given handshake keys.
*/
void incrementStreamCounter(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k) void incrementStreamCounter(Transaction txn, TransportId t,
throws DbException; TransportKeySetId k) throws DbException;
/** /**
* Merges the given metadata with the existing metadata for the given * Merges the given metadata with the existing metadata for the given
@@ -491,6 +541,12 @@ public interface DatabaseComponent {
*/ */
void removeGroup(Transaction txn, Group g) throws DbException; void removeGroup(Transaction txn, Group g) throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -501,6 +557,12 @@ public interface DatabaseComponent {
*/ */
void removeMessage(Transaction txn, MessageId m) throws DbException; void removeMessage(Transaction txn, MessageId m) throws DbException;
/**
* Removes a pending contact (and all associated state) from the database.
*/
void removePendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -509,20 +571,14 @@ public interface DatabaseComponent {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k) void removeTransportKeys(Transaction txn, TransportId t,
throws DbException; TransportKeySetId k) throws DbException;
/** /**
* Marks the given contact as verified. * Marks the given contact as verified.
*/ */
void setContactVerified(Transaction txn, ContactId c) throws DbException; void setContactVerified(Transaction txn, ContactId c) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/** /**
* Sets an alias name for the contact or unsets it if alias is null. * Sets an alias name for the contact or unsets it if alias is null.
*/ */
@@ -553,21 +609,36 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException; Collection<MessageId> dependencies) throws DbException;
/** /**
* Sets the reordering window for the given key set and transport in the * Sets the reordering window for the given transport key set in the given
* given rotation period. * time period.
*/ */
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, void setReorderingWindow(Transaction txn, TransportKeySetId k,
long rotationPeriod, long base, byte[] bitmap) throws DbException; TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
/**
* Sets the reordering window for the given handshake key set in the given
* time period.
*/
void setReorderingWindow(Transaction txn, HandshakeKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k) void setTransportKeysActive(Transaction txn, TransportId t,
TransportKeySetId k) throws DbException;
/**
* Stores the given handshake keys, deleting any keys they have replaced.
*/
void updateHandshakeKeys(Transaction txn, Collection<HandshakeKeySet> keys)
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, deleting any keys they have replaced. * Stores the given transport keys, deleting any keys they have replaced.
*/ */
void updateTransportKeys(Transaction txn, Collection<KeySet> keys) void updateTransportKeys(Transaction txn, Collection<TransportKeySet> keys)
throws DbException; throws DbException;
} }

View File

@@ -10,6 +10,4 @@ public interface DatabaseConfig {
File getDatabaseDirectory(); File getDatabaseDirectory();
File getDatabaseKeyDirectory(); File getDatabaseKeyDirectory();
long getMaxSize();
} }

View File

@@ -0,0 +1,24 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.Event;
/**
* A {@link CommitAction} that broadcasts an event.
*/
public class EventAction implements CommitAction {
private final Event event;
EventAction(Event event) {
this.event = event;
}
public Event getEvent() {
return event;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a database operation is attempted for a pending contact that is
* not in the database. This exception may occur due to concurrent updates and
* does not indicate a database error.
*/
public class NoSuchPendingContactException extends DbException {
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a duplicate pending contact is added to the database. This
* exception may occur due to concurrent updates and does not indicate a
* database error.
*/
public class PendingContactExistsException extends DbException {
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.EventExecutor;
/**
* A {@link CommitAction} that submits a task to the {@link EventExecutor}.
*/
public class TaskAction implements CommitAction {
private final Runnable task;
TaskAction(Runnable task) {
this.task = task;
}
public Runnable getTask() {
return task;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -1,13 +1,15 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventExecutor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import static java.util.Collections.emptyList;
/** /**
* A wrapper around a database transaction. Transactions are not thread-safe. * A wrapper around a database transaction. Transactions are not thread-safe.
*/ */
@@ -17,7 +19,7 @@ public class Transaction {
private final Object txn; private final Object txn;
private final boolean readOnly; private final boolean readOnly;
private List<Event> events = null; private List<CommitAction> actions = null;
private boolean committed = false; private boolean committed = false;
public Transaction(Object txn, boolean readOnly) { public Transaction(Object txn, boolean readOnly) {
@@ -42,19 +44,27 @@ public class Transaction {
/** /**
* Attaches an event to be broadcast when the transaction has been * Attaches an event to be broadcast when the transaction has been
* committed. * committed. The event will be broadcast on the {@link EventExecutor}.
*/ */
public void attach(Event e) { public void attach(Event e) {
if (events == null) events = new ArrayList<>(); if (actions == null) actions = new ArrayList<>();
events.add(e); actions.add(new EventAction(e));
} }
/** /**
* Returns any events attached to the transaction. * Attaches a task to be executed when the transaction has been
* committed. The task will be run on the {@link EventExecutor}.
*/ */
public List<Event> getEvents() { public void attach(Runnable r) {
if (events == null) return Collections.emptyList(); if (actions == null) actions = new ArrayList<>();
return events; actions.add(new TaskAction(r));
}
/**
* Returns any actions attached to the transaction.
*/
public List<CommitAction> getActions() {
return actions == null ? emptyList() : actions;
} }
/** /**

View File

@@ -16,7 +16,8 @@ public interface EventBus {
void removeListener(EventListener l); void removeListener(EventListener l);
/** /**
* Notifies all listeners of an event. * Asynchronously notifies all listeners of an event. Listeners are
* notified on the {@link EventExecutor}.
*/ */
void broadcast(Event e); void broadcast(Event e);
} }

View File

@@ -0,0 +1,26 @@
package org.briarproject.bramble.api.event;
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 broadcasting events and running
* tasks that need to run in a defined order with respect to events. Also used
* for annotating methods that should run on the event executor.
* <p>
* The contract of this executor is that tasks are run in the order they're
* submitted, tasks are not run concurrently, and submitting a task will never
* block. Tasks must not block. Tasks submitted during shutdown are discarded.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface EventExecutor {
}

View File

@@ -12,5 +12,6 @@ public interface EventListener {
* Called when an event is broadcast. Implementations of this method must * Called when an event is broadcast. Implementations of this method must
* not block. * not block.
*/ */
@EventExecutor
void eventOccurred(Event e); void eventOccurred(Event e);
} }

View File

@@ -2,8 +2,11 @@ package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/** /**
* A pseudonym for the local user. * A pseudonym for the local user.
*/ */
@@ -12,6 +15,8 @@ import javax.annotation.concurrent.Immutable;
public class LocalAuthor extends Author { public class LocalAuthor extends Author {
private final byte[] privateKey; private final byte[] privateKey;
@Nullable
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created; private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name, public LocalAuthor(AuthorId id, int formatVersion, String name,
@@ -19,6 +24,22 @@ public class LocalAuthor extends Author {
super(id, formatVersion, name, publicKey); super(id, formatVersion, name, publicKey);
this.privateKey = privateKey; this.privateKey = privateKey;
this.created = created; this.created = created;
handshakePublicKey = null;
handshakePrivateKey = null;
}
public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, byte[] handshakePublicKey,
byte[] handshakePrivateKey, long created) {
super(id, formatVersion, name, publicKey);
if (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH) {
throw new IllegalArgumentException();
}
this.privateKey = privateKey;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
} }
/** /**
@@ -28,6 +49,22 @@ public class LocalAuthor extends Author {
return privateKey; return privateKey;
} }
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
/** /**
* Returns the time the pseudonym was created, in milliseconds since the * Returns the time the pseudonym was created, in milliseconds since the
* Unix epoch. * Unix epoch.

View File

@@ -40,8 +40,8 @@ public interface KeyAgreementConstants {
"org.briarproject.bramble.keyagreement/SHARED_SECRET"; "org.briarproject.bramble.keyagreement/SHARED_SECRET";
/** /**
* Label for deriving the master secret. * Label for deriving the master key.
*/ */
String MASTER_SECRET_LABEL = String MASTER_KEY_LABEL =
"org.briarproject.bramble.keyagreement/MASTER_SECRET"; "org.briarproject.bramble.keyagreement/MASTER_SECRET";
} }

View File

@@ -0,0 +1,57 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* Abstract superclass for {@link TransportKeys} and {@link HandshakeKeys}.
*/
@Immutable
@NotNullByDefault
public abstract class AbstractTransportKeys {
private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr;
AbstractTransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1)
throw new IllegalArgumentException();
if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
throw new IllegalArgumentException();
if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
throw new IllegalArgumentException();
this.transportId = transportId;
this.inPrev = inPrev;
this.inCurr = inCurr;
this.inNext = inNext;
this.outCurr = outCurr;
}
public TransportId getTransportId() {
return transportId;
}
public IncomingKeys getPreviousIncomingKeys() {
return inPrev;
}
public IncomingKeys getCurrentIncomingKeys() {
return inCurr;
}
public IncomingKeys getNextIncomingKeys() {
return inNext;
}
public OutgoingKeys getCurrentOutgoingKeys() {
return outCurr;
}
public long getTimePeriod() {
return outCurr.getTimePeriod();
}
}

View File

@@ -0,0 +1,70 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A set of keys for handshaking with a given contact or pending contact over a
* given transport. Unlike a {@link TransportKeySet} these keys do not provide
* forward secrecy.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeySet {
private final HandshakeKeySetId keySetId;
@Nullable
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final HandshakeKeys keys;
public HandshakeKeySet(HandshakeKeySetId keySetId, ContactId contactId,
HandshakeKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.keys = keys;
pendingContactId = null;
}
public HandshakeKeySet(HandshakeKeySetId keySetId,
PendingContactId pendingContactId, HandshakeKeys keys) {
this.keySetId = keySetId;
this.pendingContactId = pendingContactId;
this.keys = keys;
contactId = null;
}
public HandshakeKeySetId getKeySetId() {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public HandshakeKeys getKeys() {
return keys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof HandshakeKeySet &&
keySetId.equals(((HandshakeKeySet) o).keySetId);
}
}

View File

@@ -0,0 +1,36 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for an integer that uniquely identifies a
* {@link HandshakeKeySet set of handshake keys} within the scope of the local
* device.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeySetId {
private final int id;
public HandshakeKeySetId(int id) {
this.id = id;
}
public int getInt() {
return id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return o instanceof HandshakeKeySetId &&
id == ((HandshakeKeySetId) o).id;
}
}

View File

@@ -0,0 +1,36 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* Keys for handshaking with a given contact or pending contact over a given
* transport. Unlike {@link TransportKeys} these keys do not provide forward
* secrecy.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeys extends AbstractTransportKeys {
private final SecretKey rootKey;
private final boolean alice;
public HandshakeKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr,
SecretKey rootKey, boolean alice) {
super(transportId, inPrev, inCurr, inNext, outCurr);
this.rootKey = rootKey;
this.alice = alice;
}
public SecretKey getRootKey() {
return rootKey;
}
public boolean isAlice() {
return alice;
}
}

View File

@@ -1,30 +1,35 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
/** /**
* Contains transport keys for receiving streams from a given contact over a * Contains transport keys for receiving streams from a given contact over a
* given transport in a given rotation period. * given transport in a given time period.
*/ */
@Immutable
@NotNullByDefault
public class IncomingKeys { public class IncomingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod, windowBase; private final long timePeriod, windowBase;
private final byte[] windowBitmap; private final byte[] windowBitmap;
public IncomingKeys(SecretKey tagKey, SecretKey headerKey, public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod) { long timePeriod) {
this(tagKey, headerKey, rotationPeriod, 0, this(tagKey, headerKey, timePeriod, 0,
new byte[REORDERING_WINDOW_SIZE / 8]); new byte[REORDERING_WINDOW_SIZE / 8]);
} }
public IncomingKeys(SecretKey tagKey, SecretKey headerKey, public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, long windowBase, byte[] windowBitmap) { long timePeriod, long windowBase, byte[] windowBitmap) {
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.rotationPeriod = rotationPeriod; this.timePeriod = timePeriod;
this.windowBase = windowBase; this.windowBase = windowBase;
this.windowBitmap = windowBitmap; this.windowBitmap = windowBitmap;
} }
@@ -37,8 +42,8 @@ public class IncomingKeys {
return headerKey; return headerKey;
} }
public long getRotationPeriod() { public long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
public long getWindowBase() { public long getWindowBase() {

View File

@@ -27,14 +27,14 @@ public interface KeyManager {
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams * @param active whether the derived keys can be used for outgoing streams
*/ */
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c, Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
SecretKey master, long timestamp, boolean alice, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException; throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys)
throws DbException; throws DbException;
/** /**

View File

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

View File

@@ -1,27 +1,32 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/** /**
* Contains transport keys for sending streams to a given contact over a given * Contains transport keys for sending streams to a given contact over a given
* transport in a given rotation period. * transport in a given time period.
*/ */
@Immutable
@NotNullByDefault
public class OutgoingKeys { public class OutgoingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod, streamCounter; private final long timePeriod, streamCounter;
private final boolean active; private final boolean active;
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey, public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, boolean active) { long timePeriod, boolean active) {
this(tagKey, headerKey, rotationPeriod, 0, active); this(tagKey, headerKey, timePeriod, 0, active);
} }
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey, public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long rotationPeriod, long streamCounter, boolean active) { long timePeriod, long streamCounter, boolean active) {
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.rotationPeriod = rotationPeriod; this.timePeriod = timePeriod;
this.streamCounter = streamCounter; this.streamCounter = streamCounter;
this.active = active; this.active = active;
} }
@@ -34,8 +39,8 @@ public class OutgoingKeys {
return headerKey; return headerKey;
} }
public long getRotationPeriod() { public long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
public long getStreamCounter() { public long getStreamCounter() {

View File

@@ -2,8 +2,13 @@ package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class StreamContext { public class StreamContext {
private final ContactId contactId; private final ContactId contactId;

View File

@@ -82,30 +82,58 @@ public interface TransportConstants {
int REORDERING_WINDOW_SIZE = 32; int REORDERING_WINDOW_SIZE = 32;
/** /**
* Label for deriving Alice's initial tag key from the master secret. * Label for deriving Alice's initial tag key from the root key in
* rotation mode.
*/ */
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY"; String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
/** /**
* Label for deriving Bob's initial tag key from the master secret. * Label for deriving Bob's initial tag key from the root key in rotation
* mode.
*/ */
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY"; String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
/** /**
* Label for deriving Alice's initial header key from the master secret. * Label for deriving Alice's initial header key from the root key in
* rotation mode.
*/ */
String ALICE_HEADER_LABEL = String ALICE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HEADER_KEY"; "org.briarproject.bramble.transport/ALICE_HEADER_KEY";
/** /**
* Label for deriving Bob's initial header key from the master secret. * Label for deriving Bob's initial header key from the root key in
* rotation mode.
*/ */
String BOB_HEADER_LABEL = String BOB_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HEADER_KEY"; "org.briarproject.bramble.transport/BOB_HEADER_KEY";
/** /**
* Label for deriving the next period's key in key rotation. * Label for deriving the next period's key in rotation mode.
*/ */
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE"; String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
/**
* Label for deriving Alice's tag key from the root key in handshake mode.
*/
String ALICE_HANDSHAKE_TAG_LABEL =
"org.briarproject.bramble.transport/ALICE_HANDSHAKE_TAG_KEY";
/**
* Label for deriving Bob's tag key from the root key in handshake mode.
*/
String BOB_HANDSHAKE_TAG_LABEL =
"org.briarproject.bramble.transport/BOB_HANDSHAKE_TAG_KEY";
/**
* Label for deriving Alice's header key from the root key in handshake
* mode.
*/
String ALICE_HANDSHAKE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HANDSHAKE_HEADER_KEY";
/**
* Label for deriving Bob's header key from the root key in handshake mode.
*/
String BOB_HANDSHAKE_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HANDSHAKE_HEADER_KEY";
} }

View File

@@ -0,0 +1,49 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A set of keys for communicating with a given contact over a given transport.
* Unlike a {@link HandshakeKeySet} these keys provide forward secrecy.
*/
@Immutable
@NotNullByDefault
public class TransportKeySet {
private final TransportKeySetId keySetId;
private final ContactId contactId;
private final TransportKeys keys;
public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
TransportKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.keys = keys;
}
public TransportKeySetId getKeySetId() {
return keySetId;
}
public ContactId getContactId() {
return contactId;
}
public TransportKeys getKeys() {
return keys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof TransportKeySet &&
keySetId.equals(((TransportKeySet) o).keySetId);
}
}

View File

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

View File

@@ -1,52 +1,20 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
/** import javax.annotation.concurrent.Immutable;
* Keys for communicating with a given contact over a given transport.
*/
public class TransportKeys {
private final TransportId transportId; /**
private final IncomingKeys inPrev, inCurr, inNext; * Keys for communicating with a given contact over a given transport. Unlike
private final OutgoingKeys outCurr; * {@link HandshakeKeys} these keys provide forward secrecy.
*/
@Immutable
@NotNullByDefault
public class TransportKeys extends AbstractTransportKeys {
public TransportKeys(TransportId transportId, IncomingKeys inPrev, public TransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) { IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
if (inPrev.getRotationPeriod() != inCurr.getRotationPeriod() - 1) super(transportId, inPrev, inCurr, inNext, outCurr);
throw new IllegalArgumentException();
if (inNext.getRotationPeriod() != inCurr.getRotationPeriod() + 1)
throw new IllegalArgumentException();
if (outCurr.getRotationPeriod() != inCurr.getRotationPeriod())
throw new IllegalArgumentException();
this.transportId = transportId;
this.inPrev = inPrev;
this.inCurr = inCurr;
this.inNext = inNext;
this.outCurr = outCurr;
}
public TransportId getTransportId() {
return transportId;
}
public IncomingKeys getPreviousIncomingKeys() {
return inPrev;
}
public IncomingKeys getCurrentIncomingKeys() {
return inCurr;
}
public IncomingKeys getNextIncomingKeys() {
return inNext;
}
public OutgoingKeys getCurrentOutgoingKeys() {
return outCurr;
}
public long getRotationPeriod() {
return outCurr.getRotationPeriod();
} }
} }

View File

@@ -1,6 +1,10 @@
package org.briarproject.bramble.test; package org.briarproject.bramble.test;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -25,6 +29,7 @@ import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -41,6 +46,7 @@ public class TestUtils {
new AtomicInteger((int) (Math.random() * 1000 * 1000)); new AtomicInteger((int) (Math.random() * 1000 * 1000));
private static final Random random = new Random(); private static final Random random = new Random();
private static final long timestamp = System.currentTimeMillis(); private static final long timestamp = System.currentTimeMillis();
private static final AtomicInteger nextContactId = new AtomicInteger(1);
public static File getTestDirectory() { public static File getTestDirectory() {
int name = nextTestDir.getAndIncrement(); int name = nextTestDir.getAndIncrement();
@@ -140,6 +146,35 @@ public class TestUtils {
return new Message(id, groupId, timestamp, body); return new Message(id, groupId, timestamp, body);
} }
public static PendingContact getPendingContact() {
return getPendingContact(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
}
public static PendingContact getPendingContact(int nameLength) {
PendingContactId id = new PendingContactId(getRandomId());
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String alias = getRandomString(nameLength);
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
}
public static ContactId getContactId() {
return new ContactId(nextContactId.getAndIncrement());
}
public static Contact getContact() {
return getContact(getAuthor(), random.nextBoolean());
}
public static Contact getContact(Author a, boolean verified) {
return getContact(getContactId(), a, verified);
}
public static Contact getContact(ContactId c, Author a, boolean verified) {
return new Contact(c, a, getRandomString(MAX_AUTHOR_NAME_LENGTH),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
}
public static double getMedian(Collection<? extends Number> samples) { public static double getMedian(Collection<? extends Number> samples) {
int size = samples.size(); int size = samples.size();
if (size == 0) throw new IllegalArgumentException(); if (size == 0) throw new IllegalArgumentException();

View File

@@ -39,8 +39,7 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
@Override @Override
public Group createContactGroup(ClientId clientId, int majorVersion, public Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact) { Contact contact, AuthorId local) {
AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId(); AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote); byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, majorVersion, descriptor); return groupFactory.createGroup(clientId, majorVersion, descriptor);

View File

@@ -2,10 +2,11 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactExchangeListener;
import org.briarproject.bramble.api.contact.ContactExchangeTask; import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactExchangeFailedEvent;
import org.briarproject.bramble.api.contact.event.ContactExchangeSucceededEvent;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
@@ -13,6 +14,7 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -63,6 +65,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final RecordReaderFactory recordReaderFactory; private final RecordReaderFactory recordReaderFactory;
private final RecordWriterFactory recordWriterFactory; private final RecordWriterFactory recordWriterFactory;
private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ContactManager contactManager; private final ContactManager contactManager;
@@ -71,18 +74,18 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private final StreamReaderFactory streamReaderFactory; private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory; private final StreamWriterFactory streamWriterFactory;
private volatile ContactExchangeListener listener;
private volatile LocalAuthor localAuthor; private volatile LocalAuthor localAuthor;
private volatile DuplexTransportConnection conn; private volatile DuplexTransportConnection conn;
private volatile TransportId transportId; private volatile TransportId transportId;
private volatile SecretKey masterSecret; private volatile SecretKey masterKey;
private volatile boolean alice; private volatile boolean alice;
@Inject @Inject
ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper, ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper,
RecordReaderFactory recordReaderFactory, RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory, Clock clock, RecordWriterFactory recordWriterFactory, EventBus eventBus,
ConnectionManager connectionManager, ContactManager contactManager, Clock clock, ConnectionManager connectionManager,
ContactManager contactManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory, CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) { StreamWriterFactory streamWriterFactory) {
@@ -90,6 +93,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.recordReaderFactory = recordReaderFactory; this.recordReaderFactory = recordReaderFactory;
this.recordWriterFactory = recordWriterFactory; this.recordWriterFactory = recordWriterFactory;
this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.contactManager = contactManager; this.contactManager = contactManager;
@@ -100,15 +104,13 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
@Override @Override
public void startExchange(ContactExchangeListener listener, public void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
LocalAuthor localAuthor, SecretKey masterSecret,
DuplexTransportConnection conn, TransportId transportId, DuplexTransportConnection conn, TransportId transportId,
boolean alice) { boolean alice) {
this.listener = listener;
this.localAuthor = localAuthor; this.localAuthor = localAuthor;
this.conn = conn; this.conn = conn;
this.transportId = transportId; this.transportId = transportId;
this.masterSecret = masterSecret; this.masterKey = masterKey;
this.alice = alice; this.alice = alice;
start(); start();
} }
@@ -123,8 +125,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
out = conn.getWriter().getOutputStream(); out = conn.getWriter().getOutputStream();
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
listener.contactExchangeFailed();
tryToClose(conn); tryToClose(conn);
eventBus.broadcast(new ContactExchangeFailedEvent());
return; return;
} }
@@ -134,15 +136,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
localProperties = transportPropertyManager.getLocalProperties(); localProperties = transportPropertyManager.getLocalProperties();
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
listener.contactExchangeFailed(); eventBus.broadcast(new ContactExchangeFailedEvent());
tryToClose(conn); tryToClose(conn);
return; return;
} }
// Derive the header keys for the transport streams // Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, masterKey,
masterSecret, new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret, SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterKey,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
// Create the readers // Create the readers
@@ -161,9 +163,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
.createRecordWriter(streamWriter.getOutputStream()); .createRecordWriter(streamWriter.getOutputStream());
// Derive the nonces to be signed // Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret, byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterKey,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret, byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterKey,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
byte[] localNonce = alice ? aliceNonce : bobNonce; byte[] localNonce = alice ? aliceNonce : bobNonce;
byte[] remoteNonce = alice ? bobNonce : aliceNonce; byte[] remoteNonce = alice ? bobNonce : aliceNonce;
@@ -196,7 +198,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
listener.contactExchangeFailed(); eventBus.broadcast(new ContactExchangeFailedEvent());
tryToClose(conn); tryToClose(conn);
return; return;
} }
@@ -204,7 +206,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
// Verify the contact's signature // Verify the contact's signature
if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) { if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) {
LOG.warning("Invalid signature"); LOG.warning("Invalid signature");
listener.contactExchangeFailed(); eventBus.broadcast(new ContactExchangeFailedEvent());
tryToClose(conn); tryToClose(conn);
return; return;
} }
@@ -221,15 +223,17 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
conn); conn);
// Pseudonym exchange succeeded // Pseudonym exchange succeeded
LOG.info("Pseudonym exchange succeeded"); LOG.info("Pseudonym exchange succeeded");
listener.contactExchangeSucceeded(remoteInfo.author); eventBus.broadcast(
new ContactExchangeSucceededEvent(remoteInfo.author));
} catch (ContactExistsException e) { } catch (ContactExistsException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
tryToClose(conn); tryToClose(conn);
listener.duplicateContact(remoteInfo.author); eventBus.broadcast(
new ContactExchangeFailedEvent(remoteInfo.author));
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
tryToClose(conn); tryToClose(conn);
listener.contactExchangeFailed(); eventBus.broadcast(new ContactExchangeFailedEvent());
} }
} }
@@ -289,8 +293,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
throws DbException { throws DbException {
return db.transactionWithResult(false, txn -> { return db.transactionWithResult(false, txn -> {
ContactId contactId = contactManager.addContact(txn, remoteAuthor, ContactId contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), masterSecret, timestamp, alice, masterKey, timestamp, alice, true, true);
true, true);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties); remoteProperties);
return contactId; return contactId;

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -18,7 +17,6 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@@ -30,8 +28,9 @@ import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.contact.PendingContact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
@@ -68,31 +67,31 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException { throws DbException {
ContactId c = db.addContact(txn, a, verified);
keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Transaction txn, Author a, boolean verified)
throws DbException {
ContactId c = db.addContact(txn, a, verified);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Author a, SecretKey rootKey, long timestamp,
boolean alice, boolean verified, boolean active)
throws DbException {
return db.transactionWithResult(false, txn -> return db.transactionWithResult(false, txn ->
addContact(txn, remote, local, master, timestamp, alice, addContact(txn, a, rootKey, timestamp, alice,
verified, active)); verified, active));
} }
@@ -123,8 +122,8 @@ class ContactManagerImpl implements ContactManager {
public PendingContact addRemoteContactRequest(String link, String alias) { public PendingContact addRemoteContactRequest(String link, String alias) {
// TODO replace with real implementation // TODO replace with real implementation
PendingContactId id = new PendingContactId(link.getBytes()); PendingContactId id = new PendingContactId(link.getBytes());
return new PendingContact(id, alias, WAITING_FOR_CONNECTION, return new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH], alias,
System.currentTimeMillis()); WAITING_FOR_CONNECTION, System.currentTimeMillis());
} }
@Override @Override
@@ -144,32 +143,18 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId) public Contact getContact(AuthorId a) throws DbException {
throws DbException { return db.transactionWithResult(true, txn -> getContact(txn, a));
return db.transactionWithResult(true, txn ->
getContact(txn, remoteAuthorId, localAuthorId));
} }
@Override @Override
public Contact getContact(Transaction txn, AuthorId remoteAuthorId, public Contact getContact(Transaction txn, AuthorId a) throws DbException {
AuthorId localAuthorId) throws DbException { return db.getContact(txn, a);
Collection<Contact> contacts =
db.getContactsByAuthorId(txn, remoteAuthorId);
for (Contact c : contacts) {
if (c.getLocalAuthorId().equals(localAuthorId)) {
return c;
}
}
throw new NoSuchContactException();
} }
@Override @Override
public Collection<Contact> getActiveContacts() throws DbException { public Collection<Contact> getContacts() throws DbException {
Collection<Contact> contacts = return db.transactionWithResult(true, db::getContacts);
db.transactionWithResult(true, db::getContacts);
List<Contact> active = new ArrayList<>(contacts.size());
for (Contact c : contacts) if (c.isActive()) active.add(c);
return active;
} }
@Override @Override
@@ -177,12 +162,6 @@ class ContactManagerImpl implements ContactManager {
db.transaction(false, txn -> removeContact(txn, c)); db.transaction(false, txn -> removeContact(txn, c));
} }
@Override
public void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException {
db.setContactActive(txn, c, active);
}
@Override @Override
public void setContactAlias(Transaction txn, ContactId c, public void setContactAlias(Transaction txn, ContactId c,
@Nullable String alias) throws DbException { @Nullable String alias) throws DbException {
@@ -201,16 +180,14 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId, public boolean contactExists(Transaction txn, AuthorId a)
AuthorId localAuthorId) throws DbException { throws DbException {
return db.containsContact(txn, remoteAuthorId, localAuthorId); return db.containsContact(txn, a);
} }
@Override @Override
public boolean contactExists(AuthorId remoteAuthorId, public boolean contactExists(AuthorId a) throws DbException {
AuthorId localAuthorId) throws DbException { return db.transactionWithResult(true, txn -> contactExists(txn, a));
return db.transactionWithResult(true, txn ->
contactExists(txn, remoteAuthorId, localAuthorId));
} }
@Override @Override
@@ -232,12 +209,12 @@ class ContactManagerImpl implements ContactManager {
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.getId().equals(authorId)) if (localAuthor.getId().equals(authorId))
return new AuthorInfo(OURSELVES); return new AuthorInfo(OURSELVES);
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId); if (db.containsContact(txn, authorId)) {
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN); Contact c = db.getContact(txn, authorId);
if (contacts.size() > 1) throw new AssertionError();
Contact c = contacts.iterator().next();
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias()); if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
else return new AuthorInfo(UNVERIFIED, c.getAlias()); else return new AuthorInfo(UNVERIFIED, c.getAlias());
} }
return new AuthorInfo(UNKNOWN);
}
} }

View File

@@ -4,18 +4,22 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.digests.Blake2bDigest; import org.spongycastle.crypto.digests.Blake2bDigest;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
@@ -24,6 +28,9 @@ import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.writeUint16;
import static org.briarproject.bramble.util.ByteUtils.writeUint64;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
class TransportCryptoImpl implements TransportCrypto { class TransportCryptoImpl implements TransportCrypto {
@@ -36,45 +43,44 @@ class TransportCryptoImpl implements TransportCrypto {
@Override @Override
public TransportKeys deriveTransportKeys(TransportId t, public TransportKeys deriveTransportKeys(TransportId t,
SecretKey master, long rotationPeriod, boolean alice, SecretKey rootKey, long timePeriod, boolean weAreAlice,
boolean active) { boolean active) {
// Keys for the previous period are derived from the master secret // Keys for the previous period are derived from the root key
SecretKey inTagPrev = deriveTagKey(master, t, !alice); SecretKey inTagPrev = deriveTagKey(rootKey, t, !weAreAlice);
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice); SecretKey inHeaderPrev = deriveHeaderKey(rootKey, t, !weAreAlice);
SecretKey outTagPrev = deriveTagKey(master, t, alice); SecretKey outTagPrev = deriveTagKey(rootKey, t, weAreAlice);
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice); SecretKey outHeaderPrev = deriveHeaderKey(rootKey, t, weAreAlice);
// Derive the keys for the current and next periods // Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod); SecretKey inTagCurr = rotateKey(inTagPrev, timePeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod); SecretKey inHeaderCurr = rotateKey(inHeaderPrev, timePeriod);
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1); SecretKey inTagNext = rotateKey(inTagCurr, timePeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1); SecretKey inHeaderNext = rotateKey(inHeaderCurr, timePeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod); SecretKey outTagCurr = rotateKey(outTagPrev, timePeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod); SecretKey outHeaderCurr = rotateKey(outHeaderPrev, timePeriod);
// Initialise the reordering windows and stream counters // Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev, IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
rotationPeriod - 1); timePeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr, IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
rotationPeriod); timePeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext, IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
rotationPeriod + 1); timePeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr, OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
rotationPeriod, active); timePeriod, active);
// Collect and return the keys // Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
} }
@Override @Override
public TransportKeys rotateTransportKeys(TransportKeys k, public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
long rotationPeriod) { if (k.getTimePeriod() >= timePeriod) return k;
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys(); IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getRotationPeriod(); long startPeriod = outCurr.getTimePeriod();
boolean active = outCurr.isActive(); boolean active = outCurr.isActive();
// Rotate the keys // Rotate the keys
for (long p = startPeriod + 1; p <= rotationPeriod; p++) { for (long p = startPeriod + 1; p <= timePeriod; p++) {
inPrev = inCurr; inPrev = inCurr;
inCurr = inNext; inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
@@ -89,24 +95,117 @@ class TransportCryptoImpl implements TransportCrypto {
outCurr); outCurr);
} }
private SecretKey rotateKey(SecretKey k, long rotationPeriod) { private SecretKey rotateKey(SecretKey k, long timePeriod) {
byte[] period = new byte[INT_64_BYTES]; byte[] period = new byte[INT_64_BYTES];
ByteUtils.writeUint64(rotationPeriod, period, 0); writeUint64(timePeriod, period, 0);
return crypto.deriveKey(ROTATE_LABEL, k, period); return crypto.deriveKey(ROTATE_LABEL, k, period);
} }
private SecretKey deriveTagKey(SecretKey master, TransportId t, private SecretKey deriveTagKey(SecretKey rootKey, TransportId t,
boolean alice) { boolean keyBelongsToAlice) {
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL; String label = keyBelongsToAlice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
byte[] id = StringUtils.toUtf8(t.getString()); byte[] id = toUtf8(t.getString());
return crypto.deriveKey(label, master, id); return crypto.deriveKey(label, rootKey, id);
} }
private SecretKey deriveHeaderKey(SecretKey master, TransportId t, private SecretKey deriveHeaderKey(SecretKey rootKey, TransportId t,
boolean alice) { boolean keyBelongsToAlice) {
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL; String label = keyBelongsToAlice ? ALICE_HEADER_LABEL :
byte[] id = StringUtils.toUtf8(t.getString()); BOB_HEADER_LABEL;
return crypto.deriveKey(label, master, id); byte[] id = toUtf8(t.getString());
return crypto.deriveKey(label, rootKey, id);
}
@Override
public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean weAreAlice) {
if (timePeriod < 1) throw new IllegalArgumentException();
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod - 1);
IncomingKeys inCurr = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
weAreAlice);
}
private IncomingKeys deriveIncomingHandshakeKeys(TransportId t,
SecretKey rootKey, boolean weAreAlice, long timePeriod) {
SecretKey tag = deriveHandshakeTagKey(t, rootKey, !weAreAlice,
timePeriod);
SecretKey header = deriveHandshakeHeaderKey(t, rootKey, !weAreAlice,
timePeriod);
return new IncomingKeys(tag, header, timePeriod);
}
private OutgoingKeys deriveOutgoingHandshakeKeys(TransportId t,
SecretKey rootKey, boolean weAreAlice, long timePeriod) {
SecretKey tag = deriveHandshakeTagKey(t, rootKey, weAreAlice,
timePeriod);
SecretKey header = deriveHandshakeHeaderKey(t, rootKey, weAreAlice,
timePeriod);
return new OutgoingKeys(tag, header, timePeriod, true);
}
private SecretKey deriveHandshakeTagKey(TransportId t, SecretKey rootKey,
boolean keyBelongsToAlice, long timePeriod) {
String label = keyBelongsToAlice ? ALICE_HANDSHAKE_TAG_LABEL :
BOB_HANDSHAKE_TAG_LABEL;
byte[] id = toUtf8(t.getString());
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
return crypto.deriveKey(label, rootKey, id, period);
}
private SecretKey deriveHandshakeHeaderKey(TransportId t, SecretKey rootKey,
boolean keyBelongsToAlice, long timePeriod) {
String label = keyBelongsToAlice ? ALICE_HANDSHAKE_HEADER_LABEL :
BOB_HANDSHAKE_HEADER_LABEL;
byte[] id = toUtf8(t.getString());
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
return crypto.deriveKey(label, rootKey, id, period);
}
@Override
public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
long elapsed = timePeriod - k.getTimePeriod();
TransportId t = k.getTransportId();
SecretKey rootKey = k.getRootKey();
boolean weAreAlice = k.isAlice();
if (elapsed <= 0) {
// The keys are for the given period or later - don't update them
return k;
} else if (elapsed == 1) {
// The keys are one period old - shift by one period, keeping the
// reordering windows for keys we retain
IncomingKeys inPrev = k.getCurrentIncomingKeys();
IncomingKeys inCurr = k.getNextIncomingKeys();
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else if (elapsed == 2) {
// The keys are two periods old - shift by two periods, keeping
// the reordering windows for keys we retain
IncomingKeys inPrev = k.getNextIncomingKeys();
IncomingKeys inCurr = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else {
// The keys are more than two periods old - derive fresh keys
return deriveHandshakeKeys(t, rootKey, timePeriod, weAreAlice);
}
} }
@Override @Override
@@ -125,14 +224,14 @@ class TransportCryptoImpl implements TransportCrypto {
// The input is the protocol version as a 16-bit integer, followed by // The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer // the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES]; byte[] protocolVersionBytes = new byte[INT_16_BYTES];
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0); writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length); prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES]; byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0); writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length); prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength]; byte[] mac = new byte[macLength];
prf.doFinal(mac, 0); prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC // The output is the first TAG_LENGTH bytes of the MAC
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); arraycopy(mac, 0, tag, 0, TAG_LENGTH);
} }
} }

View File

@@ -2,9 +2,13 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException; import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
@@ -23,8 +27,11 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -33,11 +40,14 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* A low-level interface to the database (DatabaseComponent provides a * A low-level interface to the database ({@link DatabaseComponent} provides a
* high-level interface). Most operations take a transaction argument, which is * high-level interface).
* obtained by calling {@link #startTransaction()}. Every transaction must be * <p/>
* terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or * Most operations take a transaction argument, which is obtained by calling
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown. * {@link #startTransaction()}. Every transaction must be terminated by calling
* either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an
* exception is thrown.
*/ */
@NotNullByDefault @NotNullByDefault
interface Database<T> { interface Database<T> {
@@ -77,11 +87,10 @@ interface Database<T> {
void commitTransaction(T txn) throws DbException; void commitTransaction(T txn) throws DbException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact with the given pseudonym and returns an ID for the
* and returns an ID for the contact. * contact.
*/ */
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified, ContactId addContact(T txn, Author a, boolean verified) throws DbException;
boolean active) throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -95,6 +104,20 @@ interface Database<T> {
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException; throws DbException;
/**
* Stores the given handshake keys for the given contact and returns a
* key set ID.
*/
HandshakeKeySetId addHandshakeKeys(T txn, ContactId c, HandshakeKeys k)
throws DbException;
/**
* Stores the given handshake keys for the given pending contact and
* returns a key set ID.
*/
HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/** /**
* Stores a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -121,6 +144,11 @@ interface Database<T> {
*/ */
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException; void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(T txn, PendingContact p) throws DbException;
/** /**
* Stores a transport. * Stores a transport.
*/ */
@@ -131,17 +159,15 @@ interface Database<T> {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException; throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact.
* local pseudonym.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
boolean containsContact(T txn, AuthorId remote, AuthorId local) boolean containsContact(T txn, AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact.
@@ -171,6 +197,14 @@ interface Database<T> {
*/ */
boolean containsMessage(T txn, MessageId m) throws DbException; boolean containsMessage(T txn, MessageId m) throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(T txn, PendingContactId p)
throws DbException;
/** /**
* Returns true if the database contains the given transport. * Returns true if the database contains the given transport.
* <p/> * <p/>
@@ -215,6 +249,13 @@ interface Database<T> {
*/ */
Contact getContact(T txn, ContactId c) throws DbException; Contact getContact(T txn, ContactId c) throws DbException;
/**
* Returns the contact with the given author ID.
* <p/>
* Read-only.
*/
Contact getContact(T txn, AuthorId a) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/> * <p/>
@@ -222,28 +263,6 @@ interface Database<T> {
*/ */
Collection<Contact> getContacts(T txn) throws DbException; Collection<Contact> getContacts(T txn) throws DbException;
/**
* Returns a possibly empty collection of contacts with the given author ID.
* <p/>
* Read-only.
*/
Collection<Contact> getContactsByAuthorId(T txn, AuthorId remote)
throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/**
* Returns the amount of free storage space available to the database, in
* bytes. This is based on the minimum of the space available on the device
* where the database is stored and the database's configured size.
*/
long getFreeSpace() throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -284,6 +303,14 @@ interface Database<T> {
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g) Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -474,6 +501,13 @@ interface Database<T> {
*/ */
long getNextSendTime(T txn, ContactId c) throws DbException; long getNextSendTime(T txn, ContactId c) throws DbException;
/**
* Returns all pending contacts.
* <p/>
* Read-only.
*/
Collection<PendingContact> getPendingContacts(T txn) throws DbException;
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact and have been requested by the contact, up to the given * given contact and have been requested by the contact, up to the given
@@ -496,13 +530,19 @@ interface Database<T> {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<KeySet> getTransportKeys(T txn, TransportId t) Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
throws DbException;
/**
* Increments the outgoing stream counter for the given handshake keys.
*/
void incrementStreamCounter(T txn, TransportId t, HandshakeKeySetId k)
throws DbException; throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(T txn, TransportId t, KeySetId k) void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -571,6 +611,12 @@ interface Database<T> {
void removeGroupVisibility(T txn, ContactId c, GroupId g) void removeGroupVisibility(T txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -588,6 +634,11 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c, void removeOfferedMessages(T txn, ContactId c,
Collection<MessageId> requested) throws DbException; Collection<MessageId> requested) throws DbException;
/**
* Removes a pending contact (and all associated state) from the database.
*/
void removePendingContact(T txn, PendingContactId p) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -596,7 +647,7 @@ interface Database<T> {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(T txn, TransportId t, KeySetId k) void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -610,12 +661,6 @@ interface Database<T> {
*/ */
void setContactVerified(T txn, ContactId c) throws DbException; void setContactVerified(T txn, ContactId c) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(T txn, ContactId c, boolean active)
throws DbException;
/** /**
* Sets an alias name for a contact. * Sets an alias name for a contact.
*/ */
@@ -641,16 +686,29 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Sets the reordering window for the given key set and transport in the * Sets the state of the given pending contact.
* given rotation period.
*/ */
void setReorderingWindow(T txn, KeySetId k, TransportId t, void setPendingContactState(T txn, PendingContactId p,
long rotationPeriod, long base, byte[] bitmap) throws DbException; PendingContactState state) throws DbException;
/**
* Sets the reordering window for the given transport key set in the given
* time period.
*/
void setReorderingWindow(T txn, TransportKeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException;
/**
* Sets the reordering window for the given handshake key set in the given
* time period.
*/
void setReorderingWindow(T txn, HandshakeKeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(T txn, TransportId t, KeySetId k) void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
throws DbException; throws DbException;
/** /**
@@ -661,8 +719,13 @@ interface Database<T> {
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency) void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency)
throws DbException; throws DbException;
/**
* Updates the given handshake keys.
*/
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
/** /**
* Updates the given transport keys following key rotation. * Updates the given transport keys following key rotation.
*/ */
void updateTransportKeys(T txn, KeySet ks) throws DbException; void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
} }

View File

@@ -2,27 +2,34 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent; import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.CommitAction;
import org.briarproject.bramble.api.db.CommitAction.Visitor;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbCallable; import org.briarproject.bramble.api.db.DbCallable;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.DbRunnable; import org.briarproject.bramble.api.db.DbRunnable;
import org.briarproject.bramble.api.db.EventAction;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.db.NullableDbCallable; import org.briarproject.bramble.api.db.NullableDbCallable;
import org.briarproject.bramble.api.db.PendingContactExistsException;
import org.briarproject.bramble.api.db.TaskAction;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -55,8 +62,11 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.KeySet; import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.ArrayList; import java.util.ArrayList;
@@ -64,6 +74,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -92,25 +103,29 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private final Database<T> db; private final Database<T> db;
private final Class<T> txnClass; private final Class<T> txnClass;
private final EventBus eventBus; private final EventBus eventBus;
private final ShutdownManager shutdown; private final Executor eventExecutor;
private final ShutdownManager shutdownManager;
private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean closed = new AtomicBoolean(false);
private final ReentrantReadWriteLock lock = private final ReentrantReadWriteLock lock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final Visitor visitor = new CommitActionVisitor();
@Inject @Inject
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus, DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
ShutdownManager shutdown) { @EventExecutor Executor eventExecutor,
ShutdownManager shutdownManager) {
this.db = db; this.db = db;
this.txnClass = txnClass; this.txnClass = txnClass;
this.eventBus = eventBus; this.eventBus = eventBus;
this.shutdown = shutdown; this.eventExecutor = eventExecutor;
this.shutdownManager = shutdownManager;
} }
@Override @Override
public boolean open(SecretKey key, @Nullable MigrationListener listener) public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException { throws DbException {
boolean reopened = db.open(key, listener); boolean reopened = db.open(key, listener);
shutdown.addShutdownHook(() -> { shutdownManager.addShutdownHook(() -> {
try { try {
close(); close();
} catch (DbException e) { } catch (DbException e) {
@@ -160,13 +175,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void endTransaction(Transaction transaction) { public void endTransaction(Transaction transaction) {
try { try {
T txn = txnClass.cast(transaction.unbox()); T txn = txnClass.cast(transaction.unbox());
if (!transaction.isCommitted()) db.abortTransaction(txn); if (transaction.isCommitted()) {
for (CommitAction a : transaction.getActions())
a.accept(visitor);
} else {
db.abortTransaction(txn);
}
} finally { } finally {
if (transaction.isReadOnly()) lock.readLock().unlock(); if (transaction.isReadOnly()) lock.readLock().unlock();
else lock.writeLock().unlock(); else lock.writeLock().unlock();
} }
if (transaction.isCommitted())
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
} }
@Override @Override
@@ -214,20 +232,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public ContactId addContact(Transaction transaction, Author remote, public ContactId addContact(Transaction transaction, Author a,
AuthorId local, boolean verified, boolean active) boolean verified) throws DbException {
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local)) if (db.containsLocalAuthor(txn, a.getId()))
throw new NoSuchLocalAuthorException();
if (db.containsLocalAuthor(txn, remote.getId()))
throw new ContactExistsException(); throw new ContactExistsException();
if (db.containsContact(txn, remote.getId(), local)) if (db.containsContact(txn, a.getId()))
throw new ContactExistsException(); throw new ContactExistsException();
ContactId c = db.addContact(txn, remote, local, verified, active); ContactId c = db.addContact(txn, a, verified);
transaction.attach(new ContactAddedEvent(c, active)); transaction.attach(new ContactAddedEvent(c));
if (active) transaction.attach(new ContactStatusChangedEvent(c, true));
return c; return c;
} }
@@ -241,6 +255,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
} }
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
ContactId c, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, c, k);
}
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
PendingContactId p, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, p, k);
}
@Override @Override
public void addLocalAuthor(Transaction transaction, LocalAuthor a) public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException { throws DbException {
@@ -269,6 +307,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.mergeMessageMetadata(txn, m.getId(), meta); db.mergeMessageMetadata(txn, m.getId(), meta);
} }
@Override
public void addPendingContact(Transaction transaction, PendingContact p)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (db.containsPendingContact(txn, p.getId()))
throw new PendingContactExistsException();
db.addPendingContact(txn, p);
}
@Override @Override
public void addTransport(Transaction transaction, TransportId t, public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException { int maxLatency) throws DbException {
@@ -279,8 +327,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public KeySetId addTransportKeys(Transaction transaction, ContactId c, public TransportKeySetId addTransportKeys(Transaction transaction,
TransportKeys k) throws DbException { ContactId c, TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -291,12 +339,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public boolean containsContact(Transaction transaction, AuthorId remote, public boolean containsContact(Transaction transaction, AuthorId a)
AuthorId local) throws DbException { throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local)) return db.containsContact(txn, a);
throw new NoSuchLocalAuthorException();
return db.containsContact(txn, remote, local);
} }
@Override @Override
@@ -313,6 +359,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.containsLocalAuthor(txn, local); return db.containsLocalAuthor(txn, local);
} }
@Override
public boolean containsPendingContact(Transaction transaction,
PendingContactId p) throws DbException {
T txn = unbox(transaction);
return db.containsPendingContact(txn, p);
}
@Override @Override
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
@@ -429,6 +482,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContact(txn, c); return db.getContact(txn, c);
} }
@Override
public Contact getContact(Transaction transaction, AuthorId a)
throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, a))
throw new NoSuchContactException();
return db.getContact(txn, a);
}
@Override @Override
public Collection<Contact> getContacts(Transaction transaction) public Collection<Contact> getContacts(Transaction transaction)
throws DbException { throws DbException {
@@ -436,22 +498,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContacts(txn); return db.getContacts(txn);
} }
@Override
public Collection<Contact> getContactsByAuthorId(Transaction transaction,
AuthorId remote) throws DbException {
T txn = unbox(transaction);
return db.getContactsByAuthorId(txn, remote);
}
@Override
public Collection<ContactId> getContacts(Transaction transaction,
AuthorId a) throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
return db.getContacts(txn, a);
}
@Override @Override
public Group getGroup(Transaction transaction, GroupId g) public Group getGroup(Transaction transaction, GroupId g)
throws DbException { throws DbException {
@@ -486,6 +532,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getGroupVisibility(txn, c, g); return db.getGroupVisibility(txn, c, g);
} }
@Override
public Collection<HandshakeKeySet> getHandshakeKeys(Transaction transaction,
TransportId t) throws DbException {
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
return db.getHandshakeKeys(txn, t);
}
@Override @Override
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a) public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -643,6 +698,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getNextSendTime(txn, c); return db.getNextSendTime(txn, c);
} }
@Override
public Collection<PendingContact> getPendingContacts(
Transaction transaction) throws DbException {
T txn = unbox(transaction);
return db.getPendingContacts(txn);
}
@Override @Override
public Settings getSettings(Transaction transaction, String namespace) public Settings getSettings(Transaction transaction, String namespace)
throws DbException { throws DbException {
@@ -651,7 +713,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public Collection<KeySet> getTransportKeys(Transaction transaction, public Collection<TransportKeySet> getTransportKeys(Transaction transaction,
TransportId t) throws DbException { TransportId t) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -661,7 +723,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void incrementStreamCounter(Transaction transaction, TransportId t, public void incrementStreamCounter(Transaction transaction, TransportId t,
KeySetId k) throws DbException { HandshakeKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.incrementStreamCounter(txn, t, k);
}
@Override
public void incrementStreamCounter(Transaction transaction, TransportId t,
TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -810,6 +882,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
} }
@Override
public void removeHandshakeKeys(Transaction transaction,
TransportId t, HandshakeKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.removeHandshakeKeys(txn, t, k);
}
@Override @Override
public void removeLocalAuthor(Transaction transaction, AuthorId a) public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -832,6 +914,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.removeMessage(txn, m); db.removeMessage(txn, m);
} }
@Override
public void removePendingContact(Transaction transaction,
PendingContactId p) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
db.removePendingContact(txn, p);
}
@Override @Override
public void removeTransport(Transaction transaction, TransportId t) public void removeTransport(Transaction transaction, TransportId t)
throws DbException { throws DbException {
@@ -844,7 +936,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void removeTransportKeys(Transaction transaction, public void removeTransportKeys(Transaction transaction,
TransportId t, KeySetId k) throws DbException { TransportId t, TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -863,17 +955,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new ContactVerifiedEvent(c)); transaction.attach(new ContactVerifiedEvent(c));
} }
@Override
public void setContactActive(Transaction transaction, ContactId c,
boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setContactActive(txn, c, active);
transaction.attach(new ContactStatusChangedEvent(c, active));
}
@Override @Override
public void setContactAlias(Transaction transaction, ContactId c, public void setContactAlias(Transaction transaction, ContactId c,
@Nullable String alias) throws DbException { @Nullable String alias) throws DbException {
@@ -943,19 +1024,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void setReorderingWindow(Transaction transaction, KeySetId k, public void setReorderingWindow(Transaction transaction,
TransportId t, long rotationPeriod, long base, byte[] bitmap) TransportKeySetId k, TransportId t, long timePeriod, long base,
throws DbException { byte[] bitmap) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
throw new NoSuchTransportException(); throw new NoSuchTransportException();
db.setReorderingWindow(txn, k, t, rotationPeriod, base, bitmap); db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
}
@Override
public void setReorderingWindow(Transaction transaction,
HandshakeKeySetId k, TransportId t, long timePeriod, long base,
byte[] bitmap) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
} }
@Override @Override
public void setTransportKeysActive(Transaction transaction, TransportId t, public void setTransportKeysActive(Transaction transaction, TransportId t,
KeySetId k) throws DbException { TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -964,14 +1056,39 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void updateTransportKeys(Transaction transaction, public void updateHandshakeKeys(Transaction transaction,
Collection<KeySet> keys) throws DbException { Collection<HandshakeKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
for (KeySet ks : keys) { for (HandshakeKeySet ks : keys) {
TransportId t = ks.getTransportKeys().getTransportId(); TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t))
db.updateHandshakeKeys(txn, ks);
}
}
@Override
public void updateTransportKeys(Transaction transaction,
Collection<TransportKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
for (TransportKeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t)) if (db.containsTransport(txn, t))
db.updateTransportKeys(txn, ks); db.updateTransportKeys(txn, ks);
} }
} }
private class CommitActionVisitor implements Visitor {
@Override
public void visit(EventAction a) {
eventBus.broadcast(a.getEvent());
}
@Override
public void visit(TaskAction a) {
eventExecutor.execute(a.getTask());
}
}
} }

View File

@@ -3,11 +3,13 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import java.sql.Connection; import java.sql.Connection;
import java.util.concurrent.Executor;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -27,8 +29,9 @@ public class DatabaseModule {
@Provides @Provides
@Singleton @Singleton
DatabaseComponent provideDatabaseComponent(Database<Connection> db, DatabaseComponent provideDatabaseComponent(Database<Connection> db,
EventBus eventBus, ShutdownManager shutdown) { EventBus eventBus, @EventExecutor Executor eventExecutor,
ShutdownManager shutdownManager) {
return new DatabaseComponentImpl<>(db, Connection.class, eventBus, return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
shutdown); eventExecutor, shutdownManager);
} }
} }

View File

@@ -76,30 +76,6 @@ class H2Database extends JdbcDatabase {
} }
} }
@Override
public long getFreeSpace() {
File dir = config.getDatabaseDirectory();
long maxSize = config.getMaxSize();
long free = dir.getFreeSpace();
long used = getDiskSpace(dir);
long quota = maxSize - used;
return Math.min(free, quota);
}
private long getDiskSpace(File f) {
if (f.isDirectory()) {
long total = 0;
File[] children = f.listFiles();
if (children != null)
for (File child : children) total += getDiskSpace(child);
return total;
} else if (f.isFile()) {
return f.length();
} else {
return 0;
}
}
@Override @Override
protected Connection createConnection() throws SQLException { protected Connection createConnection() throws SQLException {
SecretKey key = this.key; SecretKey key = this.key;

View File

@@ -86,30 +86,6 @@ class HyperSqlDatabase extends JdbcDatabase {
} }
} }
@Override
public long getFreeSpace() {
File dir = config.getDatabaseDirectory();
long maxSize = config.getMaxSize();
long free = dir.getFreeSpace();
long used = getDiskSpace(dir);
long quota = maxSize - used;
return Math.min(free, quota);
}
private long getDiskSpace(File f) {
if (f.isDirectory()) {
long total = 0;
File[] children = f.listFiles();
if (children != null)
for (File child : children) total += getDiskSpace(child);
return total;
} else if (f.isFile()) {
return f.length();
} else {
return 0;
}
}
@Override @Override
protected Connection createConnection() throws SQLException { protected Connection createConnection() throws SQLException {
SecretKey key = this.key; SecretKey key = this.key;

View File

@@ -17,7 +17,7 @@ class Migration40_41 implements Migration<Connection> {
private final DatabaseTypes dbTypes; private final DatabaseTypes dbTypes;
public Migration40_41(DatabaseTypes databaseTypes) { Migration40_41(DatabaseTypes databaseTypes) {
this.dbTypes = databaseTypes; this.dbTypes = databaseTypes;
} }

View File

@@ -0,0 +1,97 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration41_42 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration41_42.class.getName());
private final DatabaseTypes dbTypes;
Migration41_42(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 41;
}
@Override
public int getEndVersion() {
return 42;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE outgoingKeys"
+ " ALTER COLUMN rotationPeriod"
+ " RENAME TO timePeriod");
s.execute("ALTER TABLE incomingKeys"
+ " ALTER COLUMN rotationPeriod"
+ " RENAME TO timePeriod");
s.execute("ALTER TABLE incomingKeys"
+ " DROP COLUMN contactId");
s.execute(dbTypes.replaceTypes("CREATE TABLE pendingContacts"
+ " (pendingContactId _HASH NOT NULL,"
+ " publicKey _BINARY NOT NULL,"
+ " alias _STRING NOT NULL,"
+ " state INT NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " PRIMARY KEY (pendingContactId))"));
s.execute(dbTypes.replaceTypes("CREATE TABLE outgoingHandshakeKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER,"
+ " timePeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if contact is pending
+ " pendingContactId _HASH," // Null if not pending
+ " rootKey _SECRET NOT NULL,"
+ " alice BOOLEAN NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
+ " UNIQUE (keySetId),"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (pendingContactId)"
+ " REFERENCES pendingContacts (pendingContactId)"
+ " ON DELETE CASCADE)"));
s.execute(dbTypes.replaceTypes("CREATE TABLE incomingHandshakeKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId INT NOT NULL,"
+ " timePeriod BIGINT NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " base BIGINT NOT NULL,"
+ " bitmap _BINARY NOT NULL,"
+ " periodOffset INT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId, periodOffset),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (keySetId)"
+ " REFERENCES outgoingHandshakeKeys (keySetId)"
+ " ON DELETE CASCADE)"));
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -0,0 +1,52 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration42_43 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration42_43.class.getName());
private final DatabaseTypes dbTypes;
Migration42_43(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 42;
}
@Override
public int getEndVersion() {
return 43;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute(dbTypes.replaceTypes("ALTER TABLE localAuthors"
+ " ADD COLUMN handshakePublicKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE localAuthors"
+ " ADD COLUMN handshakePrivateKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE contacts"
+ " ADD COLUMN handshakePublicKey _BINARY"));
s.execute("ALTER TABLE contacts"
+ " DROP COLUMN active");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -0,0 +1,40 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration43_44 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration43_44.class.getName());
@Override
public int getStartVersion() {
return 43;
}
@Override
public int getEndVersion() {
return 44;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE contacts"
+ " DROP COLUMN localAuthorId");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -0,0 +1,32 @@
package org.briarproject.bramble.event;
import org.briarproject.bramble.api.event.EventExecutor;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
/**
* Default implementation of {@link EventExecutor} that uses a dedicated thread
* to notify listeners of events. Applications may prefer to supply an
* implementation that uses an existing thread, such as the UI thread.
*/
@Module
public class DefaultEventExecutorModule {
@Provides
@Singleton
@EventExecutor
Executor provideEventExecutor() {
return newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
}
}

View File

@@ -2,13 +2,16 @@ package org.briarproject.bramble.event;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -16,6 +19,12 @@ class EventBusImpl implements EventBus {
private final Collection<EventListener> listeners = private final Collection<EventListener> listeners =
new CopyOnWriteArrayList<>(); new CopyOnWriteArrayList<>();
private final Executor eventExecutor;
@Inject
EventBusImpl(@EventExecutor Executor eventExecutor) {
this.eventExecutor = eventExecutor;
}
@Override @Override
public void addListener(EventListener l) { public void addListener(EventListener l) {
@@ -29,6 +38,8 @@ class EventBusImpl implements EventBus {
@Override @Override
public void broadcast(Event e) { public void broadcast(Event e) {
eventExecutor.execute(() -> {
for (EventListener l : listeners) l.eventOccurred(e); for (EventListener l : listeners) l.eventOccurred(e);
});
} }
} }

View File

@@ -12,7 +12,7 @@ public class EventModule {
@Provides @Provides
@Singleton @Singleton
EventBus provideEventBus() { EventBus provideEventBus(EventBusImpl eventBus) {
return new EventBusImpl(); return eventBus;
} }
} }

View File

@@ -14,7 +14,7 @@ import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
@@ -90,7 +90,7 @@ class KeyAgreementProtocol {
/** /**
* Perform the BQP protocol. * Perform the BQP protocol.
* *
* @return the negotiated master secret. * @return the negotiated master key.
* @throws AbortException when the protocol may have been tampered with. * @throws AbortException when the protocol may have been tampered with.
* @throws IOException for all other other connection errors. * @throws IOException for all other other connection errors.
*/ */
@@ -115,7 +115,7 @@ class KeyAgreementProtocol {
receiveConfirm(s, theirPublicKey); receiveConfirm(s, theirPublicKey);
sendConfirm(s, theirPublicKey); sendConfirm(s, theirPublicKey);
} }
return crypto.deriveKey(MASTER_SECRET_LABEL, s); return crypto.deriveKey(MASTER_KEY_LABEL, s);
} catch (AbortException e) { } catch (AbortException e) {
sendAbort(e.getCause() != null); sendAbort(e.getCause() != null);
throw e; throw e;

View File

@@ -114,9 +114,9 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
keyAgreementCrypto, payloadEncoder, transport, remotePayload, keyAgreementCrypto, payloadEncoder, transport, remotePayload,
localPayload, localKeyPair, alice); localPayload, localKeyPair, alice);
try { try {
SecretKey master = protocol.perform(); SecretKey masterKey = protocol.perform();
KeyAgreementResult result = KeyAgreementResult result =
new KeyAgreementResult(master, transport.getConnection(), new KeyAgreementResult(masterKey, transport.getConnection(),
transport.getTransportId(), alice); transport.getTransportId(), alice);
LOG.info("Finished BQP protocol"); LOG.info("Finished BQP protocol");
// Broadcast result to caller // Broadcast result to caller

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
@@ -80,12 +80,10 @@ class Poller implements EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactStatusChangedEvent) { if (e instanceof ContactAddedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e; ContactAddedEvent c = (ContactAddedEvent) e;
if (c.isActive()) {
// Connect to the newly activated contact // Connect to the newly activated contact
connectToContact(c.getContactId()); connectToContact(c.getContactId());
}
} else if (e instanceof ConnectionClosedEvent) { } else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e; ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased // Reschedule polling, the polling interval may have decreased

View File

@@ -17,16 +17,24 @@ public interface CircumventionProvider {
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"}; String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"};
/** /**
* Countries where vanilla bridge connection are likely to work. * Countries where obfs4 bridge connection are likely to work.
* Should be a subset of {@link #BLOCKED}. * Should be a subset of {@link #BLOCKED}.
*/ */
String[] BRIDGES = { "EG", "BY", "TR", "SY", "VE" }; String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" };
/**
* Countries where obfs4 bridges won't work and meek is needed.
* Should be a subset of {@link #BRIDGES}.
*/
String[] NEEDS_MEEK = {"CN", "IR"};
boolean isTorProbablyBlocked(String countryCode); boolean isTorProbablyBlocked(String countryCode);
boolean doBridgesWork(String countryCode); boolean doBridgesWork(String countryCode);
boolean needsMeek(String countryCode);
@IoExecutor @IoExecutor
List<String> getBridges(); List<String> getBridges(boolean meek);
} }

View File

@@ -22,6 +22,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
new HashSet<>(asList(BLOCKED)); new HashSet<>(asList(BLOCKED));
private static final Set<String> BRIDGES_WORK_IN_COUNTRIES = private static final Set<String> BRIDGES_WORK_IN_COUNTRIES =
new HashSet<>(asList(BRIDGES)); new HashSet<>(asList(BRIDGES));
private static final Set<String> BRIDGES_NEED_MEEK =
new HashSet<>(asList(NEEDS_MEEK));
@Nullable @Nullable
private volatile List<String> bridges = null; private volatile List<String> bridges = null;
@@ -40,9 +42,14 @@ class CircumventionProviderImpl implements CircumventionProvider {
return BRIDGES_WORK_IN_COUNTRIES.contains(countryCode); return BRIDGES_WORK_IN_COUNTRIES.contains(countryCode);
} }
@Override
public boolean needsMeek(String countryCode) {
return BRIDGES_NEED_MEEK.contains(countryCode);
}
@Override @Override
@IoExecutor @IoExecutor
public List<String> getBridges() { public List<String> getBridges(boolean useMeek) {
List<String> bridges = this.bridges; List<String> bridges = this.bridges;
if (bridges != null) return new ArrayList<>(bridges); if (bridges != null) return new ArrayList<>(bridges);
@@ -53,6 +60,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
bridges = new ArrayList<>(); bridges = new ArrayList<>();
while (scanner.hasNextLine()) { while (scanner.hasNextLine()) {
String line = scanner.nextLine(); String line = scanner.nextLine();
boolean isMeekBridge = line.startsWith("Bridge meek");
if (useMeek && !isMeekBridge || !useMeek && isMeekBridge) continue;
if (!line.startsWith("#")) bridges.add(line); if (!line.startsWith("#")) bridges.add(line);
} }
scanner.close(); scanner.close();

View File

@@ -470,13 +470,19 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!enable) callback.transportDisabled(); if (!enable) callback.transportDisabled();
} }
private void enableBridges(boolean enable) throws IOException { private void enableBridges(boolean enable, boolean needsMeek)
throws IOException {
if (enable) { if (enable) {
Collection<String> conf = new ArrayList<>(); Collection<String> conf = new ArrayList<>();
conf.add("UseBridges 1"); conf.add("UseBridges 1");
if (needsMeek) {
conf.add("ClientTransportPlugin meek_lite exec " +
obfs4File.getAbsolutePath());
} else {
conf.add("ClientTransportPlugin obfs4 exec " + conf.add("ClientTransportPlugin obfs4 exec " +
obfs4File.getAbsolutePath()); obfs4File.getAbsolutePath());
conf.addAll(circumventionProvider.getBridges()); }
conf.addAll(circumventionProvider.getBridges(needsMeek));
controlConnection.setConf(conf); controlConnection.setConf(conf);
} else { } else {
controlConnection.setConf("UseBridges", "0"); controlConnection.setConf("UseBridges", "0");
@@ -716,12 +722,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
enableNetwork(false); enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
(automatic && bridgesWork)) { (automatic && bridgesWork)) {
LOG.info("Enabling network, using bridges"); if (circumventionProvider.needsMeek(country)) {
enableBridges(true); LOG.info("Enabling network, using meek bridges");
enableBridges(true, true);
} else {
LOG.info("Enabling network, using obfs4 bridges");
enableBridges(true, false);
}
enableNetwork(true); enableNetwork(true);
} else { } else {
LOG.info("Enabling network, not using bridges"); LOG.info("Enabling network, not using bridges");
enableBridges(false); enableBridges(false, false);
enableNetwork(true); enableNetwork(true);
} }
if (online && wifi && charging) { if (online && wifi && charging) {

View File

@@ -13,6 +13,8 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -44,6 +46,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final ClientVersioningManager clientVersioningManager; private final ClientVersioningManager clientVersioningManager;
private final MetadataParser metadataParser; private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
@@ -53,11 +56,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Inject @Inject
TransportPropertyManagerImpl(DatabaseComponent db, TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, ClientHelper clientHelper,
IdentityManager identityManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.clientVersioningManager = clientVersioningManager; this.clientVersioningManager = clientVersioningManager;
this.metadataParser = metadataParser; this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
@@ -77,7 +82,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
db.addGroup(txn, g); db.addGroup(txn, g);
// Apply the client's visibility to the contact group // Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn, Visibility client = clientVersioningManager.getClientVisibility(txn,
@@ -93,14 +98,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn)));
} }
@Override @Override
public void onClientVisibilityChanging(Transaction txn, Contact c, public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException { Visibility v) throws DbException {
// Apply the client's visibility to the contact group // Apply the client's visibility to the contact group
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
db.setGroupVisibility(txn, c.getId(), g.getId(), v); db.setGroupVisibility(txn, c.getId(), g.getId(), v);
} }
@@ -132,7 +137,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addRemoteProperties(Transaction txn, ContactId c, public void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException { Map<TransportId, TransportProperties> props) throws DbException {
Group g = getContactGroup(db.getContact(txn, c)); Group g = getContactGroup(db.getContact(txn, c), getLocalAuthorId(txn));
for (Entry<TransportId, TransportProperties> e : props.entrySet()) { for (Entry<TransportId, TransportProperties> e : props.entrySet()) {
storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0, storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0,
false, false); false, false);
@@ -191,17 +196,16 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
TransportId t) throws DbException { TransportId t) throws DbException {
return db.transactionWithResult(true, txn -> { return db.transactionWithResult(true, txn -> {
Map<ContactId, TransportProperties> remote = new HashMap<>(); Map<ContactId, TransportProperties> remote = new HashMap<>();
AuthorId local = getLocalAuthorId(txn);
for (Contact c : db.getContacts(txn)) for (Contact c : db.getContacts(txn))
remote.put(c.getId(), getRemoteProperties(txn, c, t)); remote.put(c.getId(), getRemoteProperties(txn, c, local, t));
return remote; return remote;
}); });
} }
private TransportProperties getRemoteProperties(Transaction txn, Contact c, private TransportProperties getRemoteProperties(Transaction txn, Contact c,
TransportId t) throws DbException { AuthorId local, TransportId t) throws DbException {
// Don't return properties for inactive contacts Group g = getContactGroup(c, local);
if (!c.isActive()) return new TransportProperties();
Group g = getContactGroup(c);
try { try {
// Find the latest remote update // Find the latest remote update
LatestUpdate latest = findLatest(txn, g.getId(), t, false); LatestUpdate latest = findLatest(txn, g.getId(), t, false);
@@ -218,8 +222,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public TransportProperties getRemoteProperties(ContactId c, TransportId t) public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException { throws DbException {
return db.transactionWithResult(true, txn -> return db.transactionWithResult(true, txn -> {
getRemoteProperties(txn, db.getContact(txn, c), t)); Contact contact = db.getContact(txn ,c);
AuthorId local = getLocalAuthorId(txn);
return getRemoteProperties(txn, contact, local, t);
});
} }
@Override @Override
@@ -252,8 +259,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
if (latest != null) if (latest != null)
db.removeMessage(txn, latest.messageId); db.removeMessage(txn, latest.messageId);
// Store the merged properties in each contact's group // Store the merged properties in each contact's group
AuthorId localAuthorId = getLocalAuthorId(txn);
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c); Group g = getContactGroup(c, localAuthorId);
latest = findLatest(txn, g.getId(), t, true); latest = findLatest(txn, g.getId(), t, true);
version = latest == null ? 1 : latest.version + 1; version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), t, merged, version, storeMessage(txn, g.getId(), t, merged, version,
@@ -269,9 +277,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
} }
private Group getContactGroup(Contact c) { private AuthorId getLocalAuthorId(Transaction txn) throws DbException {
return identityManager.getLocalAuthor(txn).getId();
}
private Group getContactGroup(Contact c, AuthorId local) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c); MAJOR_VERSION, c, local);
} }
private void storeMessage(Transaction txn, GroupId g, TransportId t, private void storeMessage(Transaction txn, GroupId g, TransportId t,

View File

@@ -1,9 +1,7 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -19,8 +17,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -46,7 +44,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final TransportKeyManagerFactory transportKeyManagerFactory; private final TransportKeyManagerFactory transportKeyManagerFactory;
private final Map<ContactId, Boolean> activeContacts;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@@ -58,8 +55,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory; this.transportKeyManagerFactory = transportKeyManagerFactory;
// Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<>();
managers = new ConcurrentHashMap<>(); managers = new ConcurrentHashMap<>();
} }
@@ -73,8 +68,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
transports.put(f.getId(), f.getMaxLatency()); transports.put(f.getId(), f.getMaxLatency());
try { try {
db.transaction(false, txn -> { db.transaction(false, txn -> {
for (Contact c : db.getContacts(txn))
if (c.isActive()) activeContacts.put(c.getId(), true);
for (Entry<TransportId, Integer> e : transports.entrySet()) for (Entry<TransportId, Integer> e : transports.entrySet())
db.addTransport(txn, e.getKey(), e.getValue()); db.addTransport(txn, e.getKey(), e.getValue());
for (Entry<TransportId, Integer> e : transports.entrySet()) { for (Entry<TransportId, Integer> e : transports.entrySet()) {
@@ -95,22 +88,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c, public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
SecretKey master, long timestamp, boolean alice, boolean active) ContactId c, SecretKey rootKey, long timestamp, boolean alice,
throws DbException { boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, TransportKeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = e.getValue();
ids.put(t, m.addContact(txn, c, master, timestamp, alice, active)); ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
} }
return ids; return ids;
} }
@Override @Override
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys) public void activateKeys(Transaction txn, Map<TransportId,
throws DbException { TransportKeySetId> keys) throws DbException {
for (Entry<TransportId, KeySetId> e : keys.entrySet()) { for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
if (m == null) { if (m == null) {
@@ -130,8 +123,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Override @Override
public StreamContext getStreamContext(ContactId c, TransportId t) public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException { throws DbException {
// Don't allow outgoing streams to inactive contacts
if (!activeContacts.containsKey(c)) return null;
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
if (m == null) { if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t); if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
@@ -157,15 +148,10 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) { if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId()); removeContact(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) activeContacts.put(c.getContactId(), true);
else activeContacts.remove(c.getContactId());
} }
} }
private void removeContact(ContactId c) { private void removeContact(ContactId c) {
activeContacts.remove(c);
dbExecutor.execute(() -> { dbExecutor.execute(() -> {
for (TransportKeyManager m : managers.values()) m.removeContact(c); for (TransportKeyManager m : managers.values()) m.removeContact(c);
}); });

View File

@@ -11,18 +11,18 @@ import javax.annotation.concurrent.NotThreadSafe;
class MutableIncomingKeys { class MutableIncomingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod; private final long timePeriod;
private final ReorderingWindow window; private final ReorderingWindow window;
MutableIncomingKeys(IncomingKeys in) { MutableIncomingKeys(IncomingKeys in) {
tagKey = in.getTagKey(); tagKey = in.getTagKey();
headerKey = in.getHeaderKey(); headerKey = in.getHeaderKey();
rotationPeriod = in.getRotationPeriod(); timePeriod = in.getTimePeriod();
window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap()); window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap());
} }
IncomingKeys snapshot() { IncomingKeys snapshot() {
return new IncomingKeys(tagKey, headerKey, rotationPeriod, return new IncomingKeys(tagKey, headerKey, timePeriod,
window.getBase(), window.getBitmap()); window.getBase(), window.getBitmap());
} }
@@ -34,8 +34,8 @@ class MutableIncomingKeys {
return headerKey; return headerKey;
} }
long getRotationPeriod() { long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
ReorderingWindow getWindow() { ReorderingWindow getWindow() {

View File

@@ -1,22 +1,22 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.transport.KeySetId; import org.briarproject.bramble.api.transport.TransportKeySetId;
class MutableKeySet { class MutableKeySet {
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final ContactId contactId; private final ContactId contactId;
private final MutableTransportKeys transportKeys; private final MutableTransportKeys transportKeys;
MutableKeySet(KeySetId keySetId, ContactId contactId, MutableKeySet(TransportKeySetId keySetId, ContactId contactId,
MutableTransportKeys transportKeys) { MutableTransportKeys transportKeys) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
this.transportKeys = transportKeys; this.transportKeys = transportKeys;
} }
KeySetId getKeySetId() { TransportKeySetId getKeySetId() {
return keySetId; return keySetId;
} }

View File

@@ -11,20 +11,20 @@ import javax.annotation.concurrent.NotThreadSafe;
class MutableOutgoingKeys { class MutableOutgoingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long rotationPeriod; private final long timePeriod;
private long streamCounter; private long streamCounter;
private boolean active; private boolean active;
MutableOutgoingKeys(OutgoingKeys out) { MutableOutgoingKeys(OutgoingKeys out) {
tagKey = out.getTagKey(); tagKey = out.getTagKey();
headerKey = out.getHeaderKey(); headerKey = out.getHeaderKey();
rotationPeriod = out.getRotationPeriod(); timePeriod = out.getTimePeriod();
streamCounter = out.getStreamCounter(); streamCounter = out.getStreamCounter();
active = out.isActive(); active = out.isActive();
} }
OutgoingKeys snapshot() { OutgoingKeys snapshot() {
return new OutgoingKeys(tagKey, headerKey, rotationPeriod, return new OutgoingKeys(tagKey, headerKey, timePeriod,
streamCounter, active); streamCounter, active);
} }
@@ -36,8 +36,8 @@ class MutableOutgoingKeys {
return headerKey; return headerKey;
} }
long getRotationPeriod() { long getTimePeriod() {
return rotationPeriod; return timePeriod;
} }
long getStreamCounter() { long getStreamCounter() {

View File

@@ -5,8 +5,8 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,10 +15,11 @@ interface TransportKeyManager {
void start(Transaction txn) throws DbException; void start(Transaction txn) throws DbException;
KeySetId addContact(Transaction txn, ContactId c, SecretKey master, TransportKeySetId addContact(Transaction txn, ContactId c,
long timestamp, boolean alice, boolean active) throws DbException; SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException;
void activateKeys(Transaction txn, KeySetId k) throws DbException; void activateKeys(Transaction txn, TransportKeySetId k) throws DbException;
void removeContact(ContactId c); void removeContact(ContactId c);

View File

@@ -11,9 +11,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.transport.ReorderingWindow.Change; import org.briarproject.bramble.transport.ReorderingWindow.Change;
@@ -51,12 +51,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
private final TransportId transportId; private final TransportId transportId;
private final long rotationPeriodLength; private final long timePeriodLength;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
// The following are locking: lock // The following are locking: lock
private final Map<KeySetId, MutableKeySet> keys = new HashMap<>(); private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>();
private final Map<Bytes, TagContext> inContexts = new HashMap<>(); private final Map<Bytes, TagContext> inContexts = new HashMap<>();
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>(); private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
@@ -70,7 +70,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
this.scheduler = scheduler; this.scheduler = scheduler;
this.clock = clock; this.clock = clock;
this.transportId = transportId; this.transportId = transportId;
rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
} }
@Override @Override
@@ -80,8 +80,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
lock.lock(); lock.lock();
try { try {
// Load the transport keys from the DB // Load the transport keys from the DB
Collection<KeySet> loaded = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> loaded =
// Rotate the keys to the current rotation period db.getTransportKeys(txn, transportId);
// Rotate the keys to the current time period
RotationResult rotationResult = rotateKeys(loaded, now); RotationResult rotationResult = rotateKeys(loaded, now);
// Initialise mutable state for all contacts // Initialise mutable state for all contacts
addKeys(rotationResult.current); addKeys(rotationResult.current);
@@ -95,15 +96,17 @@ class TransportKeyManagerImpl implements TransportKeyManager {
scheduleKeyRotation(now); scheduleKeyRotation(now);
} }
private RotationResult rotateKeys(Collection<KeySet> keys, long now) { private RotationResult rotateKeys(Collection<TransportKeySet> keys,
long now) {
RotationResult rotationResult = new RotationResult(); RotationResult rotationResult = new RotationResult();
long rotationPeriod = now / rotationPeriodLength; long timePeriod = now / timePeriodLength;
for (KeySet ks : keys) { for (TransportKeySet ks : keys) {
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
TransportKeys k1 = TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
transportCrypto.rotateTransportKeys(k, rotationPeriod); timePeriod);
KeySet ks1 = new KeySet(ks.getKeySetId(), ks.getContactId(), k1); TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
if (k1.getRotationPeriod() > k.getRotationPeriod()) ks.getContactId(), k1);
if (k1.getTimePeriod() > k.getTimePeriod())
rotationResult.rotated.add(ks1); rotationResult.rotated.add(ks1);
rotationResult.current.add(ks1); rotationResult.current.add(ks1);
} }
@@ -111,15 +114,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void addKeys(Collection<KeySet> keys) { private void addKeys(Collection<TransportKeySet> keys) {
for (KeySet ks : keys) { for (TransportKeySet ks : keys) {
addKeys(ks.getKeySetId(), ks.getContactId(), addKeys(ks.getKeySetId(), ks.getContactId(),
new MutableTransportKeys(ks.getTransportKeys())); new MutableTransportKeys(ks.getKeys()));
} }
} }
// Locking: lock // Locking: lock
private void addKeys(KeySetId keySetId, ContactId contactId, private void addKeys(TransportKeySetId keySetId, ContactId contactId,
MutableTransportKeys m) { MutableTransportKeys m) {
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m); MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
keys.put(keySetId, ks); keys.put(keySetId, ks);
@@ -130,7 +133,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void encodeTags(KeySetId keySetId, ContactId contactId, private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys) { MutableIncomingKeys inKeys) {
for (long streamNumber : inKeys.getWindow().getUnseen()) { for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = TagContext tagCtx =
@@ -155,7 +158,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
private void scheduleKeyRotation(long now) { private void scheduleKeyRotation(long now) {
long delay = rotationPeriodLength - now % rotationPeriodLength; long delay = timePeriodLength - now % timePeriodLength;
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS); scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
} }
@@ -170,20 +173,21 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public KeySetId addContact(Transaction txn, ContactId c, SecretKey master, public TransportKeySetId addContact(Transaction txn, ContactId c,
long timestamp, boolean alice, boolean active) throws DbException { SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException {
lock.lock(); lock.lock();
try { try {
// Work out what rotation period the timestamp belongs to // Work out what time period the timestamp belongs to
long rotationPeriod = timestamp / rotationPeriodLength; long timePeriod = timestamp / timePeriodLength;
// Derive the transport keys // Derive the transport keys
TransportKeys k = transportCrypto.deriveTransportKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, rotationPeriod, alice, active); rootKey, timePeriod, alice, active);
// Rotate the keys to the current rotation period if necessary // Rotate the keys to the current time period if necessary
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength; timePeriod = clock.currentTimeMillis() / timePeriodLength;
k = transportCrypto.rotateTransportKeys(k, rotationPeriod); k = transportCrypto.rotateTransportKeys(k, timePeriod);
// Write the keys back to the DB // Write the keys back to the DB
KeySetId keySetId = db.addTransportKeys(txn, c, k); TransportKeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact // Initialise mutable state for the contact
addKeys(keySetId, c, new MutableTransportKeys(k)); addKeys(keySetId, c, new MutableTransportKeys(k));
return keySetId; return keySetId;
@@ -193,7 +197,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public void activateKeys(Transaction txn, KeySetId k) throws DbException { public void activateKeys(Transaction txn, TransportKeySetId k)
throws DbException {
lock.lock(); lock.lock();
try { try {
MutableKeySet ks = keys.get(k); MutableKeySet ks = keys.get(k);
@@ -300,7 +305,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Write the window back to the DB // Write the window back to the DB
db.setReorderingWindow(txn, tagCtx.keySetId, transportId, db.setReorderingWindow(txn, tagCtx.keySetId, transportId,
inKeys.getRotationPeriod(), window.getBase(), inKeys.getTimePeriod(), window.getBase(),
window.getBitmap()); window.getBitmap());
// If the outgoing keys are inactive, activate them // If the outgoing keys are inactive, activate them
MutableKeySet ks = keys.get(tagCtx.keySetId); MutableKeySet ks = keys.get(tagCtx.keySetId);
@@ -322,11 +327,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();
try { try {
// Rotate the keys to the current rotation period // Rotate the keys to the current time period
Collection<KeySet> snapshot = new ArrayList<>(keys.size()); Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
for (MutableKeySet ks : keys.values()) { for (MutableKeySet ks : keys.values()) {
snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(), snapshot.add(new TransportKeySet(ks.getKeySetId(),
ks.getTransportKeys().snapshot())); ks.getContactId(), ks.getTransportKeys().snapshot()));
} }
RotationResult rotationResult = rotateKeys(snapshot, now); RotationResult rotationResult = rotateKeys(snapshot, now);
// Rebuild the mutable state for all contacts // Rebuild the mutable state for all contacts
@@ -346,12 +351,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private static class TagContext { private static class TagContext {
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final ContactId contactId; private final ContactId contactId;
private final MutableIncomingKeys inKeys; private final MutableIncomingKeys inKeys;
private final long streamNumber; private final long streamNumber;
private TagContext(KeySetId keySetId, ContactId contactId, private TagContext(TransportKeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys, long streamNumber) { MutableIncomingKeys inKeys, long streamNumber) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
@@ -362,7 +367,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private static class RotationResult { private static class RotationResult {
private final Collection<KeySet> current = new ArrayList<>(); private final Collection<TransportKeySet> current = new ArrayList<>();
private final Collection<KeySet> rotated = new ArrayList<>(); private final Collection<TransportKeySet> rotated = new ArrayList<>();
} }
} }

View File

@@ -12,6 +12,8 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -58,6 +60,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final Clock clock; private final Clock clock;
private final Group localGroup; private final Group localGroup;
@@ -68,9 +71,11 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
@Inject @Inject
ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper, ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
IdentityManager identityManager,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.clock = clock; this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
@@ -154,7 +159,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group and share it with the contact // Create a group and share it with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
@@ -173,7 +178,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn)));
} }
@Override @Override
@@ -308,7 +313,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
List<ClientVersion> versions) throws DbException { List<ClientVersion> versions) throws DbException {
try { try {
// Find the latest local and remote updates // Find the latest local and remote updates
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
LatestUpdates latest = findLatestUpdates(txn, g.getId()); LatestUpdates latest = findLatestUpdates(txn, g.getId());
// Load and parse the latest local update // Load and parse the latest local update
if (latest.local == null) throw new DbException(); if (latest.local == null) throw new DbException();
@@ -344,16 +349,20 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
} }
} }
private Group getContactGroup(Contact c) { private AuthorId getLocalAuthorId(Transaction txn) throws DbException {
return identityManager.getLocalAuthor(txn).getId();
}
private Group getContactGroup(Contact c, AuthorId local) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c); MAJOR_VERSION, c, local);
} }
@Nullable @Nullable
private LatestUpdates findLatestUpdates(Transaction txn, ContactId c) private LatestUpdates findLatestUpdates(Transaction txn, ContactId c)
throws DbException, FormatException { throws DbException, FormatException {
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
Group g = getContactGroup(contact); Group g = getContactGroup(contact, getLocalAuthorId(txn));
// Contact may be in the process of being added or removed, so // Contact may be in the process of being added or removed, so
// contact group may not exist // contact group may not exist
if (!db.containsGroup(txn, g.getId())) return null; if (!db.containsGroup(txn, g.getId())) return null;

View File

@@ -2,3 +2,4 @@ Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3
Bridge obfs4 52.15.78.72:9443 02069A3C5362476936B62BA6F5ACC41ABD573A9B cert=ijYG/OKc7kqu2YzKNFfeXN7/BG2BOgfEP2KyYEiGDQthnHbsOiTWHeIG0WJVW+BckzDgKw iat-mode=0 Bridge obfs4 52.15.78.72:9443 02069A3C5362476936B62BA6F5ACC41ABD573A9B cert=ijYG/OKc7kqu2YzKNFfeXN7/BG2BOgfEP2KyYEiGDQthnHbsOiTWHeIG0WJVW+BckzDgKw iat-mode=0
Bridge obfs4 13.58.29.242:9443 0C58939A77DA6B6B29D4B5236A75865659607AE0 cert=OylWIEHb/ezpq1zWxW0sgKRn+9ARH2eOcQOZ8/Gew+4l+oKOhQ2jUX/Y+FSl61JorXZUWA iat-mode=0 Bridge obfs4 13.58.29.242:9443 0C58939A77DA6B6B29D4B5236A75865659607AE0 cert=OylWIEHb/ezpq1zWxW0sgKRn+9ARH2eOcQOZ8/Gew+4l+oKOhQ2jUX/Y+FSl61JorXZUWA iat-mode=0
Bridge obfs4 45.33.37.112:9443 60A609BB4ABE8D46E634AE81ED29ADAB7776B399 cert=t5v19WmNv5Sc2YPNr8RQids365W7MY8zJwQVkOxBjUMFomMWARDzsbYpcWLLcw0J9Gm+BQ iat-mode=0 Bridge obfs4 45.33.37.112:9443 60A609BB4ABE8D46E634AE81ED29ADAB7776B399 cert=t5v19WmNv5Sc2YPNr8RQids365W7MY8zJwQVkOxBjUMFomMWARDzsbYpcWLLcw0J9Gm+BQ iat-mode=0
Bridge meek_lite 0.0.2.0:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com

View File

@@ -5,27 +5,20 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo; import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Random; import java.util.Random;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
@@ -33,8 +26,8 @@ import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -49,42 +42,40 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
private final ContactManager contactManager; private final ContactManager contactManager;
private final ContactId contactId = new ContactId(42); private final Author author = getAuthor();
private final Author remote = getAuthor();
private final AuthorId local = new AuthorId(getRandomId());
private final LocalAuthor localAuthor = getLocalAuthor(); private final LocalAuthor localAuthor = getLocalAuthor();
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final boolean verified = false, active = true; private final boolean verified = false, active = true;
private final Contact contact = private final Contact contact = getContact(author, verified);
new Contact(contactId, remote, local, alias, verified, active); private final ContactId contactId = contact.getId();
public ContactManagerImplTest() { public ContactManagerImplTest() {
contactManager = new ContactManagerImpl(db, keyManager, identityManager); contactManager =
new ContactManagerImpl(db, keyManager, identityManager);
} }
@Test @Test
public void testAddContact() throws Exception { public void testAddContact() throws Exception {
SecretKey master = getSecretKey(); SecretKey rootKey = getSecretKey();
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
boolean alice = new Random().nextBoolean(); boolean alice = new Random().nextBoolean();
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, verified, active); oneOf(db).addContact(txn, author, verified);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager).addContact(txn, contactId, master, timestamp, oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
alice, active); alice, active);
oneOf(db).getContact(txn, contactId); oneOf(db).getContact(txn, contactId);
will(returnValue(contact)); will(returnValue(contact));
}}); }});
assertEquals(contactId, contactManager.addContact(remote, local, assertEquals(contactId, contactManager.addContact(author, rootKey,
master, timestamp, alice, verified, active)); timestamp, alice, verified, active));
} }
@Test @Test
public void testGetContact() throws Exception { public void testGetContactByContactId() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
@@ -96,49 +87,20 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testGetContactByAuthor() throws Exception { public void testGetContactByAuthorId() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
assertEquals(contact, contactManager.getContact(remote.getId(), local));
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownAuthor() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId()); oneOf(db).getContact(txn, author.getId());
will(returnValue(emptyList())); will(returnValue(contact));
}}); }});
contactManager.getContact(remote.getId(), local); assertEquals(contact, contactManager.getContact(author.getId()));
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownLocalAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
contactManager.getContact(remote.getId(), new AuthorId(getRandomId()));
} }
@Test @Test
public void testGetActiveContacts() throws Exception { public void testGetContacts() throws Exception {
Collection<Contact> activeContacts = Collections.singletonList(contact); Collection<Contact> contacts = singletonList(contact);
Collection<Contact> contacts = new ArrayList<>(activeContacts);
contacts.add(new Contact(new ContactId(3), remote, local, alias, true,
false));
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
@@ -146,7 +108,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
will(returnValue(contacts)); will(returnValue(contacts));
}}); }});
assertEquals(activeContacts, contactManager.getActiveContacts()); assertEquals(contacts, contactManager.getContacts());
} }
@Test @Test
@@ -162,19 +124,11 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
contactManager.removeContact(contactId); contactManager.removeContact(contactId);
} }
@Test
public void testSetContactActive() throws Exception {
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).setContactActive(txn, contactId, active);
}});
contactManager.setContactActive(txn, contactId, active);
}
@Test @Test
public void testSetContactAlias() throws Exception { public void testSetContactAlias() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(db).setContactAlias(txn, contactId, alias); oneOf(db).setContactAlias(txn, contactId, alias);
@@ -195,85 +149,82 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).containsContact(txn, remote.getId(), local); oneOf(db).containsContact(txn, author.getId());
will(returnValue(true)); will(returnValue(true));
}}); }});
assertTrue(contactManager.contactExists(remote.getId(), local)); assertTrue(contactManager.contactExists(author.getId()));
} }
@Test @Test
public void testGetAuthorInfo() throws Exception { public void testGetAuthorInfoOurselves() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor)); will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}}); }});
AuthorInfo authorInfo = AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId()); contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
}
@Test
public void testGetAuthorInfoTransaction() throws DbException {
Transaction txn = new Transaction(null, true);
// check unknown author
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(emptyList()));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
// check unverified contact
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
checkAuthorInfoContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check verified contact
contacts = singletonList(new Contact(new ContactId(1), remote,
localAuthor.getId(), alias, true, true));
checkAuthorInfoContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check ourselves
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
never(db).getContactsByAuthorId(txn, remote.getId());
}});
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(OURSELVES, authorInfo.getStatus()); assertEquals(OURSELVES, authorInfo.getStatus());
assertNull(authorInfo.getAlias()); assertNull(authorInfo.getAlias());
} }
private void checkAuthorInfoContext(Transaction txn, AuthorId authorId, @Test
Collection<Contact> contacts) throws DbException { public void testGetAuthorInfoVerified() throws Exception {
context.checking(new Expectations() {{ Transaction txn = new Transaction(null, true);
Contact verified = getContact(author, true);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor)); will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, authorId); oneOf(db).containsContact(txn, author.getId());
will(returnValue(contacts)); will(returnValue(true));
oneOf(db).getContact(txn, author.getId());
will(returnValue(verified));
}}); }});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(verified.getAlias(), authorInfo.getAlias());
} }
@Test
public void testGetAuthorInfoUnverified() throws Exception {
Transaction txn = new Transaction(null, true);
Contact unverified = getContact(author, false);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId());
will(returnValue(true));
oneOf(db).getContact(txn, author.getId());
will(returnValue(unverified));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(unverified.getAlias(), authorInfo.getAlias());
}
@Test
public void testGetAuthorInfoUnknown() throws Exception {
Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId());
will(returnValue(false));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
}
} }

View File

@@ -0,0 +1,167 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import java.util.Arrays;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
public class HandshakeKeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey rootKey = getSecretKey();
@Test
public void testKeysAreDistinct() {
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testKeysAreNotUpdatedToPreviousPeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testKeysAreNotUpdatedToCurrentPeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testKeysAreUpdatedByOnePeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testKeysAreUpdatedByTwoPeriods() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testKeysAreUpdatedByThreePeriods() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Update into the future
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
}
@Test
public void testPreviousKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current
// outgoing keys
assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.updateHandshakeKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
kA.getCurrentOutgoingKeys());
}
@Test
public void testNextKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.updateHandshakeKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId,
rootKey1, 123, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1,
rootKey, 123, true);
assertAllDifferent(k, k1);
}
}

View File

@@ -1,167 +0,0 @@
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.crypto.TransportCrypto;
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.TestSecureRandomProvider;
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.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class KeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey master = getSecretKey();
@Test
public void testKeysAreDistinct() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
assertAllDifferent(k);
}
@Test
public void testCurrentKeysMatchCurrentKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Alice's outgoing keys should equal Bob's incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getCurrentIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
// Rotate into the future
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Alice's outgoing keys should equal Bob's incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getCurrentIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
}
@Test
public void testPreviousKeysMatchPreviousKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's outgoing keys should equal Bob's previous incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getPreviousIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getPreviousIncomingKeys().getHeaderKey().getBytes());
}
@Test
public void testNextKeysMatchNextKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's outgoing keys should equal Bob's next incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getNextIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getNextIncomingKeys().getHeaderKey().getBytes());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getNextIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
}
@Test
public void testMasterKeyAffectsOutput() {
SecretKey master1 = getSecretKey();
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
master1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertFalse(transportId.getString().equals(transportId1.getString()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
master, 123, true, true);
assertAllDifferent(k, k1);
}
private void assertAllDifferent(TransportKeys... transportKeys) {
List<SecretKey> secretKeys = new ArrayList<>();
for (TransportKeys k : transportKeys) {
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
secretKeys.add(k.getNextIncomingKeys().getTagKey());
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
}
assertAllDifferent(secretKeys);
}
private void assertAllDifferent(List<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
}

View File

@@ -0,0 +1,45 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.transport.AbstractTransportKeys;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
class KeyDerivationTestUtils {
static void assertAllDifferent(AbstractTransportKeys... transportKeys) {
List<SecretKey> secretKeys = new ArrayList<>();
for (AbstractTransportKeys k : transportKeys) {
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
secretKeys.add(k.getNextIncomingKeys().getTagKey());
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
}
assertAllDifferent(secretKeys);
}
static void assertAllDifferent(List<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
static void assertMatches(IncomingKeys in, OutgoingKeys out) {
assertArrayEquals(in.getTagKey().getBytes(),
out.getTagKey().getBytes());
assertArrayEquals(in.getHeaderKey().getBytes(),
out.getHeaderKey().getBytes());
}
}

View File

@@ -0,0 +1,167 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
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.TestSecureRandomProvider;
import org.junit.Test;
import java.util.Arrays;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
public class TransportKeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey rootKey = getSecretKey();
@Test
public void testKeysAreDistinct() {
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testKeysAreNotRotatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testKeysAreNotRotatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testKeysAreRotatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testKeysAreRotatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testKeysAreRotatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Rotate into the future
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
}
@Test
public void testPreviousKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current
// outgoing keys
assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
kA.getCurrentOutgoingKeys());
}
@Test
public void testNextKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
rootKey1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
rootKey, 123, true, true);
assertAllDifferent(k, k1);
}
}

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -13,7 +13,9 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -43,18 +45,21 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Sequence;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@@ -70,13 +75,13 @@ import static org.briarproject.bramble.api.transport.TransportConstants.REORDERI
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES; import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@@ -87,9 +92,10 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final Database<Object> database = context.mock(Database.class); private final Database<Object> database = context.mock(Database.class);
private final ShutdownManager shutdown = private final ShutdownManager shutdownManager =
context.mock(ShutdownManager.class); context.mock(ShutdownManager.class);
private final EventBus eventBus = context.mock(EventBus.class); private final EventBus eventBus = context.mock(EventBus.class);
private final Executor eventExecutor = context.mock(Executor.class);
private final SecretKey key = getSecretKey(); private final SecretKey key = getSecretKey();
private final Object txn = new Object(); private final Object txn = new Object();
@@ -107,7 +113,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final int maxLatency; private final int maxLatency;
private final ContactId contactId; private final ContactId contactId;
private final Contact contact; private final Contact contact;
private final KeySetId keySetId; private final TransportKeySetId keySetId;
private final PendingContactId pendingContactId;
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
clientId = getClientId(); clientId = getClientId();
@@ -116,7 +123,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
localAuthor = getLocalAuthor(); localAuthor = getLocalAuthor();
alias = getRandomString(5);
message = getMessage(groupId); message = getMessage(groupId);
message1 = getMessage(groupId); message1 = getMessage(groupId);
messageId = message.getId(); messageId = message.getId();
@@ -125,16 +131,18 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
metadata.put("foo", new byte[] {'b', 'a', 'r'}); metadata.put("foo", new byte[] {'b', 'a', 'r'});
transportId = getTransportId(); transportId = getTransportId();
maxLatency = Integer.MAX_VALUE; maxLatency = Integer.MAX_VALUE;
contactId = new ContactId(234); contact = getContact(author, true);
contact = new Contact(contactId, author, localAuthor.getId(), alias, contactId = contact.getId();
true, true); alias = contact.getAlias();
keySetId = new KeySetId(345); keySetId = new TransportKeySetId(345);
pendingContactId = new PendingContactId(getRandomId());
} }
private DatabaseComponent createDatabaseComponent(Database<Object> database, private DatabaseComponent createDatabaseComponent(Database<Object> database,
EventBus eventBus, ShutdownManager shutdown) { EventBus eventBus, Executor eventExecutor,
ShutdownManager shutdownManager) {
return new DatabaseComponentImpl<>(database, Object.class, eventBus, return new DatabaseComponentImpl<>(database, Object.class, eventBus,
shutdown); eventExecutor, shutdownManager);
} }
@Test @Test
@@ -144,7 +152,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// open() // open()
oneOf(database).open(key, null); oneOf(database).open(key, null);
will(returnValue(false)); will(returnValue(false));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class))); oneOf(shutdownManager).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle)); will(returnValue(shutdownHandle));
// startTransaction() // startTransaction()
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -155,19 +163,13 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).addLocalAuthor(txn, localAuthor); oneOf(database).addLocalAuthor(txn, localAuthor);
oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class))); oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class)));
// addContact() // addContact()
oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true));
oneOf(database).containsLocalAuthor(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).containsContact(txn, author.getId(), oneOf(database).containsContact(txn, author.getId());
localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).addContact(txn, author, localAuthor.getId(), oneOf(database).addContact(txn, author, true);
true, true);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
oneOf(eventBus).broadcast(with(any(
ContactStatusChangedEvent.class)));
// getContacts() // getContacts()
oneOf(database).getContacts(txn); oneOf(database).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
@@ -207,13 +209,12 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).close(); oneOf(database).close();
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
assertFalse(db.open(key, null)); assertFalse(db.open(key, null));
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.addLocalAuthor(transaction, localAuthor); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, db.addContact(transaction, author, assertEquals(contactId, db.addContact(transaction, author, true));
localAuthor.getId(), true, true));
assertEquals(singletonList(contact), assertEquals(singletonList(contact),
db.getContacts(transaction)); db.getContacts(transaction));
db.addGroup(transaction, group); // First time - listeners called db.addGroup(transaction, group); // First time - listeners called
@@ -238,7 +239,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).abortTransaction(txn); oneOf(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addLocalMessage(transaction, message, metadata, true)); db.addLocalMessage(transaction, message, metadata, true));
@@ -263,7 +264,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addLocalMessage(transaction, message, metadata, true)); db.addLocalMessage(transaction, message, metadata, true));
@@ -274,14 +275,25 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(17).of(database).startTransaction(); exactly(18).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(17).of(database).containsContact(txn, contactId); exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(17).of(database).abortTransaction(txn); oneOf(database).containsContact(txn, author.getId());
will(returnValue(false));
exactly(18).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, contactId,
createHandshakeKeys()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -332,6 +344,14 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.getContact(transaction, author.getId()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.getMessageStatus(transaction, contactId, groupId)); db.getMessageStatus(transaction, contactId, groupId));
@@ -399,14 +419,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.setContactActive(transaction, contactId, true));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setContactAlias(transaction, contactId, alias)); db.setContactAlias(transaction, contactId, alias));
@@ -430,24 +442,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the pseudonym is in the DB (which it's not) // Check whether the pseudonym is in the DB (which it's not)
exactly(3).of(database).startTransaction(); exactly(2).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(3).of(database).containsLocalAuthor(txn, exactly(2).of(database).containsLocalAuthor(txn,
localAuthor.getId()); localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
exactly(3).of(database).abortTransaction(txn); exactly(2).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(),
true, true));
fail();
} catch (NoSuchLocalAuthorException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -481,7 +484,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -565,7 +568,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -668,7 +671,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
exactly(5).of(database).abortTransaction(txn); exactly(5).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -713,6 +716,39 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
} }
@Test
public void testVariousMethodsThrowExceptionIfPendingContactIsMissing()
throws Exception {
context.checking(new Expectations() {{
// Check whether the pending contact is in the DB (which it's not)
exactly(2).of(database).startTransaction();
will(returnValue(txn));
exactly(2).of(database).containsPendingContact(txn,
pendingContactId);
will(returnValue(false));
exactly(2).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, pendingContactId,
createHandshakeKeys()));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.removePendingContact(transaction, pendingContactId));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
}
@Test @Test
public void testGenerateAck() throws Exception { public void testGenerateAck() throws Exception {
Collection<MessageId> messagesToAck = asList(messageId, messageId1); Collection<MessageId> messagesToAck = asList(messageId, messageId1);
@@ -727,7 +763,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Ack a = db.generateAck(transaction, contactId, 123); Ack a = db.generateAck(transaction, contactId, 123);
@@ -761,7 +797,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class))); oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
assertEquals(messages, db.generateBatch(transaction, contactId, assertEquals(messages, db.generateBatch(transaction, contactId,
@@ -786,7 +822,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency); Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
@@ -810,7 +846,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Request r = db.generateRequest(transaction, contactId, 123); Request r = db.generateRequest(transaction, contactId, 123);
@@ -844,7 +880,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class))); oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
assertEquals(messages, db.generateRequestedBatch(transaction, assertEquals(messages, db.generateRequestedBatch(transaction,
@@ -865,7 +901,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class))); oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Ack a = new Ack(singletonList(messageId)); Ack a = new Ack(singletonList(messageId));
@@ -903,7 +939,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class))); oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
// Receive the message twice // Receive the message twice
@@ -931,7 +967,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class))); oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.receiveMessage(transaction, contactId, message)); db.receiveMessage(transaction, contactId, message));
@@ -949,7 +985,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.receiveMessage(transaction, contactId, message)); db.receiveMessage(transaction, contactId, message));
@@ -989,7 +1025,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageToRequestEvent.class))); oneOf(eventBus).broadcast(with(any(MessageToRequestEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
Offer o = new Offer(asList(messageId, messageId1, Offer o = new Offer(asList(messageId, messageId1,
messageId2, messageId3)); messageId2, messageId3));
@@ -1012,7 +1048,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageRequestedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageRequestedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
Request r = new Request(singletonList(messageId)); Request r = new Request(singletonList(messageId));
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -1042,7 +1078,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
GroupVisibilityUpdatedEvent.class, 0)); GroupVisibilityUpdatedEvent.class, 0));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setGroupVisibility(transaction, contactId, groupId, db.setGroupVisibility(transaction, contactId, groupId,
@@ -1076,7 +1112,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
GroupVisibilityUpdatedEvent.class, 0)); GroupVisibilityUpdatedEvent.class, 0));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setGroupVisibility(transaction, contactId, groupId, db.setGroupVisibility(transaction, contactId, groupId,
@@ -1102,7 +1138,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setGroupVisibility(transaction, contactId, groupId, db.setGroupVisibility(transaction, contactId, groupId,
@@ -1112,8 +1148,9 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
@Test @Test
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
TransportKeys transportKeys = createTransportKeys(); TransportKeys transportKeys = createTransportKeys();
KeySet ks = new KeySet(keySetId, contactId, transportKeys); TransportKeySet ks =
Collection<KeySet> keys = singletonList(ks); new TransportKeySet(keySetId, contactId, transportKeys);
Collection<TransportKeySet> keys = singletonList(ks);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// startTransaction() // startTransaction()
@@ -1132,7 +1169,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.updateTransportKeys(transaction, keys); db.updateTransportKeys(transaction, keys);
@@ -1171,7 +1208,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(true, transaction -> { db.transaction(true, transaction -> {
// With visible group - return stored status // With visible group - return stored status
@@ -1221,7 +1258,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(true, transaction -> { db.transaction(true, transaction -> {
// With visible group - return stored status // With visible group - return stored status
@@ -1240,6 +1277,27 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}); });
} }
private HandshakeKeys createHandshakeKeys() {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
2, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
3, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
2, 456, true);
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
getSecretKey(), true);
}
private TransportKeys createTransportKeys() { private TransportKeys createTransportKeys() {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
@@ -1287,7 +1345,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
// First merge should broadcast an event // First merge should broadcast an event
@@ -1330,7 +1388,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
assertNotNull(db.startTransaction(firstTxnReadOnly)); assertNotNull(db.startTransaction(firstTxnReadOnly));
db.startTransaction(secondTxnReadOnly); db.startTransaction(secondTxnReadOnly);
@@ -1342,8 +1400,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true));
// Contact is a local identity // Contact is a local identity
oneOf(database).containsLocalAuthor(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(true)); will(returnValue(true));
@@ -1351,12 +1407,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, true));
true, true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
// Expected // Expected
@@ -1368,24 +1423,20 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true));
oneOf(database).containsLocalAuthor(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(false)); will(returnValue(false));
// Contact already exists for this local identity // Contact already exists
oneOf(database).containsContact(txn, author.getId(), oneOf(database).containsContact(txn, author.getId());
localAuthor.getId());
will(returnValue(true)); will(returnValue(true));
oneOf(database).abortTransaction(txn); oneOf(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, true));
true, true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
// Expected // Expected
@@ -1401,7 +1452,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// open() // open()
oneOf(database).open(key, null); oneOf(database).open(key, null);
will(returnValue(false)); will(returnValue(false));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class))); oneOf(shutdownManager).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle)); will(returnValue(shutdownHandle));
// startTransaction() // startTransaction()
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -1441,7 +1492,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).close(); oneOf(database).close();
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); eventExecutor, shutdownManager);
assertFalse(db.open(key, null)); assertFalse(db.open(key, null));
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
@@ -1455,4 +1506,43 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}); });
db.close(); db.close();
} }
@Test
public void testCommitActionsOccurInOrder() throws Exception {
TestEvent action1 = new TestEvent();
Runnable action2 = () -> {
};
TestEvent action3 = new TestEvent();
Runnable action4 = () -> {
};
Sequence sequence = context.sequence("sequence");
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
inSequence(sequence);
oneOf(database).commitTransaction(txn);
inSequence(sequence);
oneOf(eventBus).broadcast(action1);
inSequence(sequence);
oneOf(eventExecutor).execute(action2);
inSequence(sequence);
oneOf(eventBus).broadcast(action3);
inSequence(sequence);
oneOf(eventExecutor).execute(action4);
inSequence(sequence);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
db.transaction(false, transaction -> {
transaction.attach(action1);
transaction.attach(action2);
transaction.attach(action3);
transaction.attach(action4);
});
}
private static class TestEvent extends Event {
}
} }

View File

@@ -45,8 +45,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
private final Migration<Connection> migration1 = private final Migration<Connection> migration1 =
context.mock(Migration.class, "migration1"); context.mock(Migration.class, "migration1");
protected final DatabaseConfig config = protected final DatabaseConfig config = new TestDatabaseConfig(testDir);
new TestDatabaseConfig(testDir, 1024 * 1024);
protected final MessageFactory messageFactory = new TestMessageFactory(); protected final MessageFactory messageFactory = new TestMessageFactory();
protected final SecretKey key = getSecretKey(); protected final SecretKey key = getSecretKey();
protected final Clock clock = new SystemClock(); protected final Clock clock = new SystemClock();

View File

@@ -76,8 +76,8 @@ public abstract class DatabasePerformanceComparisonTest
private Database<Connection> openDatabase(boolean conditionA) private Database<Connection> openDatabase(boolean conditionA)
throws DbException { throws DbException {
Database<Connection> db = createDatabase(conditionA, Database<Connection> db = createDatabase(conditionA,
new TestDatabaseConfig(testDir, MAX_SIZE), new TestDatabaseConfig(testDir), new TestMessageFactory(),
new TestMessageFactory(), new SystemClock()); new SystemClock());
db.open(databaseKey, null); db.open(databaseKey, null);
return db; return db;
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
@@ -49,9 +48,6 @@ import static org.junit.Assert.assertTrue;
public abstract class DatabasePerformanceTest extends BrambleTestCase { public abstract class DatabasePerformanceTest extends BrambleTestCase {
private static final int ONE_MEGABYTE = 1024 * 1024;
static final int MAX_SIZE = 100 * ONE_MEGABYTE;
/** /**
* How many contacts to simulate. * How many contacts to simulate.
*/ */
@@ -134,11 +130,10 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
@Test @Test
public void testContainsContactByAuthorId() throws Exception { public void testContainsContactByAuthorId() throws Exception {
String name = "containsContact(T, AuthorId, AuthorId)"; String name = "containsContact(T, AuthorId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
AuthorId remote = pickRandom(contacts).getAuthor().getId(); db.containsContact(txn, pickRandom(contacts).getAuthor().getId());
db.containsContact(txn, remote, localAuthor.getId());
db.commitTransaction(txn); db.commitTransaction(txn);
}); });
} }
@@ -205,7 +200,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
} }
@Test @Test
public void testGetContact() throws Exception { public void testGetContactByContactId() throws Exception {
String name = "getContact(T, ContactId)"; String name = "getContact(T, ContactId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -214,6 +209,16 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
}); });
} }
@Test
public void testGetContactByAuthorId() throws Exception {
String name = "getContact(T, AuthorId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
db.getContact(txn, pickRandom(contacts).getAuthor().getId());
db.commitTransaction(txn);
});
}
@Test @Test
public void testGetContacts() throws Exception { public void testGetContacts() throws Exception {
String name = "getContacts(T)"; String name = "getContacts(T)";
@@ -224,27 +229,6 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
}); });
} }
@Test
public void testGetContactsByRemoteAuthorId() throws Exception {
String name = "getContactsByAuthorId(T, AuthorId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
AuthorId remote = pickRandom(contacts).getAuthor().getId();
db.getContactsByAuthorId(txn, remote);
db.commitTransaction(txn);
});
}
@Test
public void testGetContactsByLocalAuthorId() throws Exception {
String name = "getContacts(T, AuthorId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
db.getContacts(txn, localAuthor.getId());
db.commitTransaction(txn);
});
}
@Test @Test
public void testGetGroup() throws Exception { public void testGetGroup() throws Exception {
String name = "getGroup(T, GroupId)"; String name = "getGroup(T, GroupId)";
@@ -548,8 +532,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
for (int i = 0; i < CONTACTS; i++) { for (int i = 0; i < CONTACTS; i++) {
ContactId c = db.addContact(txn, getAuthor(), localAuthor.getId(), ContactId c = db.addContact(txn, getAuthor(), random.nextBoolean());
random.nextBoolean(), true);
contacts.add(db.getContact(txn, c)); contacts.add(db.getContact(txn, c));
contactGroups.put(c, new ArrayList<>()); contactGroups.put(c, new ArrayList<>());
for (int j = 0; j < GROUPS_PER_CONTACT; j++) { for (int j = 0; j < GROUPS_PER_CONTACT; j++) {

View File

@@ -48,8 +48,8 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
private Database<Connection> openDatabase() throws DbException { private Database<Connection> openDatabase() throws DbException {
Database<Connection> db = createDatabase( Database<Connection> db = createDatabase(
new TestDatabaseConfig(testDir, MAX_SIZE), new TestDatabaseConfig(testDir), new TestMessageFactory(),
new TestMessageFactory(), new SystemClock()); new SystemClock());
db.open(databaseKey, null); db.open(databaseKey, null);
return db; return db;
} }

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -20,10 +21,13 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.HandshakeKeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
@@ -52,12 +56,12 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED; import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID; import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING; import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
@@ -71,6 +75,7 @@ import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getPendingContact;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
@@ -79,7 +84,6 @@ import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -88,11 +92,9 @@ import static org.junit.Assert.fail;
public abstract class JdbcDatabaseTest extends BrambleTestCase { public abstract class JdbcDatabaseTest extends BrambleTestCase {
private static final int ONE_MEGABYTE = 1024 * 1024; private static final int ONE_MEGABYTE = 1024 * 1024;
private static final int MAX_SIZE = 5 * ONE_MEGABYTE;
// All our transports use a maximum latency of 30 seconds // All our transports use a maximum latency of 30 seconds
private static final int MAX_LATENCY = 30 * 1000; private static final int MAX_LATENCY = 30 * 1000;
private final SecretKey key = getSecretKey(); private final SecretKey key = getSecretKey();
private final File testDir = getTestDirectory(); private final File testDir = getTestDirectory();
private final GroupId groupId; private final GroupId groupId;
@@ -105,7 +107,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final MessageId messageId; private final MessageId messageId;
private final TransportId transportId; private final TransportId transportId;
private final ContactId contactId; private final ContactId contactId;
private final KeySetId keySetId, keySetId1; private final TransportKeySetId keySetId, keySetId1;
private final HandshakeKeySetId handshakeKeySetId, handshakeKeySetId1;
private final PendingContact pendingContact;
private final Random random = new Random(); private final Random random = new Random();
JdbcDatabaseTest() { JdbcDatabaseTest() {
@@ -119,8 +123,11 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
messageId = message.getId(); messageId = message.getId();
transportId = getTransportId(); transportId = getTransportId();
contactId = new ContactId(1); contactId = new ContactId(1);
keySetId = new KeySetId(1); keySetId = new TransportKeySetId(1);
keySetId1 = new KeySetId(2); keySetId1 = new TransportKeySetId(2);
handshakeKeySetId = new HandshakeKeySetId(1);
handshakeKeySetId1 = new HandshakeKeySetId(2);
pendingContact = getPendingContact();
} }
protected abstract JdbcDatabase createDatabase(DatabaseConfig config, protected abstract JdbcDatabase createDatabase(DatabaseConfig config,
@@ -138,8 +145,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), assertEquals(contactId, db.addContact(txn, author, true));
true, true));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsGroup(txn, groupId)); assertFalse(db.containsGroup(txn, groupId));
db.addGroup(txn, group); db.addGroup(txn, group);
@@ -200,9 +206,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -231,9 +235,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared but unvalidated message // Add a contact, a shared group and a shared but unvalidated message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true, null); db.addMessage(txn, message, UNKNOWN, true, null);
@@ -276,9 +278,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, an invisible group and a shared message // Add a contact, an invisible group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -327,9 +327,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and an unshared message // Add a contact, a shared group and an unshared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false, null); db.addMessage(txn, message, DELIVERED, false, null);
@@ -358,17 +356,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
// The message is sendable, but too large to send // The message is sendable, but too large to send
Collection<MessageId> ids = Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
db.getMessagesToSend(txn, contactId, message.getRawLength() - 1, message.getRawLength() - 1, MAX_LATENCY);
MAX_LATENCY);
assertTrue(ids.isEmpty()); assertTrue(ids.isEmpty());
// The message is just the right size to send // The message is just the right size to send
ids = db.getMessagesToSend(txn, contactId, message.getRawLength(), ids = db.getMessagesToSend(txn, contactId, message.getRawLength(),
@@ -385,9 +380,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a visible group // Add a contact and a visible group
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, false); db.addGroupVisibility(txn, contactId, groupId, false);
@@ -426,9 +419,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -454,29 +445,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testGetFreeSpace() throws Exception {
Message message = getMessage(groupId, MAX_MESSAGE_BODY_LENGTH);
Database<Connection> db = open(false);
// Sanity check: there should be enough space on disk for this test
assertTrue(testDir.getFreeSpace() > MAX_SIZE);
// The free space should not be more than the allowed maximum size
long free = db.getFreeSpace();
assertTrue(free <= MAX_SIZE);
assertTrue(free > 0);
// Storing a message should reduce the free space
Connection txn = db.startTransaction();
db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null);
db.commitTransaction(txn);
assertTrue(db.getFreeSpace() < free);
db.close();
}
@Test @Test
public void testCloseWaitsForCommit() throws Exception { public void testCloseWaitsForCommit() throws Exception {
CountDownLatch closing = new CountDownLatch(1); CountDownLatch closing = new CountDownLatch(1);
@@ -581,9 +549,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a shared group // Add a contact and a shared group
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
@@ -601,9 +567,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
// The group is not in the database // The group is not in the database
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -619,9 +583,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, an invisible group and a message // Add a contact, an invisible group and a message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -638,9 +600,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a group // Add a contact and a group
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
@@ -678,10 +638,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
long rotationPeriod = 123, rotationPeriod1 = 234; long timePeriod = 123, timePeriod1 = 234;
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
TransportKeys keys = createTransportKeys(rotationPeriod, active); TransportKeys keys = createTransportKeys(timePeriod, active);
TransportKeys keys1 = createTransportKeys(rotationPeriod1, active); TransportKeys keys1 = createTransportKeys(timePeriod1, active);
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -690,43 +650,44 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
// Add the contact, the transport and the transport keys // Add the contact, the transport and the transport keys
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, active));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
// Retrieve the transport keys // Retrieve the transport keys
Collection<KeySet> allKeys = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> allKeys =
db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) { for (TransportKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(keys, ks.getTransportKeys()); assertKeysEquals(keys, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getTransportKeys()); assertKeysEquals(keys1, ks.getKeys());
} }
} }
// Rotate the transport keys // Rotate the transport keys
TransportKeys rotated = createTransportKeys(rotationPeriod + 1, active); TransportKeys rotated = createTransportKeys(timePeriod + 1, active);
TransportKeys rotated1 = TransportKeys rotated1 =
createTransportKeys(rotationPeriod1 + 1, active); createTransportKeys(timePeriod1 + 1, active);
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated)); db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1)); rotated));
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
rotated1));
// Retrieve the transport keys again // Retrieve the transport keys again
allKeys = db.getTransportKeys(txn, transportId); allKeys = db.getTransportKeys(txn, transportId);
assertEquals(2, allKeys.size()); assertEquals(2, allKeys.size());
for (KeySet ks : allKeys) { for (TransportKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
if (ks.getKeySetId().equals(keySetId)) { if (ks.getKeySetId().equals(keySetId)) {
assertKeysEquals(rotated, ks.getTransportKeys()); assertKeysEquals(rotated, ks.getKeys());
} else { } else {
assertEquals(keySetId1, ks.getKeySetId()); assertEquals(keySetId1, ks.getKeySetId());
assertKeysEquals(rotated1, ks.getTransportKeys()); assertKeysEquals(rotated1, ks.getKeys());
} }
} }
@@ -741,7 +702,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private void assertKeysEquals(TransportKeys expected, private void assertKeysEquals(TransportKeys expected,
TransportKeys actual) { TransportKeys actual) {
assertEquals(expected.getTransportId(), actual.getTransportId()); assertEquals(expected.getTransportId(), actual.getTransportId());
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod()); assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertKeysEquals(expected.getPreviousIncomingKeys(), assertKeysEquals(expected.getPreviousIncomingKeys(),
actual.getPreviousIncomingKeys()); actual.getPreviousIncomingKeys());
assertKeysEquals(expected.getCurrentIncomingKeys(), assertKeysEquals(expected.getCurrentIncomingKeys(),
@@ -757,7 +718,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
actual.getTagKey().getBytes()); actual.getTagKey().getBytes());
assertArrayEquals(expected.getHeaderKey().getBytes(), assertArrayEquals(expected.getHeaderKey().getBytes(),
actual.getHeaderKey().getBytes()); actual.getHeaderKey().getBytes());
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod()); assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertEquals(expected.getWindowBase(), actual.getWindowBase()); assertEquals(expected.getWindowBase(), actual.getWindowBase());
assertArrayEquals(expected.getWindowBitmap(), actual.getWindowBitmap()); assertArrayEquals(expected.getWindowBitmap(), actual.getWindowBitmap());
} }
@@ -767,39 +728,240 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
actual.getTagKey().getBytes()); actual.getTagKey().getBytes());
assertArrayEquals(expected.getHeaderKey().getBytes(), assertArrayEquals(expected.getHeaderKey().getBytes(),
actual.getHeaderKey().getBytes()); actual.getHeaderKey().getBytes());
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod()); assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertEquals(expected.getStreamCounter(), actual.getStreamCounter()); assertEquals(expected.getStreamCounter(), actual.getStreamCounter());
assertEquals(expected.isActive(), actual.isActive()); assertEquals(expected.isActive(), actual.isActive());
} }
@Test
public void testHandshakeKeys() throws Exception {
long timePeriod = 123, timePeriod1 = 234;
boolean alice = random.nextBoolean();
SecretKey rootKey = getSecretKey();
SecretKey rootKey1 = getSecretKey();
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no handshake keys in the database
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
// Add the contact, the transport and the handshake keys
assertEquals(contactId, db.addContact(txn, author, true));
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
assertEquals(handshakeKeySetId1,
db.addHandshakeKeys(txn, contactId, keys1));
// Retrieve the handshake keys
Collection<HandshakeKeySet> allKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys());
}
}
// Update the handshake keys
HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice);
HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
contactId, updated));
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
contactId, updated1));
// Retrieve the handshake keys again
allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertEquals(contactId, ks.getContactId());
assertNull(ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys());
}
}
// Removing the contact should remove the handshake keys
db.removeContact(txn, contactId);
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
db.commitTransaction(txn);
db.close();
}
private void assertKeysEquals(HandshakeKeys expected,
HandshakeKeys actual) {
assertEquals(expected.getTransportId(), actual.getTransportId());
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
assertArrayEquals(expected.getRootKey().getBytes(),
actual.getRootKey().getBytes());
assertEquals(expected.isAlice(), actual.isAlice());
assertKeysEquals(expected.getPreviousIncomingKeys(),
actual.getPreviousIncomingKeys());
assertKeysEquals(expected.getCurrentIncomingKeys(),
actual.getCurrentIncomingKeys());
assertKeysEquals(expected.getNextIncomingKeys(),
actual.getNextIncomingKeys());
assertKeysEquals(expected.getCurrentOutgoingKeys(),
actual.getCurrentOutgoingKeys());
}
@Test
public void testHandshakeKeysForPendingContact() throws Exception {
long timePeriod = 123, timePeriod1 = 234;
boolean alice = random.nextBoolean();
SecretKey rootKey = getSecretKey();
SecretKey rootKey1 = getSecretKey();
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no handshake keys in the database
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
// Add the pending contact, the transport and the handshake keys
db.addPendingContact(txn, pendingContact);
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId, db.addHandshakeKeys(txn,
pendingContact.getId(), keys));
assertEquals(handshakeKeySetId1, db.addHandshakeKeys(txn,
pendingContact.getId(), keys1));
// Retrieve the handshake keys
Collection<HandshakeKeySet> allKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(keys, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(keys1, ks.getKeys());
}
}
// Update the handshake keys
HandshakeKeys updated =
createHandshakeKeys(timePeriod + 1, rootKey, alice);
HandshakeKeys updated1 =
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
pendingContact.getId(), updated));
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
pendingContact.getId(), updated1));
// Retrieve the handshake keys again
allKeys = db.getHandshakeKeys(txn, transportId);
assertEquals(2, allKeys.size());
for (HandshakeKeySet ks : allKeys) {
assertNull(ks.getContactId());
assertEquals(pendingContact.getId(), ks.getPendingContactId());
if (ks.getKeySetId().equals(handshakeKeySetId)) {
assertKeysEquals(updated, ks.getKeys());
} else {
assertEquals(handshakeKeySetId1, ks.getKeySetId());
assertKeysEquals(updated1, ks.getKeys());
}
}
// Removing the pending contact should remove the handshake keys
db.removePendingContact(txn, pendingContact.getId());
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testIncrementStreamCounter() throws Exception { public void testIncrementStreamCounter() throws Exception {
long rotationPeriod = 123; long timePeriod = 123;
TransportKeys keys = createTransportKeys(rotationPeriod, true); TransportKeys keys = createTransportKeys(timePeriod, true);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter(); long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
// Increment the stream counter twice and retrieve the transport keys // Increment the stream counter twice and retrieve the transport keys
db.incrementStreamCounter(txn, transportId, keySetId); db.incrementStreamCounter(txn, transportId, keySetId);
db.incrementStreamCounter(txn, transportId, keySetId); db.incrementStreamCounter(txn, transportId, keySetId);
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> newKeys =
db.getTransportKeys(txn, transportId);
assertEquals(1, newKeys.size()); assertEquals(1, newKeys.size());
KeySet ks = newKeys.iterator().next(); TransportKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId()); assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId()); assertEquals(transportId, k.getTransportId());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
assertEquals(rotationPeriod, outCurr.getRotationPeriod()); assertEquals(timePeriod, outCurr.getTimePeriod());
assertEquals(streamCounter + 2, outCurr.getStreamCounter());
// The rest of the keys should be unaffected
assertKeysEquals(keys.getPreviousIncomingKeys(),
k.getPreviousIncomingKeys());
assertKeysEquals(keys.getCurrentIncomingKeys(),
k.getCurrentIncomingKeys());
assertKeysEquals(keys.getNextIncomingKeys(), k.getNextIncomingKeys());
db.commitTransaction(txn);
db.close();
}
@Test
public void testIncrementStreamCounterForHandshakeKeys() throws Exception {
long timePeriod = 123;
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean();
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the contact, transport and handshake keys
assertEquals(contactId, db.addContact(txn, author, true));
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
// Increment the stream counter twice and retrieve the handshake keys
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
Collection<HandshakeKeySet> newKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size());
HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
assertEquals(timePeriod, outCurr.getTimePeriod());
assertEquals(streamCounter + 2, outCurr.getStreamCounter()); assertEquals(streamCounter + 2, outCurr.getStreamCounter());
// The rest of the keys should be unaffected // The rest of the keys should be unaffected
@@ -816,8 +978,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
@Test @Test
public void testSetReorderingWindow() throws Exception { public void testSetReorderingWindow() throws Exception {
boolean active = random.nextBoolean(); boolean active = random.nextBoolean();
long rotationPeriod = 123; long timePeriod = 123;
TransportKeys keys = createTransportKeys(rotationPeriod, active); TransportKeys keys = createTransportKeys(timePeriod, active);
long base = keys.getCurrentIncomingKeys().getWindowBase(); long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap(); byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
@@ -825,25 +987,24 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, active));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
// Update the reordering window and retrieve the transport keys // Update the reordering window and retrieve the transport keys
random.nextBytes(bitmap); random.nextBytes(bitmap);
db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod, db.setReorderingWindow(txn, keySetId, transportId, timePeriod,
base + 1, bitmap); base + 1, bitmap);
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId); Collection<TransportKeySet> newKeys =
db.getTransportKeys(txn, transportId);
assertEquals(1, newKeys.size()); assertEquals(1, newKeys.size());
KeySet ks = newKeys.iterator().next(); TransportKeySet ks = newKeys.iterator().next();
assertEquals(keySetId, ks.getKeySetId()); assertEquals(keySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId()); assertEquals(contactId, ks.getContactId());
TransportKeys k = ks.getTransportKeys(); TransportKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId()); assertEquals(transportId, k.getTransportId());
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
assertEquals(rotationPeriod, inCurr.getRotationPeriod()); assertEquals(timePeriod, inCurr.getTimePeriod());
assertEquals(base + 1, inCurr.getWindowBase()); assertEquals(base + 1, inCurr.getWindowBase());
assertArrayEquals(bitmap, inCurr.getWindowBitmap()); assertArrayEquals(bitmap, inCurr.getWindowBitmap());
@@ -859,54 +1020,90 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
} }
@Test @Test
public void testGetContactsByAuthorId() throws Exception { public void testSetReorderingWindowForHandshakeKeys() throws Exception {
long timePeriod = 123;
SecretKey rootKey = getSecretKey();
boolean alice = random.nextBoolean();
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
long base = keys.getCurrentIncomingKeys().getWindowBase();
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a local author - no contacts should be associated // Add the contact, transport and handshake keys
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author,true));
db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys));
// Add a contact associated with the local author // Update the reordering window and retrieve the handshake keys
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), random.nextBytes(bitmap);
true, true)); db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod,
base + 1, bitmap);
Collection<HandshakeKeySet> newKeys =
db.getHandshakeKeys(txn, transportId);
assertEquals(1, newKeys.size());
HandshakeKeySet ks = newKeys.iterator().next();
assertEquals(handshakeKeySetId, ks.getKeySetId());
assertEquals(contactId, ks.getContactId());
HandshakeKeys k = ks.getKeys();
assertEquals(transportId, k.getTransportId());
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
assertEquals(alice, k.isAlice());
IncomingKeys inCurr = k.getCurrentIncomingKeys();
assertEquals(timePeriod, inCurr.getTimePeriod());
assertEquals(base + 1, inCurr.getWindowBase());
assertArrayEquals(bitmap, inCurr.getWindowBitmap());
// Ensure contact is returned from database by Author ID // The rest of the keys should be unaffected
Collection<Contact> contacts = assertKeysEquals(keys.getPreviousIncomingKeys(),
db.getContactsByAuthorId(txn, author.getId()); k.getPreviousIncomingKeys());
assertEquals(1, contacts.size()); assertKeysEquals(keys.getNextIncomingKeys(), k.getNextIncomingKeys());
assertEquals(contactId, contacts.iterator().next().getId()); assertKeysEquals(keys.getCurrentOutgoingKeys(),
k.getCurrentOutgoingKeys());
// Ensure no contacts are returned after contact was deleted
db.removeContact(txn, contactId);
contacts = db.getContactsByAuthorId(txn, author.getId());
assertEquals(0, contacts.size());
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
@Test @Test
public void testGetContactsByLocalAuthorId() throws Exception { public void testGetContactByContactId() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a local author - no contacts should be associated // Add a contact
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
Collection<ContactId> contacts =
db.getContacts(txn, localAuthor.getId());
assertEquals(emptyList(), contacts);
// Add a contact associated with the local author // Check the contact is returned
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(), Contact c = db.getContact(txn, contactId);
true, true)); assertEquals(contactId, c.getId());
contacts = db.getContacts(txn, localAuthor.getId()); assertEquals(author.getId(), c.getAuthor().getId());
assertEquals(singletonList(contactId), contacts); assertEquals(author.getFormatVersion(),
c.getAuthor().getFormatVersion());
assertEquals(author.getName(), c.getAuthor().getName());
assertArrayEquals(author.getPublicKey(), c.getAuthor().getPublicKey());
// Remove the local author - the contact should be removed db.commitTransaction(txn);
db.removeLocalAuthor(txn, localAuthor.getId()); db.close();
contacts = db.getContacts(txn, localAuthor.getId()); }
assertEquals(emptyList(), contacts);
assertFalse(db.containsContact(txn, contactId)); @Test
public void testGetContactByAuthorId() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
assertEquals(contactId, db.addContact(txn, author, true));
// Check the contact is returned
Contact c = db.getContact(txn, author.getId());
assertEquals(contactId, c.getId());
assertEquals(author.getId(), c.getAuthor().getId());
assertEquals(author.getFormatVersion(),
c.getAuthor().getFormatVersion());
assertEquals(author.getName(), c.getAuthor().getName());
assertArrayEquals(author.getPublicKey(), c.getAuthor().getPublicKey());
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -918,9 +1115,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact - initially there should be no offered messages // Add a contact - initially there should be no offered messages
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
assertEquals(0, db.countOfferedMessages(txn, contactId)); assertEquals(0, db.countOfferedMessages(txn, contactId));
// Add some offered messages and count them // Add some offered messages and count them
@@ -1502,9 +1697,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1601,43 +1794,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testDifferentLocalAuthorsCanHaveTheSameContact()
throws Exception {
LocalAuthor localAuthor1 = getLocalAuthor();
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add two local authors
db.addLocalAuthor(txn, localAuthor);
db.addLocalAuthor(txn, localAuthor1);
// Add the same contact for each local author
ContactId contactId =
db.addContact(txn, author, localAuthor.getId(), true, true);
ContactId contactId1 =
db.addContact(txn, author, localAuthor1.getId(), true, true);
// The contacts should be distinct
assertNotEquals(contactId, contactId1);
assertEquals(2, db.getContacts(txn).size());
assertEquals(1, db.getContacts(txn, localAuthor.getId()).size());
assertEquals(1, db.getContacts(txn, localAuthor1.getId()).size());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testDeleteMessage() throws Exception { public void testDeleteMessage() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1683,47 +1846,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testSetContactActive() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
// The contact should be active
Contact contact = db.getContact(txn, contactId);
assertTrue(contact.isActive());
// Set the contact inactive
db.setContactActive(txn, contactId, false);
// The contact should be inactive
contact = db.getContact(txn, contactId);
assertFalse(contact.isActive());
// Set the contact active
db.setContactActive(txn, contactId, true);
// The contact should be active
contact = db.getContact(txn, contactId);
assertTrue(contact.isActive());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testSetContactAlias() throws Exception { public void testSetContactAlias() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
// The contact should have no alias // The contact should have no alias
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
@@ -1778,9 +1907,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and a message // Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false, null); db.addMessage(txn, message, UNKNOWN, false, null);
@@ -1862,9 +1989,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1907,9 +2032,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addLocalAuthor(txn, localAuthor); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1990,41 +2113,94 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testPendingContacts() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
assertEquals(emptyList(), db.getPendingContacts(txn));
db.addPendingContact(txn, pendingContact);
Collection<PendingContact> pendingContacts =
db.getPendingContacts(txn);
assertEquals(1, pendingContacts.size());
PendingContact retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertEquals(pendingContact.getState(), retrieved.getState());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.setPendingContactState(txn, pendingContact.getId(), FAILED);
pendingContacts = db.getPendingContacts(txn);
assertEquals(1, pendingContacts.size());
retrieved = pendingContacts.iterator().next();
assertEquals(pendingContact.getId(), retrieved.getId());
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
assertEquals(FAILED, retrieved.getState());
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
db.removePendingContact(txn, pendingContact.getId());
assertEquals(emptyList(), db.getPendingContacts(txn));
db.commitTransaction(txn);
db.close();
}
private Database<Connection> open(boolean resume) throws Exception { private Database<Connection> open(boolean resume) throws Exception {
return open(resume, new TestMessageFactory(), new SystemClock()); return open(resume, new TestMessageFactory(), new SystemClock());
} }
private Database<Connection> open(boolean resume, private Database<Connection> open(boolean resume,
MessageFactory messageFactory, Clock clock) throws Exception { MessageFactory messageFactory, Clock clock) throws Exception {
Database<Connection> db = Database<Connection> db = createDatabase(
createDatabase(new TestDatabaseConfig(testDir, MAX_SIZE), new TestDatabaseConfig(testDir), messageFactory, clock);
messageFactory, clock);
if (!resume) deleteTestDirectory(testDir); if (!resume) deleteTestDirectory(testDir);
db.open(key, null); db.open(key, null);
return db; return db;
} }
private TransportKeys createTransportKeys(long rotationPeriod, private TransportKeys createTransportKeys(long timePeriod, boolean active) {
boolean active) {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey, IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
rotationPeriod - 1, 123, new byte[4]); timePeriod - 1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey(); SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey(); SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey, IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
rotationPeriod, 234, new byte[4]); timePeriod, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey(); SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey(); SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey, IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
rotationPeriod + 1, 345, new byte[4]); timePeriod + 1, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey(); SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey(); SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey, OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
rotationPeriod, 456, active); timePeriod, 456, active);
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr); return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
} }
private HandshakeKeys createHandshakeKeys(long timePeriod,
SecretKey rootKey, boolean alice) {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
timePeriod - 1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
timePeriod, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
timePeriod + 1, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
timePeriod, 456, true);
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
rootKey, alice);
}
@After @After
public void tearDown() { public void tearDown() {
deleteTestDirectory(testDir); deleteTestDirectory(testDir);

View File

@@ -45,8 +45,8 @@ public abstract class SingleDatabasePerformanceTest
private Database<Connection> openDatabase() throws DbException { private Database<Connection> openDatabase() throws DbException {
Database<Connection> db = createDatabase( Database<Connection> db = createDatabase(
new TestDatabaseConfig(testDir, MAX_SIZE), new TestDatabaseConfig(testDir), new TestMessageFactory(),
new TestMessageFactory(), new SystemClock()); new SystemClock());
db.open(databaseKey, null); db.open(databaseKey, null);
return db; return db;
} }

View File

@@ -17,7 +17,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
@@ -74,7 +74,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
Payload ourPayload = new Payload(aliceCommit, null); Payload ourPayload = new Payload(aliceCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
SecretKey masterSecret = getSecretKey(); SecretKey masterKey = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport, crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -134,13 +134,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
true, false); true, false);
will(returnValue(bobConfirm)); will(returnValue(bobConfirm));
// Alice computes master secret // Alice derives master key
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); oneOf(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
will(returnValue(masterSecret)); will(returnValue(masterKey));
}}); }});
// execute // execute
assertThat(masterSecret, is(equalTo(protocol.perform()))); assertThat(masterKey, is(equalTo(protocol.perform())));
} }
@Test @Test
@@ -150,7 +150,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
Payload ourPayload = new Payload(bobCommit, null); Payload ourPayload = new Payload(bobCommit, null);
KeyPair ourKeyPair = new KeyPair(ourPubKey, null); KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
SecretKey sharedSecret = getSecretKey(); SecretKey sharedSecret = getSecretKey();
SecretKey masterSecret = getSecretKey(); SecretKey masterKey = getSecretKey();
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks, KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
crypto, keyAgreementCrypto, payloadEncoder, transport, crypto, keyAgreementCrypto, payloadEncoder, transport,
@@ -209,13 +209,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
will(returnValue(bobConfirm)); will(returnValue(bobConfirm));
oneOf(transport).sendConfirm(bobConfirm); oneOf(transport).sendConfirm(bobConfirm);
// Bob computes master secret // Bob derives master key
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); oneOf(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
will(returnValue(masterSecret)); will(returnValue(masterKey));
}}); }});
// execute // execute
assertThat(masterSecret, is(equalTo(protocol.perform()))); assertThat(masterKey, is(equalTo(protocol.perform())));
} }
@Test(expected = AbortException.class) @Test(expected = AbortException.class)
@@ -373,8 +373,8 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
// Alice aborts // Alice aborts
oneOf(transport).sendAbort(false); oneOf(transport).sendAbort(false);
// Alice never computes master secret // Alice never derives master key
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret); never(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
}}); }});
// execute // execute

View File

@@ -17,6 +17,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -28,8 +29,8 @@ public class ConnectionRegistryImplTest extends BrambleTestCase {
private final TransportId transportId, transportId1; private final TransportId transportId, transportId1;
public ConnectionRegistryImplTest() { public ConnectionRegistryImplTest() {
contactId = new ContactId(1); contactId = getContactId();
contactId1 = new ContactId(2); contactId1 = getContactId();
transportId = getTransportId(); transportId = getTransportId();
transportId1 = getTransportId(); transportId1 = getTransportId();
} }

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
@@ -36,6 +36,7 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class PollerTest extends BrambleMockTestCase { public class PollerTest extends BrambleMockTestCase {
@@ -56,7 +57,7 @@ public class PollerTest extends BrambleMockTestCase {
private final Executor ioExecutor = new ImmediateExecutor(); private final Executor ioExecutor = new ImmediateExecutor();
private final TransportId transportId = getTransportId(); private final TransportId transportId = getTransportId();
private final ContactId contactId = new ContactId(234); private final ContactId contactId = getContactId();
private final TransportProperties properties = new TransportProperties(); private final TransportProperties properties = new TransportProperties();
private final int pollingInterval = 60 * 1000; private final int pollingInterval = 60 * 1000;
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
@@ -67,7 +68,7 @@ public class PollerTest extends BrambleMockTestCase {
} }
@Test @Test
public void testConnectOnContactStatusChanged() throws Exception { public void testConnectOnContactAdded() throws Exception {
// Two simplex plugins: one supports polling, the other doesn't // Two simplex plugins: one supports polling, the other doesn't
SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class); SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
SimplexPlugin simplexPlugin1 = SimplexPlugin simplexPlugin1 =
@@ -143,7 +144,7 @@ public class PollerTest extends BrambleMockTestCase {
connectionRegistry, pluginManager, transportPropertyManager, connectionRegistry, pluginManager, transportPropertyManager,
random, clock); random, clock);
p.eventOccurred(new ContactStatusChangedEvent(contactId, true)); p.eventOccurred(new ContactAddedEvent(contactId));
} }
@Test @Test

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -36,12 +37,11 @@ import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID; import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION; import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -49,6 +49,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class); private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ClientVersioningManager clientVersioningManager = private final ClientVersioningManager clientVersioningManager =
context.mock(ClientVersioningManager.class); context.mock(ClientVersioningManager.class);
private final MetadataParser metadataParser = private final MetadataParser metadataParser =
@@ -69,8 +71,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
); );
private final TransportProperties fooProperties, barProperties; private final TransportProperties fooProperties, barProperties;
private int nextContactId = 0;
public TransportPropertyManagerImplTest() throws Exception { public TransportPropertyManagerImplTest() throws Exception {
fooProperties = new TransportProperties(); fooProperties = new TransportProperties();
for (String key : fooPropertiesDict.keySet()) for (String key : fooPropertiesDict.keySet())
@@ -87,24 +87,26 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(localGroup)); will(returnValue(localGroup));
}}); }});
return new TransportPropertyManagerImpl(db, clientHelper, return new TransportPropertyManagerImpl(db, clientHelper,
clientVersioningManager, metadataParser, contactGroupFactory, identityManager, clientVersioningManager, metadataParser,
clock); contactGroupFactory, clock);
} }
@Test @Test
public void testCreatesGroupsAtStartup() throws Exception { public void testCreatesGroupsAtStartup() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn, localGroup.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(db).addGroup(txn, localGroup); oneOf(db).addGroup(txn, localGroup);
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn, oneOf(clientVersioningManager).getClientVisibility(txn,
@@ -141,13 +143,15 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testCreatesContactGroupWhenAddingContact() throws Exception { public void testCreatesContactGroupWhenAddingContact() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Create the group and share it with the contact // Create the group and share it with the contact
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn, oneOf(clientVersioningManager).getClientVisibility(txn,
@@ -170,12 +174,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testRemovesGroupWhenRemovingContact() throws Exception { public void testRemovesGroupWhenRemovingContact() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup); oneOf(db).removeGroup(txn, contactGroup);
}}); }});
@@ -302,7 +308,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testStoresRemotePropertiesWithVersion0() throws Exception { public void testStoresRemotePropertiesWithVersion0() throws Exception {
Contact contact = getContact(true); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Map<TransportId, TransportProperties> properties = Map<TransportId, TransportProperties> properties =
@@ -313,8 +319,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
}}); }});
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict, expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
@@ -406,31 +414,30 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testReturnsRemotePropertiesOrEmptyProperties() public void testReturnsRemotePropertiesOrEmptyProperties()
throws Exception { throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
Contact contact1 = getContact(false); Contact contact1 = getContact();
Contact contact2 = getContact(true); Contact contact2 = getContact();
Contact contact3 = getContact(true); List<Contact> contacts = asList(contact1, contact2);
List<Contact> contacts = asList(contact1, contact2, contact3); Group contactGroup1 = getGroup(CLIENT_ID, MAJOR_VERSION);
Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
Group contactGroup3 = getGroup(CLIENT_ID, MAJOR_VERSION); Map<MessageId, BdfDictionary> messageMetadata2 =
Map<MessageId, BdfDictionary> messageMetadata3 =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A remote update for another transport should be ignored // A remote update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata3.put(barUpdateId, BdfDictionary.of( messageMetadata2.put(barUpdateId, BdfDictionary.of(
new BdfEntry("transportId", "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry("version", 1), new BdfEntry("version", 1),
new BdfEntry("local", false) new BdfEntry("local", false)
)); ));
// A local update for the right transport should be ignored // A local update for the right transport should be ignored
MessageId localUpdateId = new MessageId(getRandomId()); MessageId localUpdateId = new MessageId(getRandomId());
messageMetadata3.put(localUpdateId, BdfDictionary.of( messageMetadata2.put(localUpdateId, BdfDictionary.of(
new BdfEntry("transportId", "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry("version", 1), new BdfEntry("version", 1),
new BdfEntry("local", true) new BdfEntry("local", true)
)); ));
// A remote update for the right transport should be returned // A remote update for the right transport should be returned
MessageId fooUpdateId = new MessageId(getRandomId()); MessageId fooUpdateId = new MessageId(getRandomId());
messageMetadata3.put(fooUpdateId, BdfDictionary.of( messageMetadata2.put(fooUpdateId, BdfDictionary.of(
new BdfEntry("transportId", "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry("version", 1), new BdfEntry("version", 1),
new BdfEntry("local", false) new BdfEntry("local", false)
@@ -439,23 +446,24 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(contacts)); will(returnValue(contacts));
// First contact: skipped because not active // First contact: no updates
// Second contact: no updates
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact2); MAJOR_VERSION, contact1, localAuthor.getId());
will(returnValue(contactGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup1.getId());
will(returnValue(Collections.emptyMap()));
// Second contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact2, localAuthor.getId());
will(returnValue(contactGroup2)); will(returnValue(contactGroup2));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup2.getId()); contactGroup2.getId());
will(returnValue(Collections.emptyMap())); will(returnValue(messageMetadata2));
// Third contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact3);
will(returnValue(contactGroup3));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup3.getId());
will(returnValue(messageMetadata3));
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId); oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
will(returnValue(fooUpdate)); will(returnValue(fooUpdate));
oneOf(clientHelper).parseAndValidateTransportProperties( oneOf(clientHelper).parseAndValidateTransportProperties(
@@ -466,10 +474,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
Map<ContactId, TransportProperties> properties = Map<ContactId, TransportProperties> properties =
t.getRemoteProperties(new TransportId("foo")); t.getRemoteProperties(new TransportId("foo"));
assertEquals(3, properties.size()); assertEquals(2, properties.size());
assertEquals(0, properties.get(contact1.getId()).size()); assertEquals(0, properties.get(contact1.getId()).size());
assertEquals(0, properties.get(contact2.getId()).size()); assertEquals(fooProperties, properties.get(contact2.getId()));
assertEquals(fooProperties, properties.get(contact3.getId()));
} }
@Test @Test
@@ -506,7 +513,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testMergingNewPropertiesCreatesUpdate() throws Exception { public void testMergingNewPropertiesCreatesUpdate() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
@@ -519,10 +526,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
expectStoreMessage(txn, localGroup.getId(), "foo", expectStoreMessage(txn, localGroup.getId(), "foo",
fooPropertiesDict, 1, true, false); fooPropertiesDict, 1, true, false);
// Store the new properties in each contact's group, version 1 // Store the new properties in each contact's group, version 1
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
@@ -538,7 +547,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception { public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary oldMetadata = BdfDictionary.of( BdfDictionary oldMetadata = BdfDictionary.of(
new BdfEntry("transportId", "foo"), new BdfEntry("transportId", "foo"),
@@ -575,10 +584,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// Delete the previous update // Delete the previous update
oneOf(db).removeMessage(txn, localGroupUpdateId); oneOf(db).removeMessage(txn, localGroupUpdateId);
// Store the merged properties in each contact's group, version 2 // Store the merged properties in each contact's group, version 2
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
@@ -593,12 +604,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
t.mergeLocalProperties(new TransportId("foo"), fooProperties); t.mergeLocalProperties(new TransportId("foo"), fooProperties);
} }
private Contact getContact(boolean active) {
ContactId c = new ContactId(nextContactId++);
return new Contact(c, getAuthor(), localAuthor.getId(),
getRandomString(5), true, active);
}
private void expectGetLocalProperties(Transaction txn) throws Exception { private void expectGetLocalProperties(Transaction txn) throws Exception {
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>(); Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
// The latest update for transport "foo" should be returned // The latest update for transport "foo" should be returned

View File

@@ -19,6 +19,7 @@ import java.util.concurrent.Executor;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
@@ -33,7 +34,7 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
context.mock(SyncRecordWriter.class); context.mock(SyncRecordWriter.class);
private final Executor dbExecutor = new ImmediateExecutor(); private final Executor dbExecutor = new ImmediateExecutor();
private final ContactId contactId = new ContactId(234); private final ContactId contactId = getContactId();
private final Message message = getMessage(new GroupId(getRandomId())); private final Message message = getMessage(new GroupId(getRandomId()));
private final MessageId messageId = message.getId(); private final MessageId messageId = message.getId();

View File

@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -37,6 +36,8 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -73,11 +74,11 @@ public class SyncIntegrationTest extends BrambleTestCase {
DaggerSyncIntegrationTestComponent.builder().build(); DaggerSyncIntegrationTestComponent.builder().build();
component.inject(this); component.inject(this);
contactId = new ContactId(234); contactId = getContactId();
transportId = getTransportId(); transportId = getTransportId();
// Create the transport keys // Create the transport keys
tagKey = TestUtils.getSecretKey(); tagKey = getSecretKey();
headerKey = TestUtils.getSecretKey(); headerKey = getSecretKey();
streamNumber = 123; streamNumber = 123;
// Create a group // Create a group
ClientId clientId = getClientId(); ClientId clientId = getClientId();

View File

@@ -37,6 +37,7 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING; import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN; import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
@@ -64,7 +65,7 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
private final Metadata metadata = new Metadata(); private final Metadata metadata = new Metadata();
private final MessageContext validResult = new MessageContext(metadata); private final MessageContext validResult = new MessageContext(metadata);
private final ContactId contactId = new ContactId(234); private final ContactId contactId = getContactId();
private final MessageContext validResultWithDependencies = private final MessageContext validResultWithDependencies =
new MessageContext(metadata, singletonList(messageId1)); new MessageContext(metadata, singletonList(messageId1));

View File

@@ -9,12 +9,10 @@ import java.io.File;
public class TestDatabaseConfig implements DatabaseConfig { public class TestDatabaseConfig implements DatabaseConfig {
private final File dbDir, keyDir; private final File dbDir, keyDir;
private final long maxSize;
public TestDatabaseConfig(File testDir, long maxSize) { public TestDatabaseConfig(File testDir) {
dbDir = new File(testDir, "db"); dbDir = new File(testDir, "db");
keyDir = new File(testDir, "key"); keyDir = new File(testDir, "key");
this.maxSize = maxSize;
} }
@Override @Override
@@ -26,9 +24,4 @@ public class TestDatabaseConfig implements DatabaseConfig {
public File getDatabaseKeyDirectory() { public File getDatabaseKeyDirectory() {
return keyDir; return keyDir;
} }
@Override
public long getMaxSize() {
return maxSize;
}
} }

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