Compare commits

...

338 Commits

Author SHA1 Message Date
akwizgran
37d0b61d7b Bump version numbers for 1.1.7 release. 2019-05-16 16:07:15 +01:00
akwizgran
98a1ec89d7 Update translations. 2019-05-16 16:06:29 +01:00
akwizgran
a61860af85 Merge branch '1369-thread-layout' into 'master'
Adapt private group join notices to new threaded layout

Closes #1369

See merge request briar/briar!1097
2019-05-16 14:54:46 +00:00
Torsten Grote
90437f4fa4 [android] use same color for thread dividers than indentation dividers 2019-05-16 11:35:20 -03:00
Torsten Grote
06212453b2 [android] Adapt private group join notices to new threaded layout 2019-05-16 11:12:48 -03:00
akwizgran
ddc9b5b066 Merge branch '1256-adding-contacts-headless' into 'master'
Add a REST endpoint for adding contacts

Closes #1256

See merge request briar/briar!1094
2019-05-16 14:05:48 +00:00
Torsten Grote
0aa6166afa Merge branch 'account-logging' into 'master'
Log contents of account directory for debugging

See merge request briar/briar!1096
2019-05-16 11:51:46 +00:00
akwizgran
60b91bc317 Log contents of account directory for debugging. 2019-05-16 10:06:16 +01:00
akwizgran
20481a3790 Merge branch '1369-thread-layout' into 'master'
Move AuthorView up in threaded conversation

Closes #1369

See merge request briar/briar!1095
2019-05-16 08:46:39 +00:00
Torsten Grote
576934910e Merge branch '1566-equivalent-public-keys' into 'master'
Add tests for equivalent handshake public keys

See merge request briar/briar!1093
2019-05-15 20:02:52 +00:00
Torsten Grote
4abc26093e Merge branch '1556-key-manager-methods-for-pending-contacts' into 'master'
Add key manager methods for pending contacts

Closes #1556

See merge request briar/briar!1089
2019-05-15 19:57:32 +00:00
Torsten Grote
aed63746e7 [android] Move AuthorView up in threaded conversation 2019-05-15 16:33:06 -03:00
Torsten Grote
816598b631 [headless] only include alias in contact's JSON representation if it exists 2019-05-15 16:03:02 -03:00
Torsten Grote
c062c16d27 Merge branch 'merge-handshake-and-transport-keys' into 'master'
Add support for handshake keys to KeyManager

See merge request briar/briar!1088
2019-05-15 16:27:33 +00:00
Torsten Grote
435b43488a [headless] address review comments for remote contact adding 2019-05-15 12:36:33 -03:00
Torsten Grote
faa6a85148 [headless] Add tests to ensure that remote contact adding needs auth token 2019-05-15 11:28:01 -03:00
Torsten Grote
3770a9f217 [headless] make events related to adding contacts available via websocket 2019-05-15 11:28:01 -03:00
Torsten Grote
c6211be488 [bramble-core] Broadcast events when pending contacts are added or removed 2019-05-15 11:27:59 -03:00
Torsten Grote
5a73e50248 [headless] expose ContactManager methods for adding contacts remotely 2019-05-15 11:26:21 -03:00
Torsten Grote
dc6971734a [briar-core] Add a getRealHandshakeLink() method to BriarTestUtils
Also allow testOutput from briar-core to be used in briar-headless
2019-05-15 11:26:20 -03:00
Torsten Grote
69e57bee61 [bramble] Let TestUtils return a PendingContact with random state 2019-05-15 11:26:19 -03:00
Torsten Grote
af8cabbb28 [headless] update dependencies 2019-05-15 11:25:11 -03:00
akwizgran
6f31a3c2ad Merge branch 'key-pair-refactoring' into 'master'
Key pair refactoring

See merge request briar/briar!1083
2019-05-15 14:19:48 +00:00
akwizgran
d3469e3782 Merge branch '1482-startup-activity' into 'master'
Combine Password and OpenDatabase Activity into StartupActivity

Closes #1482

See merge request briar/briar!1087
2019-05-15 09:57:01 +00:00
akwizgran
9d64b186ff Add tests for hashing public keys into shared secret. 2019-05-15 10:18:13 +01:00
akwizgran
ca591b5c7b Add test for equivalent public keys. 2019-05-15 10:18:09 +01:00
akwizgran
2c4188caf5 Use lambdas for tasks requiring a manager lookup. 2019-05-14 17:59:35 +01:00
akwizgran
0b30a0786e Rename key manager methods for clarity. 2019-05-14 17:59:35 +01:00
akwizgran
f9b928c12a Annotate equals() argument as nullable. 2019-05-14 17:59:35 +01:00
akwizgran
afa0b96293 Add utility method for null checks. 2019-05-14 17:59:34 +01:00
akwizgran
dd50f4bcd4 Add key manager methods for pending contacts. 2019-05-14 17:59:34 +01:00
akwizgran
f42fc5213e Add key manager method for contacts with handshake keys. 2019-05-14 17:59:34 +01:00
akwizgran
84e2402404 Update key management terminology. 2019-05-14 17:57:23 +01:00
akwizgran
5adc9d8dbd Add handshake keys to TransportKeyManagerImpl. 2019-05-14 17:57:22 +01:00
akwizgran
3f51ad6c07 Add handshake mode to MutableTransportKeys. 2019-05-14 17:57:22 +01:00
akwizgran
1fd6d7a6d5 Use @GuardedBy annotation. 2019-05-14 17:57:22 +01:00
akwizgran
7dc4dc566f Merge handshake and transport keys. 2019-05-14 17:57:19 +01:00
akwizgran
658c63d94e Rename an argument for clarity. 2019-05-14 17:56:19 +01:00
akwizgran
ee05c32871 Allow pending contact state update not to affect any rows. 2019-05-14 17:55:38 +01:00
akwizgran
d2951eb3cd Rename key parser classes. 2019-05-14 17:26:28 +01:00
akwizgran
de8a60ea21 Use PublicKey and PrivateKey everywhere. 2019-05-14 17:26:26 +01:00
akwizgran
0e77a47cc1 Refactor key handling to use public classes. 2019-05-14 17:24:19 +01:00
Torsten Grote
421ca309c7 Merge branch '1538-create-handshake-key-pair' into 'master'
Generate and store handshake key pair at startup if necessary

Closes #1538

See merge request briar/briar!1082
2019-05-14 15:39:44 +00:00
akwizgran
43787deafd Address review comments. 2019-05-14 15:55:42 +01:00
Torsten Grote
22ebdd8e42 [android] Ensure keyboard is shown for entering password
in new StartupActivity and when creating a new account.
2019-05-13 11:58:07 -03:00
Torsten Grote
e37ee7ee04 [android] Use LiveEvent to communicate password validation and account deletion 2019-05-13 08:21:28 -03:00
Torsten Grote
5676e18a22 [android] StartupActivity: Address first round of review comments 2019-05-13 08:21:28 -03:00
Torsten Grote
5ece6505da [android] Combine Password and OpenDatabase Activity into StartupActivity 2019-05-13 08:21:27 -03:00
Torsten Grote
451edba467 Merge branch 'live-event-reduce-visibility' into 'master'
Reduce visibility of LiveEvent inner classes

See merge request briar/briar!1092
2019-05-10 16:52:49 +00:00
Torsten Grote
5880479987 Merge branch '1537-contact-manager-pending-contacts' into 'master'
Implement contact manager methods for pending contacts

Closes #1537

See merge request briar/briar!1081
2019-05-10 15:06:08 +00:00
akwizgran
71d8fb2083 Add unit tests for Base32 encoding and decoding. 2019-05-10 15:33:19 +01:00
akwizgran
0825e77dd7 Static import. 2019-05-10 15:11:44 +01:00
akwizgran
593a709a7f Remove redundant regex. 2019-05-10 15:06:12 +01:00
akwizgran
322fefb2a2 Use matcher to discard prefix if present. 2019-05-10 15:02:47 +01:00
akwizgran
8005cdc659 Reduce visibility of LiveEvent inner classes. 2019-05-10 10:20:49 +01:00
akwizgran
33fdca4aa1 Merge branch 'live-event' into 'master'
Migrate existing uses of event-like LiveData to LiveEvent

See merge request briar/briar!1090
2019-05-10 09:09:33 +00:00
akwizgran
e5fc91b620 Rename exceptions and events. 2019-05-10 10:02:49 +01:00
akwizgran
9c08073e49 Rename account to identity. 2019-05-10 10:02:49 +01:00
akwizgran
5553b7d0e4 Remove unused method. 2019-05-10 10:02:48 +01:00
akwizgran
2cce0f5fe2 Remove OpenDatabaseHook priorities. 2019-05-10 10:02:48 +01:00
akwizgran
ebae1037be Remove unnecessary null check. 2019-05-10 10:02:48 +01:00
akwizgran
0c99ef0e5b Clean up some duplicated code. 2019-05-10 10:02:48 +01:00
akwizgran
faba9a6b70 Generate handshake keys on demand, store when DB is opened. 2019-05-10 10:02:47 +01:00
akwizgran
891c82b2e5 Add javadocs to DB hook interfaces. 2019-05-10 10:02:47 +01:00
akwizgran
56fbc93962 Move handshake keys from LocalAuthor to Account. 2019-05-10 10:02:47 +01:00
akwizgran
251eb9e712 Add javadoc for handshakeKeys flag. 2019-05-10 10:02:47 +01:00
akwizgran
8b2b7599f9 Generate and store handshake keys at startup if needed. 2019-05-10 10:02:46 +01:00
akwizgran
8c315382e2 Add DB method for setting local handshake key pair. 2019-05-10 10:02:46 +01:00
akwizgran
8183a48ebb Add unit test for OpenDatabaseHook priority. 2019-05-10 10:02:45 +01:00
akwizgran
f6611daf7b Replace Client interface with OpenDatabaseHook. 2019-05-10 10:02:45 +01:00
akwizgran
00bc8ac768 Include handshake keys when loading all local authors. 2019-05-10 10:02:45 +01:00
akwizgran
75776eb7de Generate handshake keys when creating local author. 2019-05-10 10:02:45 +01:00
akwizgran
f0a3130bf3 Test that UnsupportedVersionException is thrown. 2019-05-10 10:01:32 +01:00
akwizgran
64aa121c9c Reuse UnsupportedVersionException for handshake links. 2019-05-10 10:01:22 +01:00
akwizgran
cc3486df94 Move UnsupportedVersionException to bramble.api package. 2019-05-10 10:01:06 +01:00
akwizgran
cd24be7e42 Add unit tests for pending contact factory. 2019-05-10 10:01:05 +01:00
akwizgran
fa562b40bc Implement contact manager methods for pending contacts. 2019-05-10 10:01:03 +01:00
akwizgran
fc8ca872a8 Add base32 encoder/decoder. 2019-05-10 09:59:16 +01:00
Torsten Grote
5b63eab314 [android] migrate existing uses of event-like LiveData to LiveEvent 2019-05-09 14:47:16 -03:00
akwizgran
6f0ab8b688 Merge branch '1234-remote-contacts' into 'master'
Implement UX for adding contacts remotely

Closes #1234

See merge request briar/briar!1035
2019-05-09 16:52:24 +00:00
Torsten Grote
dfc567cbfd [bramble] Remove PendingContact test code from ContactManagerImpl 2019-05-09 13:36:21 -03:00
Torsten Grote
de98a4cb12 [android] Introduce a (Mutable)LiveEvent for single-use LiveData 2019-05-09 13:20:09 -03:00
Torsten Grote
fbe375cc4e Use event instead of CommitAction to handle removed PendingContacts 2019-05-09 11:43:22 -03:00
Torsten Grote
19bc73ac61 [android] show Toast when user shares own handshake link
This also limits the AddContactActivity to run within one single task
2019-05-03 11:47:51 -03:00
Torsten Grote
d17331b578 [android] Set handshake link when received via sharing intent or link click 2019-05-03 10:18:47 -03:00
Torsten Grote
bec1f117ba Remote Contact Adding: Rename methods and add more exception handling 2019-05-03 09:48:20 -03:00
Torsten Grote
2c014b4e46 Only remove PendingContact from UI when removed from DB 2019-05-03 09:48:19 -03:00
Torsten Grote
7a71d2bad4 Remote Contact Adding UI: Address review comments 2019-05-03 09:48:19 -03:00
Torsten Grote
4bf21b2f3b [android] hide feature to add contacts remotely behind feature flag 2019-05-03 09:48:19 -03:00
Torsten Grote
4a57939b80 [android] finalize list of pending contacts and add test code 2019-05-03 09:48:19 -03:00
Torsten Grote
66cdf4f595 Refactored IntroductionSucceededEvent into more generic ContactAddedRemotelyEvent 2019-05-03 09:48:18 -03:00
Torsten Grote
3384477499 [android] Add BriarSnackbarBuilder to standardize snackbar creation 2019-05-03 09:48:18 -03:00
Torsten Grote
58ffc6e761 [android] rough sketch of UI for adding contacts remotely 2019-05-03 09:48:15 -03:00
akwizgran
df5ac59fc9 Merge branch 'gradle-android-3.4' into 'master'
Update the Android gradle plugin to version 3.4.0

See merge request briar/briar!1085
2019-05-03 09:36:46 +00:00
akwizgran
dc649b195a Merge branch '1552-send-controller-exception' into 'master'
Don't disable TextInputView directly, use controller

Closes #1552

See merge request briar/briar!1086
2019-05-01 09:38:34 +00:00
Torsten Grote
3d9a8f9bf8 [android] Use TextSendController to disable TextInputView
Fixes #1552
2019-04-26 13:45:49 -03:00
Torsten Grote
96975e0d43 Upgrade dagger, mockito and okhttp 2019-04-26 09:07:26 -03:00
Torsten Grote
6691e708e4 Update the Android gradle plugin to version 3.4.0 2019-04-25 20:55:30 -03: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
Torsten Grote
b342759e06 Merge branch '978-tor-only-on-battery' into 'master'
Add a setting to disable Tor when running on battery

Closes #978

See merge request briar/briar!1032
2019-02-06 14:46:33 +00:00
akwizgran
93d99b0111 Tweak wording of Tor battery setting. 2019-02-06 14:23:15 +00:00
akwizgran
61e8d576d2 Update mobile data log message, simplify logic. 2019-02-06 14:20:04 +00:00
Torsten Grote
75c37a258e Add a setting to disable Tor when running on battery 2019-02-05 13:46:26 -02:00
akwizgran
e964dae64b Merge branch '1468-image-size-tests' into 'master'
Add tests for parsing image sizes

See merge request briar/briar!1026
2019-01-15 17:26:55 +00:00
akwizgran
986d884b40 Refactor ImageManager to ImageHelper. 2019-01-15 17:14:57 +00:00
akwizgran
9557afabc6 Change MIME types to "image/jpeg", unsuppress warning. 2019-01-15 16:49:18 +00:00
Torsten Grote
ebe6b0d4c0 [android] Split up AttachmentController tests into integration and unit 2019-01-15 16:33:03 +00:00
Torsten Grote
6e83fb7aef [android] add tests for getting attachment items from AttachmentController 2019-01-15 16:33:00 +00:00
Torsten Grote
7a5ec2af12 [android] Add test for MarkEnforcingInputStream 2019-01-15 16:32:23 +00:00
akwizgran
ce1fde496c Merge branch '1477-check-attachment-support' into 'master'
Find out if contacts support image attachments and enable them

Closes #1477

See merge request briar/briar!1019
2019-01-15 15:35:48 +00:00
akwizgran
4b62c51fbf Revert to using a fixed delay for the onboarding. 2019-01-15 15:23:30 +00:00
akwizgran
226ed3dd73 Wrap long line, remove redundant variable. 2019-01-14 14:31:31 +00:00
akwizgran
ab07dfb32c Use expression lambda. 2019-01-14 14:26:09 +00:00
akwizgran
20c51c1aa4 Group together fields with the same access restrictions. 2019-01-14 14:25:32 +00:00
Torsten Grote
232c2129a7 [android] use a LiveData in ConversationActivity to get notified when transition ended 2019-01-14 14:22:31 +00:00
Torsten Grote
3620edbfc9 [android] set a transition animation duration for ConversationActivity
so we know better for how long to delay the onboarding dialogs
2019-01-14 14:21:34 +00:00
Torsten Grote
ad71d69149 Create and use method in MessagingManager for checking for image support 2019-01-14 14:21:33 +00:00
Torsten Grote
f73f8ca7e7 [android] do not show two private conversation onboardings at the same time
Checking for introduction onboarding is now done in the ViewModel
together with the image onboarding. The latter has preference. If both
could be shown, the introduction onboarding will be delayed to the next
time the user enters the conversation.
2019-01-14 14:21:33 +00:00
Torsten Grote
16c701a71a [android] only enable image feature if contact supports it
Also show an onboarding the first time, the feature gets activiated
2019-01-14 14:21:19 +00:00
akwizgran
8183b7b26a Merge branch '1469-hide-ui-without-flashing' into 'master'
Hide UI without flashing

Closes #1469

See merge request briar/briar!1030
2019-01-11 17:22:04 +00:00
akwizgran
bd48c97eab Merge branch 'upgrade-jackson-2.9.8' into 'master'
Upgrade Jackson to 2.9.8

See merge request briar/briar!1031
2019-01-11 17:07:07 +00:00
akwizgran
925dc29a1f Merge branch 'hide-ui-api-15' into 'master'
Improve UI hiding behaviour

See merge request briar/briar!1029
2019-01-11 17:03:24 +00:00
akwizgran
91777fd942 Hide UI without flashing. 2019-01-11 16:59:53 +00:00
akwizgran
fbce8f81c7 Merge branch '1475-transition-name' into 'master'
Use a unique transition name for each AttachmentItem

See merge request briar/briar!1028
2019-01-11 16:54:24 +00:00
akwizgran
d7c72c4d68 Use a unique transition name for each AttachmentItem. 2019-01-11 16:45:20 +00:00
akwizgran
4faf535801 Reduce visibility. 2019-01-11 16:45:20 +00:00
akwizgran
526ef7c6d8 Add array entries for new translations. 2019-01-11 15:13:06 +00:00
akwizgran
798dff1a03 Update translations, add Macedonian and Ukrainian. 2019-01-11 12:18:15 +00:00
akwizgran
a4336776c9 Merge branch '1475-image-transitions' into 'master'
Resolve main issues with image transition animation

See merge request briar/briar!1016
2019-01-09 15:01:29 +00:00
akwizgran
418451cbd9 Use consistent conditions to decide whether to scroll. 2019-01-09 14:30:57 +00:00
akwizgran
045fcfc5fa Remove translucent window effect. 2019-01-09 14:30:57 +00:00
Torsten Grote
ef998577db [android] add nullability annotations to ImageActivity 2019-01-09 14:30:57 +00:00
Torsten Grote
a53345a3c9 [android] scroll down when new messages arrive while conversation is visible
Also shows new message notification when ConversationActivity is paused
2019-01-09 14:30:56 +00:00
Torsten Grote
ed8c09282d [android] enable image shared element transition for API 21+22
There's an Android framework bug (#224270) on these APIs that causes a NPE
when the shared element is not visible anymore when returning.
Since we know restore the list position, the shared element should be
visible and thus not produce NPEs anymore.
2019-01-09 14:30:56 +00:00
Torsten Grote
42197b5b5c [android] Fix enter transition to fullscreen ImageActivity 2019-01-09 14:30:56 +00:00
Torsten Grote
374fc7035b [android] Save and restore list position of conversation across restarts 2019-01-09 14:30:55 +00:00
akwizgran
9b796c7cc3 Merge branch '1438-send-image-attachments-multiple' into 'master'
UX for sending multiple image attachments

See merge request briar/briar!1015
2019-01-04 17:04:43 +00:00
akwizgran
532edff642 Minor code cleanups. 2019-01-04 16:55:29 +00:00
akwizgran
6857252471 Merge branch '1480-window-background' into 'master'
[android] Change light theme background color closer to white

See merge request briar/briar!1020
2018-12-21 16:48:18 +00:00
Torsten Grote
c229e19452 [android] remove images from preview that could not be loaded
We will not even attempt to attach them
2018-12-21 11:05:34 -02:00
Torsten Grote
42bca09d16 [android] Add gap between attached image previews 2018-12-21 11:05:34 -02:00
Torsten Grote
9eacbfa659 [android] Remove palette library
we are not extracting photo colors anymore
2018-12-21 11:05:34 -02:00
Torsten Grote
f14e546dc6 [android] allow to attach multiple images 2018-12-21 11:05:34 -02:00
akwizgran
684c64a1d9 Merge branch '1310-disable-enter-transition-for-samsung7' into 'master'
[android] Disable Conversation Enter Transition for Samsung 7 devices

Closes #1310

See merge request briar/briar!1023
2018-12-19 11:32:51 +00:00
akwizgran
6fdab959b1 Merge branch '631-inject-fragments-early' into 'master'
Inject fragments earlier in their lifecycle

Closes #631

See merge request briar/briar!1024
2018-12-19 11:24:31 +00:00
Torsten Grote
c8487483ff [android] Also consider Android 7.1 (API 25) to be Samsung7
which is used for disabling certain features due to crashes there.
2018-12-18 18:17:27 -02:00
Torsten Grote
a159b23dc0 [android] Disable Conversation Enter Transition for Samsung 7 devices 2018-12-18 18:16:32 -02:00
Torsten Grote
5070a27a83 [android] also fix some activity nullability issues 2018-12-18 18:12:05 -02:00
Torsten Grote
9ce73a6840 [android] inject fragments already in onAttach()
This also removes the need to override the inject method even when
there's nothing to inject.

While passing over all fragments, some nullability issues also have been
addressed.
2018-12-18 18:01:04 -02:00
akwizgran
6e9928f20f Merge branch '1484-wait-for-component-to-be-created' into 'master'
[android] AliasFragment: Wait for activity component to be created

Closes #1484

See merge request briar/briar!1022
2018-12-18 17:46:19 +00:00
Torsten Grote
b31d61afc5 [android] AliasFragment: Wait for activity component to be created
before injecting the ViewModel
2018-12-18 15:32:26 -02:00
akwizgran
5a99cb93cc Merge branch '1482-check-earlier-for-sign-in' into 'master'
[android] don't crash when re-opening conversation after briar exited

Closes #1482

See merge request briar/briar!1021
2018-12-18 12:58:39 +00:00
Torsten Grote
d0bbebd25e [android] don't crash when re-opening conversation after briar exited 2018-12-17 18:42:06 -02:00
Torsten Grote
4307d26606 [android] Change light theme background color closer to white 2018-12-17 17:25:12 -02:00
akwizgran
0089c1ac6d Merge branch '1468-restrict-image-size' into 'master'
Fix first issues related to image size

See merge request briar/briar!1018
2018-12-17 12:48:15 +00:00
akwizgran
2a7aac4930 Upgrade Jackson to 2.9.8. 2018-12-17 12:09:36 +00:00
akwizgran
a37b6d81ed Merge branch '1242-save-snackbar-fix' into 'master'
[android] Clarify the meaning of image save state

See merge request briar/briar!1017
2018-12-17 11:17:12 +00:00
Torsten Grote
1d09a6708a [android] don't ever load an entire image into memory
This happens on API 27+28 if loading TIFF or WebP files.
Using an InputStream with a read limit prevents this.
2018-12-14 20:11:43 -02:00
Torsten Grote
d3b6f484c8 [android] allow image transformations in full-screen view
to prevent crashes from huge images
2018-12-14 20:11:43 -02:00
Torsten Grote
039c6edb66 [android] increase scale levels of PhotoView 2018-12-14 20:11:43 -02:00
Torsten Grote
8b9f89eab2 [android] Clarify the meaning of image save state 2018-12-14 12:27:47 -02:00
akwizgran
1e2c17b170 Merge branch '1242-display-image-attachments-multiple' into 'master'
Swipe left/right in image screen for images from same message

See merge request briar/briar!1012
2018-12-13 16:33:24 +00:00
Torsten Grote
a994966095 [android] address review comments for image fullscreen swiping 2018-12-13 12:00:51 -02:00
Torsten Grote
2bea581654 [android] Swipe left/right in image screen to see other images from the same message 2018-12-13 11:59:41 -02:00
Torsten Grote
87377666aa Merge branch '1473-display-multiple-images' into 'master'
UX for displaying multiple image attachments

Closes #1473

See merge request briar/briar!1010
2018-12-13 13:07:24 +00:00
akwizgran
9d07b2e141 Resolve merge conflicts.
# Conflicts:
#   briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java
#   briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
2018-12-13 11:41:04 +00:00
akwizgran
5c312b49e2 Merge branch '1438-send-image-attachments' into 'master'
Store attachments and actually attach them to sent messages

Closes #1438

See merge request briar/briar!1006
2018-12-13 10:27:09 +00:00
Torsten Grote
f56efe45cd Merge branch '1477-get-client-minor-version' into 'master'
Add method for querying client minor version supported by contact

See merge request briar/briar!1014
2018-12-12 19:34:55 +00:00
Torsten Grote
2332a58681 [android] address review comments for displaying multiple images 2018-12-12 17:00:44 -02:00
Torsten Grote
8c6dfaa196 [android] Use @UiThread instead of @MainThread 2018-12-12 16:18:43 -02:00
Torsten Grote
3cfb04b60d Establish some rules for handling InputStreams
* Methods shouldn't place any special requirements on the streams
  passed into them
* This implies that if a stream's going to be marked and reset,
  that should all happen within one method
* This also implies that if a method needs to mark and reset a stream,
  it should wrap the stream in a BufferedInputStream before doing so,
  rather than requiring a markable stream to be passed in
2018-12-12 16:17:50 -02:00
Torsten Grote
e85fbfb952 [android] close InputStream with new IoUtils method 2018-12-12 16:17:50 -02:00
Torsten Grote
80ee35d926 [core] Return fake mini PNG as Attachment instead of throwing exception 2018-12-12 16:17:50 -02:00
Torsten Grote
4796902b9c [android] store attachments and actually attach them to sent messages 2018-12-12 16:17:50 -02:00
akwizgran
149e67c0f7 Reduce code duplication in tests. 2018-12-12 11:57:35 +00:00
akwizgran
1d5214117f Add tests for getClientMinorVersion(). 2018-12-11 17:55:39 +00:00
akwizgran
b8f248ca9c Add tests for getClientVisibility(). 2018-12-11 17:51:42 +00:00
Torsten Grote
dfb71a03a5 [android] Only retrieve image sizes for single images in messages
We need to do this to know the height of messages when binding the view.
The size of single images can be different (e.g. due to orientation).
For multiple images, we use a fixed size, so no retrieval is required.
2018-12-11 15:38:05 -02:00
Torsten Grote
961fdc8e72 [android] Show multiple images in message bubble 2018-12-11 15:28:21 -02:00
Torsten Grote
c3d44663cd [android] Use a nested RecyclerView with a single items to show image attachments
This is preparation for showing multiple image attachments in one
message bubble.
2018-12-11 15:28:21 -02:00
akwizgran
0081472489 Add method for querying contact's client minor version. 2018-12-11 17:25:29 +00:00
akwizgran
cdf4f3a24b Merge branch '1232-add-contacts-remotely-api' into 'master'
[api] Add interface for adding contacts remotely

See merge request briar/briar!1007
2018-12-10 10:53:37 +00:00
Torsten Grote
fb1d8e860f [api] Add interface for adding contacts remotely 2018-12-10 08:30:50 -02:00
akwizgran
a3c526ec9a Merge branch '1298-scrub-wifi-address-in-crash-report' into 'master'
Scrub wifi IP address in crash reports.

Closes #1298

See merge request briar/briar!1013
2018-12-10 10:12:42 +00:00
Jordi Salvat
dee488d06d Scrub wifi IP address in crash reports. 2018-12-10 01:07:37 +01:00
Torsten Grote
b29c7d8022 Merge branch '1385-make-link-cover-entire-word' into 'master'
[android] fix start of link in error message for adding contacts

Closes #1385

See merge request briar/briar!1011
2018-12-07 19:13:54 +00:00
akwizgran
0725d207ec Merge branch '1432-headless-integration-tests' into 'master'
[headless] Add first integration test for ContactController

See merge request briar/briar!1008
2018-12-07 17:37:22 +00:00
akwizgran
5a7599a88d Merge branch '1242-display-image-attachments-save' into 'master'
Allow the user to save image attachment outside of Briar

See merge request briar/briar!1005
2018-12-07 17:31:42 +00:00
Torsten Grote
59cd98db81 [android] Get image extension from MimeTypeMap and store it in AttachmentItem 2018-12-07 15:11:09 -02:00
Torsten Grote
768488eb04 [android] Show (tinted) security icon when warning about saving attachments 2018-12-07 14:39:43 -02:00
Torsten Grote
a6b1ad48c3 [android] Add support for saving image attachments on API < 19
This is done by using the WRITE_EXTERNAL_STORAGE permission
to write the file directly without using the system activity.
2018-12-07 13:01:44 -02:00
Torsten Grote
77299a68ed [android] Allow the user to save image attachment outside of Briar 2018-12-07 13:01:42 -02:00
akwizgran
5e5705c73b Merge branch '1438-send-image-attachments-ui' into 'master'
Implement UX for sending image attachments

See merge request briar/briar!1004
2018-12-07 14:58:23 +00:00
Torsten Grote
e6229a3a13 [android] Factor out image preview into its own view class 2018-12-06 17:56:02 -02:00
Torsten Grote
5fbacb4ee4 [android] Split out an EmojiTextInputView from TextInputViews
This also removes the TextInputController whose job is now done by the view.
2018-12-06 17:56:02 -02:00
Torsten Grote
c7f4e976ed [android] Require users of TextInputView to set its controller 2018-12-06 17:56:02 -02:00
Torsten Grote
419f2d966a [android] Show a toast when an image could not be attached 2018-12-06 17:56:02 -02:00
Torsten Grote
d6c18db9e9 [android] set image preview size to 1/4 of screen height 2018-12-06 17:56:02 -02:00
Torsten Grote
8fe49d9961 [android] Re-factor TextInputViews 2018-12-06 17:56:02 -02:00
Torsten Grote
f536cfdab8 [android] first round of review comments for attaching images 2018-12-06 17:56:02 -02:00
Torsten Grote
4d594acad5 [android] Save attached (but not sent) image on screen rotation 2018-12-06 17:56:02 -02:00
Torsten Grote
800dfed5c1 [android] support adding image attachments to private messages 2018-12-06 17:55:59 -02:00
Jordi Salvat
54b823e401 [android] fix start of link in error message for adding contacts 2018-12-06 20:44:36 +01:00
Torsten Grote
52ec56d690 Merge branch 'invalid-slide-direction' into 'master'
Revert change to slide direction

Closes #1478

See merge request briar/briar!1009
2018-12-06 15:32:07 +00: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
akwizgran
7b3afcca99 Revert change to slide direction. 2018-12-06 15:18:16 +00:00
Torsten Grote
a22d03d028 [headless] wait for lifecycle manager to finish starting
before starting web server
2018-12-05 16:08:03 -02:00
Torsten Grote
d857338ad0 [headless] Add first integration test for ContactController 2018-12-05 16:04:14 -02:00
akwizgran
dcd5e34c6b Improve UI hiding behaviour. 2018-11-30 12:40:45 +00:00
562 changed files with 18314 additions and 6269 deletions

View File

@@ -39,31 +39,6 @@
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<XML> <XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML> </XML>

View File

@@ -11,8 +11,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 10105 versionCode 10107
versionName "1.1.5" versionName "1.1.7"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -30,10 +30,10 @@ 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.22.1'
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'

View File

@@ -9,14 +9,20 @@ import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
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;
import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
class AndroidAccountManager extends AccountManagerImpl class AndroidAccountManager extends AccountManagerImpl
implements AccountManager { implements AccountManager {
@@ -37,6 +43,16 @@ class AndroidAccountManager extends AccountManagerImpl
appContext = app.getApplicationContext(); appContext = app.getApplicationContext();
} }
@Override
public boolean accountExists() {
boolean exists = super.accountExists();
if (!exists && LOG.isLoggable(INFO)) {
LOG.info("Account does not exist. Contents of account directory:");
logFileOrDir(LOG, INFO, getDataDir());
}
return exists;
}
// Locking: stateChangeLock // Locking: stateChangeLock
@Override @Override
@Nullable @Nullable
@@ -70,9 +86,17 @@ class AndroidAccountManager extends AccountManagerImpl
@Override @Override
public void deleteAccount() { public void deleteAccount() {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory before deleting:");
logFileOrDir(LOG, INFO, getDataDir());
}
super.deleteAccount(); super.deleteAccount();
SharedPreferences defaultPrefs = getDefaultSharedPreferences(); SharedPreferences defaultPrefs = getDefaultSharedPreferences();
deleteAppData(prefs, defaultPrefs); deleteAppData(prefs, defaultPrefs);
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory after deleting:");
logFileOrDir(LOG, INFO, getDataDir());
}
} }
} }
@@ -89,20 +113,46 @@ 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
File dataDir = new File(appContext.getApplicationInfo().dataDir); Set<File> files = new HashSet<>();
File[] children = dataDir.listFiles(); File dataDir = getDataDir();
if (children == null) { @Nullable
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) {
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 File getDataDir() {
return new File(appContext.getApplicationInfo().dataDir);
}
private void addIfNotNull(Set<File> files, @Nullable File file) {
if (file != null) files.add(file);
}
} }

View File

@@ -19,9 +19,7 @@ import javax.inject.Inject;
import static android.content.Intent.ACTION_BATTERY_CHANGED; import static android.content.Intent.ACTION_BATTERY_CHANGED;
import static android.content.Intent.ACTION_POWER_CONNECTED; import static android.content.Intent.ACTION_POWER_CONNECTED;
import static android.content.Intent.ACTION_POWER_DISCONNECTED; import static android.content.Intent.ACTION_POWER_DISCONNECTED;
import static android.os.BatteryManager.BATTERY_STATUS_CHARGING; import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.EXTRA_STATUS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
@@ -48,9 +46,8 @@ class AndroidBatteryManager implements BatteryManager, Service {
IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED); IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
Intent i = appContext.registerReceiver(null, filter); Intent i = appContext.registerReceiver(null, filter);
if (i == null) return false; if (i == null) return false;
int status = i.getIntExtra(EXTRA_STATUS, -1); int status = i.getIntExtra(EXTRA_PLUGGED, 0);
return status == BATTERY_STATUS_CHARGING || return status != 0;
status == BATTERY_STATUS_FULL;
} }
@Override @Override

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();
@@ -126,8 +128,14 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
will(returnValue(editor)); will(returnValue(editor));
oneOf(editor).commit(); oneOf(editor).commit();
will(returnValue(true)); will(returnValue(true));
oneOf(app).getApplicationInfo(); allowing(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,47 +1,45 @@
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.4.0:protos-26.4.0.jar:ad760915586797d39319f402837b378bff3bb4ed583e3e0c48c965631fb2135f',
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a', 'com.android.tools.analytics-library:shared:26.4.0:shared-26.4.0.jar:1332106a905d48909c81268c9e414946de3e83487db394c6073b0a9b5c3d0ed2',
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75', 'com.android.tools.analytics-library:tracker:26.4.0:tracker-26.4.0.jar:d0020cfbfd4cd75935f2972d6a24089840d4a10df6f3ef2a796093217dd37796',
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e', 'com.android.tools.build:apksig:3.4.0:apksig-3.4.0.jar:91d5a1866139c69756280355a6f61b4d619d0516841580114f45a10f2177327e',
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5', 'com.android.tools.build:apkzlib:3.4.0:apkzlib-3.4.0.jar:8653c85f5fdf1dde840e8b8af7396aeb79c34b66e541b5860059616006535592',
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa', 'com.android.tools.build:builder-model:3.4.0:builder-model-3.4.0.jar:a88f138124a9f016a70bcb4760359a502f65c7deed56507ee4014f4dd9ea853b',
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff', 'com.android.tools.build:builder-test-api:3.4.0:builder-test-api-3.4.0.jar:31089ab1ec19ca7687a010867d2f3807513c805b8226979706f4247b5d4df26f',
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be', 'com.android.tools.build:builder:3.4.0:builder-3.4.0.jar:476221b5203a7f50089bf185ed95000a34b6f5020ef0a17815afd58606922679',
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83', 'com.android.tools.build:gradle-api:3.4.0:gradle-api-3.4.0.jar:215eca38f6719213c2f492b4d622cdd11676c66c9871f8a2aed0c66d00175628',
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d', 'com.android.tools.build:manifest-merger:26.4.0:manifest-merger-26.4.0.jar:29e45e690dedd165035e97c21c2ca94d0bd4ec16b6b210daa26669a582b6f220',
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6', 'com.android.tools.ddms:ddmlib:26.4.0:ddmlib-26.4.0.jar:93f56fe4630c3166adbd6c51d7bb602d96abb91b07ba5b1165fdcd071e88c940',
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b', 'com.android.tools.external.com-intellij:intellij-core:26.4.0:intellij-core-26.4.0.jar:30cb0e879d4424de9677a50b537fb628636b4a50f5470af5e52437980c41421f',
'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.4.0:kotlin-compiler-26.4.0.jar:dd1fe225c31a0e012dc025336363a5b783e2c5c20ffb69e77f8f57e89420d998',
'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.4.0:uast-26.4.0.jar:f25f3285b775a983327583ff6584dea54e447813ef69e0ce08b05a45b5f4aab0',
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b', 'com.android.tools.layoutlib:layoutlib-api:26.4.0:layoutlib-api-26.4.0.jar:52128f5cf293b224072be361919bfd416e59480ab7264ddcdbbf046b0d7a12e3',
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade', 'com.android.tools.lint:lint-api:26.4.0:lint-api-26.4.0.jar:fdb8fca8ae4c254f438338d03d72605e00ed106f2d5550405af41ca1c8509401',
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e', 'com.android.tools.lint:lint-checks:26.4.0:lint-checks-26.4.0.jar:4ff52d40488cd3e22b9c6b2eb67784e0c3269d0b42ef9d17689cd75a7b2bceb4',
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6', 'com.android.tools.lint:lint-gradle-api:26.4.0:lint-gradle-api-26.4.0.jar:714b7a85c7d2aa10daeab16e969fe7530c659d0728a7f24021da456870418d0f',
'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.4.0:lint-gradle-26.4.0.jar:b8c130d273f522388734457e1b96790f41528fcec6fda9e8eaa4e4d95a07cfbb',
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb', 'com.android.tools.lint:lint:26.4.0:lint-26.4.0.jar:83aa062fb0405b60ed358d858c8c2955e1bae44a455b498068c6a60988755f00',
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877', 'com.android.tools:annotations:26.4.0:annotations-26.4.0.jar:a7955b8e19c3a2a861d6faa43a58b7c0d46ea9112188ee3e235c6f9f439ecc1a',
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5', 'com.android.tools:common:26.4.0:common-26.4.0.jar:ea40b94b3c1284ea7700f011388e2906a8363a66abd902891722b3c557984852',
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24', 'com.android.tools:dvlib:26.4.0:dvlib-26.4.0.jar:23af89c535b01ba36ceed1b6b309b672814eba624e643cd7dedf0519edad50cc',
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5', 'com.android.tools:repository:26.4.0:repository-26.4.0.jar:3d1763ab46199374dc6d94129bba11c70f1d5857e2c81a3ac4898abca40b176b',
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa', 'com.android.tools:sdk-common:26.4.0:sdk-common-26.4.0.jar:78a522525b30ffc6b7bf1299c831d24ce385f68a9f4878f8f752e9baefa31b0f',
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1', 'com.android.tools:sdklib:26.4.0:sdklib-26.4.0.jar:b854c23892013a326d761cf071c72cf3e038ed0469d10f4a356829fa56e4c132',
'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.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768', 'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1', 'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'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.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f', '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:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'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',
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439', 'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
@@ -50,7 +48,7 @@ dependencyVerification {
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce', 'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4', 'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038', 'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
'commons-codec:commons-codec:1.9:commons-codec-1.9.jar:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce', 'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569',
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636', 'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c', 'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
@@ -62,27 +60,28 @@ dependencyVerification {
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6', 'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.5.2:httpclient-4.5.2.jar:0dffc621400d6c632f55787d996b8aeca36b30746a716e079a985f24d8074057', 'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
'org.apache.httpcomponents:httpcore:4.4.5:httpcore-4.4.5.jar:64d5453874cab7e40a7065cb01a9a9ca1053845a9786b478878b679e0580cec3', 'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
'org.apache.httpcomponents:httpmime:4.5.2:httpmime-4.5.2.jar:231a3f7e4962053db2be8461d5422e68fc458a3a7dd7d8ada803a348e21f8f07', 'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
'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

@@ -7,7 +7,7 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.19" implementation "com.google.dagger:dagger:2.22.1"
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
/** /**
@@ -38,7 +39,7 @@ public class Bytes implements Comparable<Bytes> {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes); return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
} }

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.api;
/**
* Thrown when data being parsed uses a protocol or format version that is not
* supported.
*/
public class UnsupportedVersionException extends FormatException {
private final boolean tooOld;
public UnsupportedVersionException(boolean tooOld) {
this.tooOld = tooOld;
}
public boolean isTooOld() {
return tooOld;
}
}

View File

@@ -33,7 +33,8 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
/** /**
* Called once for each incoming message that passes validation. * Called once for each incoming message that passes validation.
* *
* @return whether or not this message should be shared * @param txn A read-write transaction
* @return Whether or not this message should be shared
* @throws DbException Should only be used for real database errors. * @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup, * If this is thrown, delivery will be attempted again at next startup,
* whereas if a FormatException is thrown, the message will be permanently * whereas if a FormatException is thrown, the message will be permanently

View File

@@ -1,6 +1,8 @@
package org.briarproject.bramble.api.client; package org.briarproject.bramble.api.client;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -96,14 +98,18 @@ public interface ClientHelper {
BdfList toList(Author a); BdfList toList(Author a);
byte[] sign(String label, BdfList toSign, byte[] privateKey) byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
throws FormatException, GeneralSecurityException; throws FormatException, GeneralSecurityException;
void verifySignature(byte[] signature, String label, BdfList signed, void verifySignature(byte[] signature, String label, BdfList signed,
byte[] publicKey) throws FormatException, GeneralSecurityException; PublicKey publicKey)
throws FormatException, GeneralSecurityException;
Author parseAndValidateAuthor(BdfList author) throws FormatException; Author parseAndValidateAuthor(BdfList author) throws FormatException;
PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
throws FormatException;
TransportProperties parseAndValidateTransportProperties( TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException; BdfDictionary properties) throws FormatException;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey;
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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -19,10 +20,13 @@ public class Contact {
private final AuthorId localAuthorId; private final AuthorId localAuthorId;
@Nullable @Nullable
private final String alias; private final String alias;
private final boolean verified, active; @Nullable
private final PublicKey handshakePublicKey;
private final boolean verified;
public Contact(ContactId id, Author author, AuthorId localAuthorId, public Contact(ContactId id, Author author, AuthorId localAuthorId,
@Nullable String alias, boolean verified, boolean active) { @Nullable String alias, @Nullable PublicKey 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)
@@ -32,8 +36,8 @@ public class Contact {
this.author = author; this.author = author;
this.localAuthorId = localAuthorId; 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() {
@@ -53,12 +57,13 @@ public class Contact {
return alias; return alias;
} }
public boolean isVerified() { @Nullable
return verified; public PublicKey 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

@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.contact;
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;
/** /**
@@ -28,7 +29,7 @@ public class ContactId {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
return o instanceof ContactId && id == ((ContactId) o).id; return o instanceof ContactId && id == ((ContactId) o).id;
} }
} }

View File

@@ -1,7 +1,10 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.crypto.SecretKey; 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.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;
@@ -31,7 +34,7 @@ public interface ContactManager {
* @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 remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException; boolean active) throws DbException;
/** /**
@@ -39,7 +42,7 @@ public interface ContactManager {
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException; boolean verified) throws DbException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
@@ -48,10 +51,40 @@ public interface ContactManager {
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Author remote, AuthorId local, SecretKey master, ContactId addContact(Author remote, AuthorId local, SecretKey rootKey,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException; throws DbException;
/**
* Returns the handshake link that needs to be sent to a contact we want
* to add.
*/
String getHandshakeLink() throws DbException;
/**
* Creates a {@link PendingContact} from the given handshake link and
* alias, adds it to the database and returns it.
*
* @param link The handshake link received from the contact we want to add
* @param alias The alias the user has given this contact
* @return A PendingContact representing the contact to be added
* @throws UnsupportedVersionException If the link uses a format version
* that is not supported
* @throws FormatException If the link is invalid
*/
PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException;
/**
* Returns a list of {@link PendingContact}s.
*/
Collection<PendingContact> getPendingContacts() throws DbException;
/**
* Removes a {@link PendingContact}.
*/
void removePendingContact(PendingContactId p) throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
*/ */
@@ -61,7 +94,7 @@ public interface ContactManager {
* Returns the contact with the given remoteAuthorId * Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId * that was added by the LocalAuthor with the given localAuthorId
* *
* @throws org.briarproject.bramble.api.db.NoSuchContactException * @throws NoSuchContactException If the contact is not in the database
*/ */
Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId) Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException; throws DbException;
@@ -70,7 +103,7 @@ public interface ContactManager {
* Returns the contact with the given remoteAuthorId * Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId * that was added by the LocalAuthor with the given localAuthorId
* *
* @throws org.briarproject.bramble.api.db.NoSuchContactException * @throws NoSuchContactException If the contact is not in the database
*/ */
Contact getContact(Transaction txn, AuthorId remoteAuthorId, Contact getContact(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException; AuthorId localAuthorId) throws DbException;
@@ -78,7 +111,7 @@ public interface ContactManager {
/** /**
* 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.
@@ -90,12 +123,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.
*/ */
@@ -132,8 +159,20 @@ public interface ContactManager {
interface ContactHook { interface ContactHook {
/**
* Called when a contact is being added.
*
* @param txn A read-write transaction
* @param c The contact that is being added
*/
void addingContact(Transaction txn, Contact c) throws DbException; void addingContact(Transaction txn, Contact c) throws DbException;
/**
* Called when a contact is being removed
*
* @param txn A read-write transaction
* @param c The contact that is being removed
*/
void removingContact(Transaction txn, Contact c) throws DbException; void removingContact(Transaction txn, Contact c) throws DbException;
} }
} }

View File

@@ -0,0 +1,34 @@
package org.briarproject.bramble.api.contact;
import java.util.regex.Pattern;
public interface HandshakeLinkConstants {
/**
* The current version of the handshake link format.
*/
int FORMAT_VERSION = 0;
/**
* The length of a base32-encoded handshake link in bytes, excluding the
* 'briar://' prefix.
*/
int BASE32_LINK_BYTES = 53;
/**
* The length of a raw handshake link in bytes, before base32 encoding.
*/
int RAW_LINK_BYTES = 33;
/**
* Regular expression for matching handshake links, including or excluding
* the 'briar://' prefix.
*/
Pattern LINK_REGEX =
Pattern.compile("(briar://)?([a-z2-7]{" + BASE32_LINK_BYTES + "})");
/**
* Label for hashing handshake public keys to calculate their identifiers.
*/
String ID_LABEL = "org.briarproject.bramble/HANDSHAKE_KEY_ID";
}

View File

@@ -0,0 +1,57 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class PendingContact {
private final PendingContactId id;
private final PublicKey publicKey;
private final String alias;
private final PendingContactState state;
private final long timestamp;
public PendingContact(PendingContactId id, PublicKey publicKey,
String alias, PendingContactState state, long timestamp) {
this.id = id;
this.publicKey = publicKey;
this.alias = alias;
this.state = state;
this.timestamp = timestamp;
}
public PendingContactId getId() {
return id;
}
public PublicKey getPublicKey() {
return publicKey;
}
public String getAlias() {
return alias;
}
public PendingContactState getState() {
return state;
}
public long getTimestamp() {
return timestamp;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof PendingContact &&
id.equals(((PendingContact) o).id);
}
}

View File

@@ -0,0 +1,25 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
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 PendingContactId(byte[] id) {
super(id);
}
@Override
public boolean equals(@Nullable 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

@@ -1,4 +1,4 @@
package org.briarproject.briar.api.introduction.event; package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
@@ -8,11 +8,11 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class IntroductionSucceededEvent extends Event { public class ContactAddedRemotelyEvent extends Event {
private final Contact contact; private final Contact contact;
public IntroductionSucceededEvent(Contact contact) { public ContactAddedRemotelyEvent(Contact contact) {
this.contact = contact; this.contact = contact;
} }

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

@@ -0,0 +1,26 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContactId;
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 pending contact is removed.
*/
@Immutable
@NotNullByDefault
public class PendingContactRemovedEvent extends Event {
private final PendingContactId id;
public PendingContactRemovedEvent(PendingContactId id) {
this.id = id;
}
public PendingContactId getId() {
return id;
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.bramble.api.contact.event;
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.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a pending contact's state is changed.
*/
@Immutable
@NotNullByDefault
public class PendingContactStateChangedEvent extends Event {
private final PendingContactId id;
private final PendingContactState state;
public PendingContactStateChangedEvent(PendingContactId id,
PendingContactState state) {
this.id = id;
this.state = state;
}
public PendingContactId getId() {
return id;
}
public PendingContactState getPendingContactState() {
return state;
}
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
/**
* Type-safe wrapper for a private key used for key agreement.
*/
@Immutable
@NotNullByDefault
public class AgreementPrivateKey extends Bytes implements PrivateKey {
public AgreementPrivateKey(byte[] encoded) {
super(encoded);
}
@Override
public String getKeyType() {
return KEY_TYPE_AGREEMENT;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -0,0 +1,35 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
/**
* Type-safe wrapper for a public key used for key agreement.
*/
@Immutable
@NotNullByDefault
public class AgreementPublicKey extends Bytes implements PublicKey {
public AgreementPublicKey(byte[] encoded) {
super(encoded);
if (encoded.length == 0 ||
encoded.length > MAX_AGREEMENT_PUBLIC_KEY_BYTES) {
throw new IllegalArgumentException();
}
}
@Override
public String getKeyType() {
return KEY_TYPE_AGREEMENT;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -55,7 +55,7 @@ public interface CryptoComponent {
* signature, to prevent it from being repurposed or colliding with a * signature, to prevent it from being repurposed or colliding with a
* signature created for another purpose * signature created for another purpose
*/ */
byte[] sign(String label, byte[] toSign, byte[] privateKey) byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
throws GeneralSecurityException; throws GeneralSecurityException;
/** /**
@@ -68,7 +68,7 @@ public interface CryptoComponent {
* @return true if the signature was valid, false otherwise. * @return true if the signature was valid, false otherwise.
*/ */
boolean verifySignature(byte[] signature, String label, byte[] signed, boolean verifySignature(byte[] signature, String label, byte[] signed,
byte[] publicKey) throws GeneralSecurityException; PublicKey publicKey) throws GeneralSecurityException;
/** /**
* Returns the hash of the given inputs. The inputs are unambiguously * Returns the hash of the given inputs. The inputs are unambiguously

View File

@@ -7,11 +7,21 @@ public interface CryptoConstants {
*/ */
int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32; int MAX_AGREEMENT_PUBLIC_KEY_BYTES = 32;
/**
* The key type for agreement key pairs.
*/
String KEY_TYPE_AGREEMENT = "Curve25519";
/** /**
* The maximum length of a signature public key in bytes. * The maximum length of a signature public key in bytes.
*/ */
int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32; int MAX_SIGNATURE_PUBLIC_KEY_BYTES = 32;
/**
* The key type for signature key pairs.
*/
String KEY_TYPE_SIGNATURE = "Ed25519";
/** /**
* The maximum length of a signature in bytes. * The maximum length of a signature in bytes.
*/ */

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

@@ -15,6 +15,8 @@ public class KeyPair {
private final PrivateKey privateKey; private final PrivateKey privateKey;
public KeyPair(PublicKey publicKey, PrivateKey privateKey) { public KeyPair(PublicKey publicKey, PrivateKey privateKey) {
if (!publicKey.getKeyType().equals(privateKey.getKeyType()))
throw new IllegalArgumentException();
this.publicKey = publicKey; this.publicKey = publicKey;
this.privateKey = privateKey; this.privateKey = privateKey;
} }

View File

@@ -8,6 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface PrivateKey { public interface PrivateKey {
/**
* Returns the type of this key pair.
*/
String getKeyType();
/** /**
* Returns the encoded representation of this key. * Returns the encoded representation of this key.
*/ */

View File

@@ -8,6 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface PublicKey { public interface PublicKey {
/**
* Returns the type of this key pair.
*/
String getKeyType();
/** /**
* Returns the encoded representation of this key. * Returns the encoded representation of this key.
*/ */

View File

@@ -0,0 +1,30 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
/**
* Type-safe wrapper for a public key used for signing.
*/
@Immutable
@NotNullByDefault
public class SignaturePrivateKey extends Bytes implements PrivateKey {
public SignaturePrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public String getKeyType() {
return KEY_TYPE_SIGNATURE;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -0,0 +1,35 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
/**
* Type-safe wrapper for a public key used for verifying signatures.
*/
@Immutable
@NotNullByDefault
public class SignaturePublicKey extends Bytes implements PublicKey {
public SignaturePublicKey(byte[] encoded) {
super(encoded);
if (encoded.length == 0 ||
encoded.length > MAX_SIGNATURE_PUBLIC_KEY_BYTES) {
throw new IllegalArgumentException();
}
}
@Override
public String getKeyType() {
return KEY_TYPE_SIGNATURE;
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -5,25 +5,34 @@ 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 rotation mode transport keys for the given transport in
* rotation period from the given master secret. * the given 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 deriveRotationKeys(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 * Derives handshake keys for the given transport in the given time period
* keys are for the given period or any later period they are not rotated. * from the given root key.
*
* @param alice Whether the keys are for use by Alice or Bob
*/ */
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean alice);
/**
* Updates the given transport keys to the given time period. If the keys
* are for the given period or any later period they are not updated.
*/
TransportKeys updateTransportKeys(TransportKeys 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,10 +2,14 @@ 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.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.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.Identity;
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.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -20,8 +24,8 @@ 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.KeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -101,7 +105,7 @@ public interface DatabaseComponent {
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException; boolean verified) throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -109,9 +113,9 @@ public interface DatabaseComponent {
void addGroup(Transaction txn, Group g) throws DbException; void addGroup(Transaction txn, Group g) throws DbException;
/** /**
* Stores a local pseudonym. * Stores an identity.
*/ */
void addLocalAuthor(Transaction txn, LocalAuthor a) throws DbException; void addIdentity(Transaction txn, Identity i) throws DbException;
/** /**
* Stores a local message. * Stores a local message.
@@ -119,6 +123,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,25 +139,46 @@ 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, KeySetId addTransportKeys(Transaction txn, ContactId c, TransportKeys k)
throws DbException;
/**
* Stores the given transport keys for the given pending contact and
* returns a key set ID.
*/
KeySetId addTransportKeys(Transaction txn, PendingContactId p,
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 for the given
* local pseudonym. * local pseudonym.
* <p/>
* Read-only.
*/ */
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local) boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
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 an identity for the given
* pseudonym.
* <p/>
* Read-only.
*/ */
boolean containsLocalAuthor(Transaction txn, AuthorId local) boolean containsIdentity(Transaction txn, AuthorId a) throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(Transaction txn, PendingContactId p)
throws DbException; throws DbException;
/** /**
@@ -235,7 +266,7 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<ContactId> getContacts(Transaction txn, AuthorId a) Collection<ContactId> getContacts(Transaction txn, AuthorId local)
throws DbException; throws DbException;
/** /**
@@ -270,18 +301,18 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the identity for the local pseudonym with the given ID.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException; Identity getIdentity(Transaction txn, AuthorId a) throws DbException;
/** /**
* Returns all local pseudonyms. * Returns the identities for all local pseudonyms.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException; Collection<Identity> getIdentities(Transaction txn) throws DbException;
/** /**
* Returns the message with the given ID. * Returns the message with the given ID.
@@ -417,6 +448,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,7 +468,7 @@ 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;
/** /**
@@ -492,15 +531,21 @@ public interface DatabaseComponent {
void removeGroup(Transaction txn, Group g) throws DbException; void removeGroup(Transaction txn, Group g) throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes an identity (and all associated state) from the database.
*/ */
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException; void removeIdentity(Transaction txn, AuthorId a) throws DbException;
/** /**
* Removes a message (and all associated state) from the database. * Removes a message (and all associated state) from the database.
*/ */
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.
*/ */
@@ -517,12 +562,6 @@ public interface DatabaseComponent {
*/ */
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,11 +592,17 @@ 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 handshake key pair for the identity with the given ID.
* given rotation period. */
void setHandshakeKeyPair(Transaction txn, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException;
/**
* Sets the reordering window for the given transport keys in the given
* time period.
*/ */
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t, void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
long rotationPeriod, long base, byte[] bitmap) throws DbException; 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.
@@ -568,6 +613,6 @@ public interface DatabaseComponent {
/** /**
* 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

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

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

@@ -1,13 +1,14 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.Nameable; import org.briarproject.bramble.api.Nameable;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
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;
/** /**
* A pseudonym for a user. * A pseudonym for a user.
@@ -24,14 +25,14 @@ public class Author implements Nameable {
private final AuthorId id; private final AuthorId id;
private final int formatVersion; private final int formatVersion;
private final String name; private final String name;
private final byte[] publicKey; private final PublicKey publicKey;
public Author(AuthorId id, int formatVersion, String name, public Author(AuthorId id, int formatVersion, String name,
byte[] publicKey) { PublicKey publicKey) {
int nameLength = StringUtils.toUtf8(name).length; int nameLength = toUtf8(name).length;
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH) if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH) if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
this.formatVersion = formatVersion; this.formatVersion = formatVersion;
@@ -63,7 +64,7 @@ public class Author implements Nameable {
/** /**
* Returns the public key used to verify the pseudonym's signatures. * Returns the public key used to verify the pseudonym's signatures.
*/ */
public byte[] getPublicKey() { public PublicKey getPublicKey() {
return publicKey; return publicKey;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
@@ -9,23 +10,16 @@ public interface AuthorFactory {
* Creates an author with the current format version and the given name and * Creates an author with the current format version and the given name and
* public key. * public key.
*/ */
Author createAuthor(String name, byte[] publicKey); Author createAuthor(String name, PublicKey publicKey);
/** /**
* Creates an author with the given format version, name and public key. * Creates an author with the given format version, name and public key.
*/ */
Author createAuthor(int formatVersion, String name, byte[] publicKey); Author createAuthor(int formatVersion, String name, PublicKey publicKey);
/** /**
* Creates a local author with the current format version and the given * Creates a local author with the current format version and the given
* name and keys. * name.
*/ */
LocalAuthor createLocalAuthor(String name, byte[] publicKey, LocalAuthor createLocalAuthor(String name);
byte[] privateKey);
/**
* Creates a local author with the given format version, name and keys.
*/
LocalAuthor createLocalAuthor(int formatVersion, String name,
byte[] publicKey, byte[] privateKey);
} }

View File

@@ -0,0 +1,88 @@
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
@Immutable
@NotNullByDefault
public class Identity {
private final LocalAuthor localAuthor;
@Nullable
private final PublicKey handshakePublicKey;
@Nullable
private final PrivateKey handshakePrivateKey;
private final long created;
public Identity(LocalAuthor localAuthor,
@Nullable PublicKey handshakePublicKey,
@Nullable PrivateKey handshakePrivateKey, long created) {
if (handshakePublicKey != null) {
if (handshakePrivateKey == null)
throw new IllegalArgumentException();
if (!handshakePublicKey.getKeyType().equals(KEY_TYPE_AGREEMENT))
throw new IllegalArgumentException();
}
if (handshakePrivateKey != null) {
if (handshakePublicKey == null)
throw new IllegalArgumentException();
if (!handshakePrivateKey.getKeyType().equals(KEY_TYPE_AGREEMENT))
throw new IllegalArgumentException();
}
this.localAuthor = localAuthor;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
}
/**
* Returns the ID of the user's pseudonym.
*/
public AuthorId getId() {
return localAuthor.getId();
}
/**
* Returns the user's pseudonym.
*/
public LocalAuthor getLocalAuthor() {
return localAuthor;
}
/**
* Returns true if the identity has a handshake key pair.
*/
public boolean hasHandshakeKeyPair() {
return handshakePublicKey != null && handshakePrivateKey != null;
}
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public PublicKey getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public PrivateKey getHandshakePrivateKey() {
return handshakePrivateKey;
}
/**
* Returns the time the identity was created, in milliseconds since the
* Unix epoch.
*/
public long getTimeCreated() {
return created;
}
}

View File

@@ -1,30 +1,30 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.KeyPair;
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.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface IdentityManager { public interface IdentityManager {
/** /**
* Creates a local identity with the given name. * Creates an identity with the given name. The identity includes a
* handshake key pair.
*/ */
@CryptoExecutor @CryptoExecutor
LocalAuthor createLocalAuthor(String name); Identity createIdentity(String name);
/** /**
* Registers the given local identity with the manager. The identity is * Registers the given identity with the manager. This method should be
* not stored until {@link #storeLocalAuthor()} is called. * called before {@link LifecycleManager#startServices(SecretKey)}. The
* identity is stored when {@link LifecycleManager#startServices(SecretKey)}
* is called. The identity must include a handshake key pair.
*/ */
void registerLocalAuthor(LocalAuthor a); void registerIdentity(Identity i);
/**
* Stores the local identity registered with
* {@link #registerLocalAuthor(LocalAuthor)}, if any.
*/
void storeLocalAuthor() throws DbException;
/** /**
* Returns the cached local identity or loads it from the database. * Returns the cached local identity or loads it from the database.
@@ -33,7 +33,15 @@ public interface IdentityManager {
/** /**
* Returns the cached local identity or loads it from the database. * Returns the cached local identity or loads it from the database.
* <p/>
* Read-only.
*/ */
LocalAuthor getLocalAuthor(Transaction txn) throws DbException; LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
/**
* Returns the cached handshake keys or loads them from the database.
* <p/>
* Read-only.
*/
KeyPair getHandshakeKeys(Transaction txn) throws DbException;
} }

View File

@@ -1,9 +1,13 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
/** /**
* A pseudonym for the local user. * A pseudonym for the local user.
*/ */
@@ -11,28 +15,20 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class LocalAuthor extends Author { public class LocalAuthor extends Author {
private final byte[] privateKey; private final PrivateKey privateKey;
private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name, public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, long created) { PublicKey publicKey, PrivateKey privateKey) {
super(id, formatVersion, name, publicKey); super(id, formatVersion, name, publicKey);
if (!privateKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException();
this.privateKey = privateKey; this.privateKey = privateKey;
this.created = created;
} }
/** /**
* Returns the private key used to generate the pseudonym's signatures. * Returns the private key used to generate the pseudonym's signatures.
*/ */
public byte[] getPrivateKey() { public PrivateKey getPrivateKey() {
return privateKey; return privateKey;
} }
/**
* Returns the time the pseudonym was created, in milliseconds since the
* Unix epoch.
*/
public long getTimeCreated() {
return created;
}
} }

View File

@@ -7,15 +7,15 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when a local pseudonym is added. * An event that is broadcast when an identity is added.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class LocalAuthorAddedEvent extends Event { public class IdentityAddedEvent extends Event {
private final AuthorId authorId; private final AuthorId authorId;
public LocalAuthorAddedEvent(AuthorId authorId) { public IdentityAddedEvent(AuthorId authorId) {
this.authorId = authorId; this.authorId = authorId;
} }

View File

@@ -7,15 +7,15 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when a local pseudonym is removed. * An event that is broadcast when an identity is removed.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class LocalAuthorRemovedEvent extends Event { public class IdentityRemovedEvent extends Event {
private final AuthorId authorId; private final AuthorId authorId;
public LocalAuthorRemovedEvent(AuthorId authorId) { public IdentityRemovedEvent(AuthorId authorId) {
this.authorId = authorId; this.authorId = authorId;
} }

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

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.keyagreement;
import java.io.IOException;
/**
* Thrown when a QR code that has been scanned uses a protocol version that is
* not supported.
*/
public class UnsupportedVersionException extends IOException {
private final boolean tooOld;
public UnsupportedVersionException(boolean tooOld) {
this.tooOld = tooOld;
}
public boolean isTooOld() {
return tooOld;
}
}

View File

@@ -2,16 +2,16 @@ package org.briarproject.bramble.api.lifecycle;
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.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
/** /**
* Manages the lifecycle of the app, starting {@link Client Clients}, starting * Manages the lifecycle of the app: opening and closing the
* and stopping {@link Service Services}, shutting down * {@link DatabaseComponent} starting and stopping {@link Service Services},
* {@link ExecutorService ExecutorServices}, and opening and closing the * and shutting down {@link ExecutorService ExecutorServices}.
* {@link DatabaseComponent}.
*/ */
@NotNullByDefault @NotNullByDefault
public interface LifecycleManager { public interface LifecycleManager {
@@ -42,18 +42,19 @@ public interface LifecycleManager {
} }
} }
/**
* Registers a hook to be called after the database is opened and before
* {@link Service services} are started. This method should be called
* before {@link #startServices(SecretKey)}.
*/
void registerOpenDatabaseHook(OpenDatabaseHook hook);
/** /**
* Registers a {@link Service} to be started and stopped. This method * Registers a {@link Service} to be started and stopped. This method
* should be called before {@link #startServices(SecretKey)}. * should be called before {@link #startServices(SecretKey)}.
*/ */
void registerService(Service s); void registerService(Service s);
/**
* Registers a {@link Client} to be started. This method should be called
* before {@link #startServices(SecretKey)}.
*/
void registerClient(Client c);
/** /**
* Registers an {@link ExecutorService} to be shut down. This method * Registers an {@link ExecutorService} to be shut down. This method
* should be called before {@link #startServices(SecretKey)}. * should be called before {@link #startServices(SecretKey)}.
@@ -62,7 +63,7 @@ public interface LifecycleManager {
/** /**
* Opens the {@link DatabaseComponent} using the given key and starts any * Opens the {@link DatabaseComponent} using the given key and starts any
* registered {@link Client Clients} and {@link Service Services}. * registered {@link Service Services}.
*/ */
StartResult startServices(SecretKey dbKey); StartResult startServices(SecretKey dbKey);
@@ -80,8 +81,7 @@ public interface LifecycleManager {
/** /**
* Waits for the {@link DatabaseComponent} to be opened and all registered * Waits for the {@link DatabaseComponent} to be opened and all registered
* {@link Client Clients} and {@link Service Services} to start before * {@link Service Services} to start before returning.
* returning.
*/ */
void waitForStartup() throws InterruptedException; void waitForStartup() throws InterruptedException;
@@ -97,4 +97,13 @@ public interface LifecycleManager {
*/ */
LifecycleState getLifecycleState(); LifecycleState getLifecycleState();
interface OpenDatabaseHook {
/**
* Called when the database is being opened, before
* {@link #waitForDatabase()} returns.
*
* @param txn A read-write transaction
*/
void onDatabaseOpened(Transaction txn) throws DbException;
}
} }

View File

@@ -6,10 +6,20 @@ import javax.annotation.Nullable;
public class NullSafety { public class NullSafety {
/** /**
* Stand-in for `Objects.requireNonNull()`. * Stand-in for {@code Objects.requireNonNull()}.
*/ */
public static <T> T requireNonNull(@Nullable T t) { public static <T> T requireNonNull(@Nullable T t) {
if (t == null) throw new NullPointerException(); if (t == null) throw new NullPointerException();
return t; return t;
} }
/**
* Checks that exactly one of the arguments is null.
*
* @throws AssertionError If both or neither of the arguments are null
*/
public static void requireExactlyOneNull(@Nullable Object a,
@Nullable Object b) {
if ((a == null) == (b == null)) throw new AssertionError();
}
} }

View File

@@ -16,6 +16,7 @@ public interface TorConstants {
String PREF_TOR_NETWORK = "network2"; String PREF_TOR_NETWORK = "network2";
String PREF_TOR_PORT = "port"; String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData"; String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1; int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;

View File

@@ -1,14 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface Client {
/**
* Called at startup to create any local state needed by the client.
*/
void createLocalState(Transaction txn) throws DbException;
}

View File

@@ -11,7 +11,8 @@ public interface IncomingMessageHook {
/** /**
* Called once for each incoming message that passes validation. * Called once for each incoming message that passes validation.
* *
* @return whether or not this message should be shared * @param txn A read-write transaction
* @return Whether or not this message should be shared
* @throws DbException Should only be used for real database errors. * @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup, * If this is thrown, delivery will be attempted again at next startup,
* whereas if an InvalidMessageException is thrown, * whereas if an InvalidMessageException is thrown,

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 or
* given transport in a given rotation period. * pending contact over a 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

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.transport; 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.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; 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;
@@ -18,17 +19,45 @@ public interface KeyManager {
/** /**
* Informs the key manager that a new contact has been added. Derives and * Informs the key manager that a new contact has been added. Derives and
* stores a set of transport keys for communicating with the contact over * stores a set of rotation mode transport keys for communicating with the
* each transport and returns the key set IDs. * contact over each transport and returns the key set IDs.
* <p/> * <p/>
* {@link StreamContext StreamContexts} for the contact can be created * {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned. * after this method has returned.
* *
* @param alice true if the local party is Alice * @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, KeySetId> addContactWithRotationKeys(Transaction txn,
SecretKey master, long timestamp, boolean alice, boolean active) ContactId c, SecretKey rootKey, long timestamp, boolean alice,
boolean active) throws DbException;
/**
* Informs the key manager that a new contact has been added. Derives and
* stores a set of handshake mode transport keys for communicating with the
* contact over each transport and returns the key set IDs.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice True if the local party is Alice
*/
Map<TransportId, KeySetId> addContactWithHandshakeKeys(Transaction txn,
ContactId c, SecretKey rootKey, boolean alice) throws DbException;
/**
* Informs the key manager that a new pending contact has been added.
* Derives and stores a set of handshake mode transport keys for
* communicating with the pending contact over each transport and returns
* the key set IDs.
* <p/>
* {@link StreamContext StreamContexts} for the pending contact can be
* created after this method has returned.
*
* @param alice True if the local party is Alice
*/
Map<TransportId, KeySetId> addPendingContact(Transaction txn,
PendingContactId p, SecretKey rootKey, boolean alice)
throws DbException; throws DbException;
/** /**
@@ -43,15 +72,28 @@ public interface KeyManager {
*/ */
boolean canSendOutgoingStreams(ContactId c, TransportId t); boolean canSendOutgoingStreams(ContactId c, TransportId t);
/**
* Returns true if we have keys that can be used for outgoing streams to
* the given pending contact over the given transport.
*/
boolean canSendOutgoingStreams(PendingContactId p, TransportId t);
/** /**
* Returns a {@link StreamContext} for sending a stream to the given * Returns a {@link StreamContext} for sending a stream to the given
* contact over the given transport, or null if an error occurs or the * contact over the given transport, or null if an error occurs.
* contact does not support the transport.
*/ */
@Nullable @Nullable
StreamContext getStreamContext(ContactId c, TransportId t) StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException; throws DbException;
/**
* Returns a {@link StreamContext} for sending a stream to the given
* pending contact over the given transport, or null if an error occurs.
*/
@Nullable
StreamContext getStreamContext(PendingContactId p, TransportId t)
throws DbException;
/** /**
* Looks up the given tag and returns a {@link StreamContext} for reading * Looks up the given tag and returns a {@link StreamContext} for reading
* from the corresponding stream, or null if an error occurs or the tag was * from the corresponding stream, or null if an error occurs or the tag was

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

@@ -5,10 +5,8 @@ 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 set of
* keys within the scope of the local device. * {@link TransportKeySet transport keys} within the scope of the local device.
* <p/>
* Key sets created on a given device must have increasing identifiers.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault

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 or pending
* transport in a given rotation period. * contact over a given 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

@@ -1,29 +1,53 @@
package org.briarproject.bramble.api.transport; 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.contact.PendingContactId;
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.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
@Immutable
@NotNullByDefault
public class StreamContext { public class StreamContext {
@Nullable
private final ContactId contactId; private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final TransportId transportId; private final TransportId transportId;
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long streamNumber; private final long streamNumber;
private final boolean handshakeMode;
public StreamContext(ContactId contactId, TransportId transportId, public StreamContext(@Nullable ContactId contactId,
SecretKey tagKey, SecretKey headerKey, long streamNumber) { @Nullable PendingContactId pendingContactId,
TransportId transportId, SecretKey tagKey, SecretKey headerKey,
long streamNumber, boolean handshakeMode) {
requireExactlyOneNull(contactId, pendingContactId);
this.contactId = contactId; this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.transportId = transportId; this.transportId = transportId;
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.streamNumber = streamNumber; this.streamNumber = streamNumber;
this.handshakeMode = handshakeMode;
} }
@Nullable
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public TransportId getTransportId() { public TransportId getTransportId() {
return transportId; return transportId;
} }
@@ -39,4 +63,8 @@ public class StreamContext {
public long getStreamNumber() { public long getStreamNumber() {
return streamNumber; return streamNumber;
} }
public boolean isHandshakeMode() {
return handshakeMode;
}
} }

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,64 @@
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;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
/**
* A set of keys for communicating with a given contact or pending contact
* over a given transport.
*/
@Immutable
@NotNullByDefault
public class TransportKeySet {
private final KeySetId keySetId;
@Nullable
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final TransportKeys keys;
public TransportKeySet(KeySetId keySetId, @Nullable ContactId contactId,
@Nullable PendingContactId pendingContactId, TransportKeys keys) {
requireExactlyOneNull(contactId, pendingContactId);
this.keySetId = keySetId;
this.contactId = contactId;
this.pendingContactId = pendingContactId;
this.keys = keys;
}
public KeySetId getKeySetId() {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
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

@@ -1,29 +1,54 @@
package org.briarproject.bramble.api.transport; 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 org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/** /**
* Keys for communicating with a given contact over a given transport. * Keys for communicating with a given contact or pending contact over a given
* transport.
*/ */
@Immutable
@NotNullByDefault
public class TransportKeys { public class TransportKeys {
private final TransportId transportId; private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext; private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr; private final OutgoingKeys outCurr;
@Nullable
private final SecretKey rootKey;
private final boolean alice;
/**
* Constructor for rotation mode.
*/
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) this(transportId, inPrev, inCurr, inNext, outCurr, null, false);
}
/**
* Constructor for handshake mode.
*/
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr,
@Nullable SecretKey rootKey, boolean alice) {
if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (inNext.getRotationPeriod() != inCurr.getRotationPeriod() + 1) if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (outCurr.getRotationPeriod() != inCurr.getRotationPeriod()) if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.transportId = transportId; this.transportId = transportId;
this.inPrev = inPrev; this.inPrev = inPrev;
this.inCurr = inCurr; this.inCurr = inCurr;
this.inNext = inNext; this.inNext = inNext;
this.outCurr = outCurr; this.outCurr = outCurr;
this.rootKey = rootKey;
this.alice = alice;
} }
public TransportId getTransportId() { public TransportId getTransportId() {
@@ -46,7 +71,38 @@ public class TransportKeys {
return outCurr; return outCurr;
} }
public long getRotationPeriod() { public long getTimePeriod() {
return outCurr.getRotationPeriod(); return outCurr.getTimePeriod();
}
/**
* Returns true if these keys are for use in handshake mode or false if
* they're for use in rotation mode.
*/
public boolean isHandshakeMode() {
return rootKey != null;
}
/**
* If these keys are for use in handshake mode, returns the root key.
*
* @throws UnsupportedOperationException If these keys are for use in
* rotation mode
*/
public SecretKey getRootKey() {
if (rootKey == null) throw new UnsupportedOperationException();
return rootKey;
}
/**
* If these keys are for use in handshake mode, returns true if the local
* party is Alice.
*
* @throws UnsupportedOperationException If these keys are for use in
* rotation mode
*/
public boolean isAlice() {
if (rootKey == null) throw new UnsupportedOperationException();
return alice;
} }
} }

View File

@@ -38,8 +38,22 @@ public interface ClientVersioningManager {
Visibility getClientVisibility(Transaction txn, ContactId contactId, Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException; ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook { /**
* Returns the minor version of the given client that is supported by the
* given contact, or -1 if the contact does not support the client.
*/
int getClientMinorVersion(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook {
/**
* Called when the visibility of a client with respect to a contact is
* changing.
*
* @param txn A read-write transaction
* @param c The contact affected by the visibility change
* @param v The new visibility of the client
*/
void onClientVisibilityChanging(Transaction txn, Contact c, void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException; Visibility v) throws DbException;
} }

View File

@@ -0,0 +1,75 @@
package org.briarproject.bramble.util;
import java.io.ByteArrayOutputStream;
public class Base32 {
private static final char[] DIGITS = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
};
public static String encode(byte[] b) {
StringBuilder s = new StringBuilder();
int byteIndex = 0, currentCode = 0x00;
int byteMask = 0x80, codeMask = 0x10;
while (byteIndex < b.length) {
if ((b[byteIndex] & byteMask) != 0) currentCode |= codeMask;
// After every 8 bits, move on to the next byte
if (byteMask == 0x01) {
byteMask = 0x80;
byteIndex++;
} else {
byteMask >>>= 1;
}
// After every 5 bits, move on to the next digit
if (codeMask == 0x01) {
s.append(DIGITS[currentCode]);
codeMask = 0x10;
currentCode = 0x00;
} else {
codeMask >>>= 1;
}
}
// If we're part-way through a digit, output it
if (codeMask != 0x10) s.append(DIGITS[currentCode]);
return s.toString();
}
public static byte[] decode(String s, boolean strict) {
ByteArrayOutputStream b = new ByteArrayOutputStream();
int digitIndex = 0, digitCount = s.length(), currentByte = 0x00;
int byteMask = 0x80, codeMask = 0x10;
while (digitIndex < digitCount) {
int code = decodeDigit(s.charAt(digitIndex));
if ((code & codeMask) != 0) currentByte |= byteMask;
// After every 8 bits, move on to the next byte
if (byteMask == 0x01) {
b.write(currentByte);
byteMask = 0x80;
currentByte = 0x00;
} else {
byteMask >>>= 1;
}
// After every 5 bits, move on to the next digit
if (codeMask == 0x01) {
codeMask = 0x10;
digitIndex++;
} else {
codeMask >>>= 1;
}
}
// If any extra bits were used for encoding, they should all be zero
if (strict && byteMask != 0x80 && currentByte != 0x00)
throw new IllegalArgumentException();
return b.toByteArray();
}
private static int decodeDigit(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a';
if (c >= '2' && c <= '7') return c - '2' + 26;
throw new IllegalArgumentException("Not a base32 digit: " + c);
}
}

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.util; package org.briarproject.bramble.util;
import java.io.File;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -19,6 +20,7 @@ public class LogUtils {
/** /**
* Logs the duration of a task. * Logs the duration of a task.
*
* @param logger the logger to use * @param logger the logger to use
* @param task a description of the task * @param task a description of the task
* @param start the start time of the task, as returned by {@link #now()} * @param start the start time of the task, as returned by {@link #now()}
@@ -33,4 +35,26 @@ public class LogUtils {
public static void logException(Logger logger, Level level, Throwable t) { public static void logException(Logger logger, Level level, Throwable t) {
if (logger.isLoggable(level)) logger.log(level, t.toString(), t); if (logger.isLoggable(level)) logger.log(level, t.toString(), t);
} }
public static void logFileOrDir(Logger logger, Level level, File f) {
if (logger.isLoggable(level)) {
if (f.isFile()) {
logWithType(logger, level, f, "F");
} else if (f.isDirectory()) {
logWithType(logger, level, f, "D");
File[] children = f.listFiles();
if (children != null) {
for (File child : children)
logFileOrDir(logger, level, child);
}
} else if (f.exists()) {
logWithType(logger, level, f, "?");
}
}
}
private static void logWithType(Logger logger, Level level, File f,
String type) {
logger.log(level, type + " " + f.getAbsolutePath() + " " + f.length());
}
} }

View File

@@ -153,4 +153,13 @@ public class StringUtils {
return new String(c); return new String(c);
} }
public static String getRandomBase32String(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++) {
int character = random.nextInt(32);
if (character < 26) c[i] = (char) ('a' + character);
else c[i] = (char) ('2' + (character - 26));
}
return new String(c);
}
} }

View File

@@ -1,9 +1,21 @@
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.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
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.Identity;
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;
@@ -25,9 +37,10 @@ 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.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
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.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH; import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH; import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
@@ -41,6 +54,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();
@@ -93,27 +107,46 @@ public class TestUtils {
return new SecretKey(getRandomBytes(SecretKey.LENGTH)); return new SecretKey(getRandomBytes(SecretKey.LENGTH));
} }
public static LocalAuthor getLocalAuthor() { public static PublicKey getSignaturePublicKey() {
return getLocalAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH)); byte[] key = getRandomBytes(MAX_SIGNATURE_PUBLIC_KEY_BYTES);
return new SignaturePublicKey(key);
} }
public static LocalAuthor getLocalAuthor(int nameLength) { public static PrivateKey getSignaturePrivateKey() {
AuthorId id = new AuthorId(getRandomId()); return new SignaturePrivateKey(getRandomBytes(123));
String name = getRandomString(nameLength); }
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); public static PublicKey getAgreementPublicKey() {
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey, byte[] key = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
return new AgreementPublicKey(key);
}
public static PrivateKey getAgreementPrivateKey() {
return new AgreementPrivateKey(getRandomBytes(123));
}
public static Identity getIdentity() {
LocalAuthor localAuthor = getLocalAuthor();
PublicKey handshakePub = getAgreementPublicKey();
PrivateKey handshakePriv = getAgreementPrivateKey();
return new Identity(localAuthor, handshakePub, handshakePriv,
timestamp); timestamp);
} }
public static Author getAuthor() { public static LocalAuthor getLocalAuthor() {
return getAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH)); AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength);
PublicKey publicKey = getSignaturePublicKey();
PrivateKey privateKey = getSignaturePrivateKey();
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
} }
public static Author getAuthor(int nameLength) { public static Author getAuthor() {
AuthorId id = new AuthorId(getRandomId()); AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength); String name = getRandomString(nameLength);
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); PublicKey publicKey = getSignaturePublicKey();
return new Author(id, FORMAT_VERSION, name, publicKey); return new Author(id, FORMAT_VERSION, name, publicKey);
} }
@@ -140,6 +173,41 @@ 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());
PublicKey publicKey = getAgreementPublicKey();
String alias = getRandomString(nameLength);
int stateIndex =
random.nextInt(PendingContactState.values().length - 1);
PendingContactState state = PendingContactState.values()[stateIndex];
return new PendingContact(id, publicKey, alias, state, timestamp);
}
public static ContactId getContactId() {
return new ContactId(nextContactId.getAndIncrement());
}
public static Contact getContact() {
return getContact(getAuthor(), new AuthorId(getRandomId()),
random.nextBoolean());
}
public static Contact getContact(Author remote, AuthorId local,
boolean verified) {
return getContact(getContactId(), remote, local, verified);
}
public static Contact getContact(ContactId c, Author remote, AuthorId local,
boolean verified) {
return new Contact(c, remote, local,
getRandomString(MAX_AUTHOR_NAME_LENGTH),
getAgreementPublicKey(), 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

@@ -2,7 +2,7 @@ dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',

View File

@@ -17,7 +17,7 @@ dependencies {
implementation 'org.whispersystems:curve25519-java:0.5.0' implementation 'org.whispersystems:curve25519-java:0.5.0'
implementation 'org.briarproject:jtorctl:0.3' implementation 'org.briarproject:jtorctl:0.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.19' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6 testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
@@ -26,7 +26,7 @@ dependencies {
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.19' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -4,8 +4,8 @@ import org.briarproject.bramble.api.account.AccountManager;
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.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.Identity;
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.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
@@ -161,8 +161,8 @@ class AccountManagerImpl implements AccountManager {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
if (hasDatabaseKey()) if (hasDatabaseKey())
throw new AssertionError("Already have a database key"); throw new AssertionError("Already have a database key");
LocalAuthor localAuthor = identityManager.createLocalAuthor(name); Identity identity = identityManager.createIdentity(name);
identityManager.registerLocalAuthor(localAuthor); identityManager.registerIdentity(identity);
SecretKey key = crypto.generateSecretKey(); SecretKey key = crypto.generateSecretKey();
if (!encryptAndStoreDatabaseKey(key, password)) return false; if (!encryptAndStoreDatabaseKey(key, password)) return false;
databaseKey = key; databaseKey = key;

View File

@@ -3,6 +3,9 @@ package org.briarproject.bramble.client;
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.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
@@ -305,14 +308,15 @@ class ClientHelperImpl implements ClientHelper {
} }
@Override @Override
public byte[] sign(String label, BdfList toSign, byte[] privateKey) public byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
throws FormatException, GeneralSecurityException { throws FormatException, GeneralSecurityException {
return crypto.sign(label, toByteArray(toSign), privateKey); return crypto.sign(label, toByteArray(toSign), privateKey);
} }
@Override @Override
public void verifySignature(byte[] signature, String label, BdfList signed, public void verifySignature(byte[] signature, String label, BdfList signed,
byte[] publicKey) throws FormatException, GeneralSecurityException { PublicKey publicKey)
throws FormatException, GeneralSecurityException {
if (!crypto.verifySignature(signature, label, toByteArray(signed), if (!crypto.verifySignature(signature, label, toByteArray(signed),
publicKey)) { publicKey)) {
throw new GeneralSecurityException("Invalid signature"); throw new GeneralSecurityException("Invalid signature");
@@ -327,11 +331,29 @@ class ClientHelperImpl implements ClientHelper {
if (formatVersion != FORMAT_VERSION) throw new FormatException(); if (formatVersion != FORMAT_VERSION) throw new FormatException();
String name = author.getString(1); String name = author.getString(1);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH); checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = author.getRaw(2); byte[] publicKeyBytes = author.getRaw(2);
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH); checkLength(publicKeyBytes, 1, MAX_PUBLIC_KEY_LENGTH);
KeyParser parser = crypto.getSignatureKeyParser();
PublicKey publicKey;
try {
publicKey = parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
return authorFactory.createAuthor(formatVersion, name, publicKey); return authorFactory.createAuthor(formatVersion, name, publicKey);
} }
@Override
public PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
throws FormatException {
KeyParser parser = crypto.getAgreementKeyParser();
try {
return parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
}
@Override @Override
public TransportProperties parseAndValidateTransportProperties( public TransportProperties parseAndValidateTransportProperties(
BdfDictionary properties) throws FormatException { BdfDictionary properties) throws FormatException {

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,7 +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, localAuthor.getId(), masterKey, timestamp, alice,
true, true); true, true);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties); remoteProperties);

View File

@@ -1,8 +1,11 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
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.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
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.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -16,7 +19,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.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@@ -25,28 +27,36 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
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;
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;
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.util.StringUtils.getRandomBase32String;
import static org.briarproject.bramble.util.StringUtils.toUtf8; import static org.briarproject.bramble.util.StringUtils.toUtf8;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class ContactManagerImpl implements ContactManager { class ContactManagerImpl implements ContactManager {
private static final String REMOTE_CONTACT_LINK =
"briar://" + getRandomBase32String(BASE32_LINK_BYTES);
private final DatabaseComponent db; private final DatabaseComponent db;
private final KeyManager keyManager; private final KeyManager keyManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final PendingContactFactory pendingContactFactory;
private final List<ContactHook> hooks; private final List<ContactHook> hooks;
@Inject @Inject
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager, ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
IdentityManager identityManager) { IdentityManager identityManager,
PendingContactFactory pendingContactFactory) {
this.db = db; this.db = db;
this.keyManager = keyManager; this.keyManager = keyManager;
this.identityManager = identityManager; this.identityManager = identityManager;
this.pendingContactFactory = pendingContactFactory;
hooks = new CopyOnWriteArrayList<>(); hooks = new CopyOnWriteArrayList<>();
} }
@@ -57,10 +67,11 @@ class ContactManagerImpl implements ContactManager {
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException { boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active); ContactId c = db.addContact(txn, remote, local, verified);
keyManager.addContact(txn, c, master, timestamp, alice, active); keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp,
alice, active);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c; return c;
@@ -68,22 +79,47 @@ class ContactManagerImpl implements ContactManager {
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException { boolean verified) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active); ContactId c = db.addContact(txn, remote, local, verified);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c; return c;
} }
@Override @Override
public ContactId addContact(Author remote, AuthorId local, SecretKey master, public ContactId addContact(Author remote, AuthorId local,
long timestamp, boolean alice, boolean verified, boolean active) SecretKey rootKey, long timestamp, boolean alice, boolean verified,
throws DbException { boolean active) throws DbException {
return db.transactionWithResult(false, txn -> return db.transactionWithResult(false, txn ->
addContact(txn, remote, local, master, timestamp, alice, addContact(txn, remote, local, rootKey, timestamp, alice,
verified, active)); verified, active));
} }
@Override
public String getHandshakeLink() {
// TODO replace with real implementation
return REMOTE_CONTACT_LINK;
}
@Override
public PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException {
PendingContact p =
pendingContactFactory.createPendingContact(link, alias);
db.transaction(false, txn -> db.addPendingContact(txn, p));
return p;
}
@Override
public Collection<PendingContact> getPendingContacts() throws DbException {
return db.transactionWithResult(true, db::getPendingContacts);
}
@Override
public void removePendingContact(PendingContactId p) throws DbException {
db.transaction(false, txn -> db.removePendingContact(txn, p));
}
@Override @Override
public Contact getContact(ContactId c) throws DbException { public Contact getContact(ContactId c) throws DbException {
return db.transactionWithResult(true, txn -> db.getContact(txn, c)); return db.transactionWithResult(true, txn -> db.getContact(txn, c));
@@ -110,12 +146,8 @@ class ContactManagerImpl implements ContactManager {
} }
@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
@@ -123,12 +155,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 {

View File

@@ -28,4 +28,10 @@ public class ContactModule {
ContactExchangeTaskImpl contactExchangeTask) { ContactExchangeTaskImpl contactExchangeTask) {
return contactExchangeTask; return contactExchangeTask;
} }
@Provides
PendingContactFactory providePendingContactFactory(
PendingContactFactoryImpl pendingContactFactory) {
return pendingContactFactory;
}
} }

View File

@@ -0,0 +1,18 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.contact.PendingContact;
interface PendingContactFactory {
/**
* Creates a {@link PendingContact} from the given handshake link and alias.
*
* @throws UnsupportedVersionException If the link uses a format version
* that is not supported
* @throws FormatException If the link is invalid
*/
PendingContact createPendingContact(String link, String alias)
throws FormatException;
}

View File

@@ -0,0 +1,70 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.Base32;
import java.security.GeneralSecurityException;
import java.util.regex.Matcher;
import javax.inject.Inject;
import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
class PendingContactFactoryImpl implements PendingContactFactory {
private final CryptoComponent crypto;
private final Clock clock;
@Inject
PendingContactFactoryImpl(CryptoComponent crypto, Clock clock) {
this.crypto = crypto;
this.clock = clock;
}
@Override
public PendingContact createPendingContact(String link, String alias)
throws FormatException {
PublicKey publicKey = parseHandshakeLink(link);
PendingContactId id = getPendingContactId(publicKey);
long timestamp = clock.currentTimeMillis();
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
}
private PublicKey parseHandshakeLink(String link) throws FormatException {
Matcher matcher = LINK_REGEX.matcher(link);
if (!matcher.find()) throw new FormatException();
// Discard 'briar://' and anything before or after the link
link = matcher.group(2);
byte[] base32 = Base32.decode(link, false);
if (base32.length != RAW_LINK_BYTES) throw new AssertionError();
byte version = base32[0];
if (version != FORMAT_VERSION)
throw new UnsupportedVersionException(version < FORMAT_VERSION);
byte[] publicKeyBytes = new byte[base32.length - 1];
arraycopy(base32, 1, publicKeyBytes, 0, publicKeyBytes.length);
try {
KeyParser parser = crypto.getAgreementKeyParser();
return parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
}
private PendingContactId getPendingContactId(PublicKey publicKey) {
byte[] hash = crypto.hash(ID_LABEL, publicKey.getEncoded());
return new PendingContactId(hash);
}
}

View File

@@ -1,5 +1,7 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
@@ -7,21 +9,24 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault @NotNullByDefault
class Curve25519KeyParser implements KeyParser { class AgreementKeyParser implements KeyParser {
@Override @Override
public PublicKey parsePublicKey(byte[] encodedKey) public PublicKey parsePublicKey(byte[] encodedKey)
throws GeneralSecurityException { throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException(); if (encodedKey.length != 32) throw new GeneralSecurityException();
return new Curve25519PublicKey(encodedKey); return new AgreementPublicKey(encodedKey);
} }
@Override @Override
public PrivateKey parsePrivateKey(byte[] encodedKey) public PrivateKey parsePrivateKey(byte[] encodedKey)
throws GeneralSecurityException { throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException(); if (encodedKey.length != 32) throw new GeneralSecurityException();
return new Curve25519PrivateKey(clamp(encodedKey)); return new AgreementPrivateKey(clamp(encodedKey));
} }
static byte[] clamp(byte[] b) { static byte[] clamp(byte[] b) {

View File

@@ -4,12 +4,16 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey; import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.KeyPairGenerator; import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
@@ -31,6 +35,8 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -80,8 +86,8 @@ class CryptoComponentImpl implements CryptoComponent {
signatureKeyPairGenerator = new KeyPairGenerator(); signatureKeyPairGenerator = new KeyPairGenerator();
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS, signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
secureRandom); secureRandom);
agreementKeyParser = new Curve25519KeyParser(); agreementKeyParser = new AgreementKeyParser();
signatureKeyParser = new EdKeyParser(); signatureKeyParser = new SignatureKeyParser();
messageEncrypter = new MessageEncrypter(secureRandom); messageEncrypter = new MessageEncrypter(secureRandom);
} }
@@ -125,9 +131,9 @@ class CryptoComponentImpl implements CryptoComponent {
// Package access for testing // Package access for testing
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub) byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
throws GeneralSecurityException { throws GeneralSecurityException {
if (!(priv instanceof Curve25519PrivateKey)) if (!priv.getKeyType().equals(KEY_TYPE_AGREEMENT))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (!(pub instanceof Curve25519PublicKey)) if (!pub.getKeyType().equals(KEY_TYPE_AGREEMENT))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
long start = now(); long start = now();
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(), byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
@@ -143,8 +149,8 @@ class CryptoComponentImpl implements CryptoComponent {
@Override @Override
public KeyPair generateAgreementKeyPair() { public KeyPair generateAgreementKeyPair() {
Curve25519KeyPair keyPair = curve25519.generateKeyPair(); Curve25519KeyPair keyPair = curve25519.generateKeyPair();
PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey()); PublicKey pub = new AgreementPublicKey(keyPair.getPublicKey());
PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey()); PrivateKey priv = new AgreementPrivateKey(keyPair.getPrivateKey());
return new KeyPair(pub, priv); return new KeyPair(pub, priv);
} }
@@ -158,9 +164,9 @@ class CryptoComponentImpl implements CryptoComponent {
java.security.KeyPair keyPair = java.security.KeyPair keyPair =
signatureKeyPairGenerator.generateKeyPair(); signatureKeyPairGenerator.generateKeyPair();
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic(); EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte()); PublicKey publicKey = new SignaturePublicKey(edPublicKey.getAbyte());
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate(); EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed()); PrivateKey privateKey = new SignaturePrivateKey(edPrivateKey.getSeed());
return new KeyPair(publicKey, privateKey); return new KeyPair(publicKey, privateKey);
} }
@@ -195,21 +201,22 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public byte[] sign(String label, byte[] toSign, byte[] privateKey) public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
throws GeneralSecurityException { throws GeneralSecurityException {
PrivateKey key = signatureKeyParser.parsePrivateKey(privateKey);
Signature sig = new EdSignature(); Signature sig = new EdSignature();
sig.initSign(key); sig.initSign(privateKey);
updateSignature(sig, label, toSign); updateSignature(sig, label, toSign);
return sig.sign(); return sig.sign();
} }
@Override @Override
public boolean verifySignature(byte[] signature, String label, public boolean verifySignature(byte[] signature, String label,
byte[] signed, byte[] publicKey) throws GeneralSecurityException { byte[] signed, PublicKey publicKey)
PublicKey key = signatureKeyParser.parsePublicKey(publicKey); throws GeneralSecurityException {
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException();
Signature sig = new EdSignature(); Signature sig = new EdSignature();
sig.initVerify(key); sig.initVerify(publicKey);
updateSignature(sig, label, signed); updateSignature(sig, label, signed);
return sig.verify(signature); return sig.verify(signature);
} }

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class Curve25519PrivateKey extends Bytes implements PrivateKey {
Curve25519PrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class Curve25519PublicKey extends Bytes implements PublicKey {
Curve25519PublicKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class EdPrivateKey extends Bytes implements PrivateKey {
EdPrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class EdPublicKey extends Bytes implements PublicKey {
EdPublicKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -17,6 +17,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.Provider; import java.security.Provider;
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM; import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
@NotNullByDefault @NotNullByDefault
class EdSignature implements Signature { class EdSignature implements Signature {
@@ -39,7 +40,7 @@ class EdSignature implements Signature {
@Override @Override
public void initSign(PrivateKey k) throws GeneralSecurityException { public void initSign(PrivateKey k) throws GeneralSecurityException {
if (!(k instanceof EdPrivateKey)) if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
EdDSAPrivateKey privateKey = new EdDSAPrivateKey( EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC)); new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
@@ -48,7 +49,7 @@ class EdSignature implements Signature {
@Override @Override
public void initVerify(PublicKey k) throws GeneralSecurityException { public void initVerify(PublicKey k) throws GeneralSecurityException {
if (!(k instanceof EdPublicKey)) if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
EdDSAPublicKey publicKey = new EdDSAPublicKey( EdDSAPublicKey publicKey = new EdDSAPublicKey(
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC)); new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));

View File

@@ -49,6 +49,7 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class MessageEncrypter { public class MessageEncrypter {
private static final String KEY_TYPE = "SEC1_brainpoolp512r1";
private static final ECDomainParameters PARAMETERS; private static final ECDomainParameters PARAMETERS;
private static final int MESSAGE_KEY_BITS = 512; private static final int MESSAGE_KEY_BITS = 512;
private static final int MAC_KEY_BITS = 256; private static final int MAC_KEY_BITS = 256;
@@ -69,7 +70,7 @@ public class MessageEncrypter {
MessageEncrypter(SecureRandom random) { MessageEncrypter(SecureRandom random) {
generator = new ECKeyPairGenerator(); generator = new ECKeyPairGenerator();
generator.init(new ECKeyGenerationParameters(PARAMETERS, random)); generator.init(new ECKeyGenerationParameters(PARAMETERS, random));
parser = new Sec1KeyParser(PARAMETERS, MESSAGE_KEY_BITS); parser = new Sec1KeyParser(KEY_TYPE, PARAMETERS, MESSAGE_KEY_BITS);
KeyEncoder encoder = new PublicKeyEncoder(); KeyEncoder encoder = new PublicKeyEncoder();
ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder); ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder);
ephemeralParser = new PublicKeyParser(PARAMETERS); ephemeralParser = new PublicKeyParser(PARAMETERS);
@@ -80,11 +81,11 @@ public class MessageEncrypter {
// Return a wrapper that uses the SEC 1 encoding // Return a wrapper that uses the SEC 1 encoding
ECPublicKeyParameters ecPublicKey = ECPublicKeyParameters ecPublicKey =
(ECPublicKeyParameters) keyPair.getPublic(); (ECPublicKeyParameters) keyPair.getPublic();
PublicKey publicKey = new Sec1PublicKey(ecPublicKey); PublicKey publicKey = new Sec1PublicKey(KEY_TYPE, ecPublicKey);
ECPrivateKeyParameters ecPrivateKey = ECPrivateKeyParameters ecPrivateKey =
(ECPrivateKeyParameters) keyPair.getPrivate(); (ECPrivateKeyParameters) keyPair.getPrivate();
PrivateKey privateKey = PrivateKey privateKey =
new Sec1PrivateKey(ecPrivateKey, MESSAGE_KEY_BITS); new Sec1PrivateKey(KEY_TYPE, ecPrivateKey, MESSAGE_KEY_BITS);
return new KeyPair(publicKey, privateKey); return new KeyPair(publicKey, privateKey);
} }

View File

@@ -31,11 +31,13 @@ class Sec1KeyParser implements KeyParser {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(Sec1KeyParser.class.getName()); Logger.getLogger(Sec1KeyParser.class.getName());
private final String keyType;
private final ECDomainParameters params; private final ECDomainParameters params;
private final BigInteger modulus; private final BigInteger modulus;
private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes; private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes;
Sec1KeyParser(ECDomainParameters params, int keyBits) { Sec1KeyParser(String keyType, ECDomainParameters params, int keyBits) {
this.keyType = keyType;
this.params = params; this.params = params;
this.keyBits = keyBits; this.keyBits = keyBits;
modulus = ((ECCurve.Fp) params.getCurve()).getQ(); modulus = ((ECCurve.Fp) params.getCurve()).getQ();
@@ -80,7 +82,7 @@ class Sec1KeyParser implements KeyParser {
throw new GeneralSecurityException(); throw new GeneralSecurityException();
// Construct a public key from the point (x, y) and the params // Construct a public key from the point (x, y) and the params
ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params); ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params);
PublicKey p = new Sec1PublicKey(k); PublicKey p = new Sec1PublicKey(keyType, k);
logDuration(LOG, "Parsing public key", start); logDuration(LOG, "Parsing public key", start);
return p; return p;
} }
@@ -97,7 +99,7 @@ class Sec1KeyParser implements KeyParser {
throw new GeneralSecurityException(); throw new GeneralSecurityException();
// Construct a private key from the private value and the params // Construct a private key from the private value and the params
ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params); ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params);
PrivateKey p = new Sec1PrivateKey(k, keyBits); PrivateKey p = new Sec1PrivateKey(keyType, k, keyBits);
logDuration(LOG, "Parsing private key", start); logDuration(LOG, "Parsing private key", start);
return p; return p;
} }

View File

@@ -10,14 +10,21 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
class Sec1PrivateKey implements PrivateKey { class Sec1PrivateKey implements PrivateKey {
private final String keyType;
private final ECPrivateKeyParameters key; private final ECPrivateKeyParameters key;
private final int bytesPerInt; private final int bytesPerInt;
Sec1PrivateKey(ECPrivateKeyParameters key, int keyBits) { Sec1PrivateKey(String keyType, ECPrivateKeyParameters key, int keyBits) {
this.keyType = keyType;
this.key = key; this.key = key;
bytesPerInt = (keyBits + 7) / 8; bytesPerInt = (keyBits + 7) / 8;
} }
@Override
public String getKeyType() {
return keyType;
}
@Override @Override
public byte[] getEncoded() { public byte[] getEncoded() {
byte[] encodedKey = new byte[bytesPerInt]; byte[] encodedKey = new byte[bytesPerInt];

View File

@@ -15,12 +15,19 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
class Sec1PublicKey implements PublicKey { class Sec1PublicKey implements PublicKey {
private final String keyType;
private final ECPublicKeyParameters key; private final ECPublicKeyParameters key;
Sec1PublicKey(ECPublicKeyParameters key) { Sec1PublicKey(String keyType, ECPublicKeyParameters key) {
this.keyType = keyType;
this.key = key; this.key = key;
} }
@Override
public String getKeyType() {
return keyType;
}
@Override @Override
public byte[] getEncoded() { public byte[] getEncoded() {
return key.getQ().getEncoded(false); return key.getQ().getEncoded(false);

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