mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
98 Commits
contact-re
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37d0b61d7b | ||
|
|
98a1ec89d7 | ||
|
|
a61860af85 | ||
|
|
90437f4fa4 | ||
|
|
06212453b2 | ||
|
|
ddc9b5b066 | ||
|
|
0aa6166afa | ||
|
|
60b91bc317 | ||
|
|
20481a3790 | ||
|
|
576934910e | ||
|
|
4abc26093e | ||
|
|
aed63746e7 | ||
|
|
816598b631 | ||
|
|
c062c16d27 | ||
|
|
435b43488a | ||
|
|
faa6a85148 | ||
|
|
3770a9f217 | ||
|
|
c6211be488 | ||
|
|
5a73e50248 | ||
|
|
dc6971734a | ||
|
|
69e57bee61 | ||
|
|
af8cabbb28 | ||
|
|
6f31a3c2ad | ||
|
|
d3469e3782 | ||
|
|
9d64b186ff | ||
|
|
ca591b5c7b | ||
|
|
2c4188caf5 | ||
|
|
0b30a0786e | ||
|
|
f9b928c12a | ||
|
|
afa0b96293 | ||
|
|
dd50f4bcd4 | ||
|
|
f42fc5213e | ||
|
|
84e2402404 | ||
|
|
5adc9d8dbd | ||
|
|
3f51ad6c07 | ||
|
|
1fd6d7a6d5 | ||
|
|
7dc4dc566f | ||
|
|
658c63d94e | ||
|
|
ee05c32871 | ||
|
|
d2951eb3cd | ||
|
|
de8a60ea21 | ||
|
|
0e77a47cc1 | ||
|
|
421ca309c7 | ||
|
|
43787deafd | ||
|
|
22ebdd8e42 | ||
|
|
e37ee7ee04 | ||
|
|
5676e18a22 | ||
|
|
5ece6505da | ||
|
|
451edba467 | ||
|
|
5880479987 | ||
|
|
71d8fb2083 | ||
|
|
0825e77dd7 | ||
|
|
593a709a7f | ||
|
|
322fefb2a2 | ||
|
|
8005cdc659 | ||
|
|
33fdca4aa1 | ||
|
|
e5fc91b620 | ||
|
|
9c08073e49 | ||
|
|
5553b7d0e4 | ||
|
|
2cce0f5fe2 | ||
|
|
ebae1037be | ||
|
|
0c99ef0e5b | ||
|
|
faba9a6b70 | ||
|
|
891c82b2e5 | ||
|
|
56fbc93962 | ||
|
|
251eb9e712 | ||
|
|
8b2b7599f9 | ||
|
|
8c315382e2 | ||
|
|
8183a48ebb | ||
|
|
f6611daf7b | ||
|
|
00bc8ac768 | ||
|
|
75776eb7de | ||
|
|
f0a3130bf3 | ||
|
|
64aa121c9c | ||
|
|
cc3486df94 | ||
|
|
cd24be7e42 | ||
|
|
fa562b40bc | ||
|
|
fc8ca872a8 | ||
|
|
5b63eab314 | ||
|
|
6f0ab8b688 | ||
|
|
dfc567cbfd | ||
|
|
de98a4cb12 | ||
|
|
fbe375cc4e | ||
|
|
19bc73ac61 | ||
|
|
d17331b578 | ||
|
|
bec1f117ba | ||
|
|
2c014b4e46 | ||
|
|
7a71d2bad4 | ||
|
|
4bf21b2f3b | ||
|
|
4a57939b80 | ||
|
|
66cdf4f595 | ||
|
|
3384477499 | ||
|
|
58ffc6e761 | ||
|
|
df5ac59fc9 | ||
|
|
dc649b195a | ||
|
|
3d9a8f9bf8 | ||
|
|
96975e0d43 | ||
|
|
6691e708e4 |
25
.idea/codeStyles/Project.xml
generated
25
.idea/codeStyles/Project.xml
generated
@@ -39,31 +39,6 @@
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</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>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10106
|
||||
versionName "1.1.6"
|
||||
versionCode 10107
|
||||
versionName "1.1.7"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
@@ -33,7 +33,7 @@ dependencies {
|
||||
tor 'org.briarproject:tor-android:0.3.5.8@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'
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.util.IoUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
@@ -20,6 +19,9 @@ import javax.annotation.Nullable;
|
||||
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
|
||||
implements AccountManager {
|
||||
@@ -41,6 +43,16 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
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
|
||||
@Override
|
||||
@Nullable
|
||||
@@ -74,9 +86,17 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
@Override
|
||||
public void deleteAccount() {
|
||||
synchronized (stateChangeLock) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of account directory before deleting:");
|
||||
logFileOrDir(LOG, INFO, getDataDir());
|
||||
}
|
||||
super.deleteAccount();
|
||||
SharedPreferences defaultPrefs = getDefaultSharedPreferences();
|
||||
deleteAppData(prefs, defaultPrefs);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of account directory after deleting:");
|
||||
logFileOrDir(LOG, INFO, getDataDir());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +114,7 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
}
|
||||
// Delete files, except lib and shared_prefs directories
|
||||
Set<File> files = new HashSet<>();
|
||||
File dataDir = new File(appContext.getApplicationInfo().dataDir);
|
||||
File dataDir = getDataDir();
|
||||
@Nullable
|
||||
File[] fileArray = dataDir.listFiles();
|
||||
if (fileArray == null) {
|
||||
@@ -121,13 +141,17 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
}
|
||||
}
|
||||
for (File file : files) {
|
||||
IoUtils.deleteFileOrDir(file);
|
||||
deleteFileOrDir(file);
|
||||
}
|
||||
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
||||
if (!new File(dataDir, "cache").mkdirs())
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
will(returnValue(true));
|
||||
oneOf(app).getApplicationInfo();
|
||||
allowing(app).getApplicationInfo();
|
||||
will(returnValue(applicationInfo));
|
||||
oneOf(app).getFilesDir();
|
||||
will(returnValue(filesDir));
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||
'com.android.tools.analytics-library:protos:26.3.2:protos-26.3.2.jar:50238fb4298b297217b184b9cd93c14f83536fcee829eb0ca850bdb5b53251f0',
|
||||
'com.android.tools.analytics-library:shared:26.3.2:shared-26.3.2.jar:ddd80dcf21905018b7c0583ba72b7282f446084d4952422609a09fbf8237ef71',
|
||||
'com.android.tools.analytics-library:tracker:26.3.2:tracker-26.3.2.jar:28c575d2d1af003e96d7b375a668ee10b2673a2dd0f6438750aa8c3b42e7d0ad',
|
||||
'com.android.tools.build:apksig:3.3.2:apksig-3.3.2.jar:84c4aaa20127c6c1fe6bdd334b3f5df71f54ad080be9029c8a10f43b6a908acd',
|
||||
'com.android.tools.build:apkzlib:3.3.2:apkzlib-3.3.2.jar:d34e523278e5dff565eba3ef3c089d515b2b5cc7b47dc77e2f3465e5e47176ac',
|
||||
'com.android.tools.build:builder-model:3.3.2:builder-model-3.3.2.jar:055e3db0ecee9e06b9f024034999a29cd92cb1885207b37542126bd8bcc57f46',
|
||||
'com.android.tools.build:builder-test-api:3.3.2:builder-test-api-3.3.2.jar:0b2e4cd7615bbcad14a3c91fe45ae26693508d06e40ba06c5968b8bc24416618',
|
||||
'com.android.tools.build:builder:3.3.2:builder-3.3.2.jar:65649704da7aef0487235fd326f0f2e99ed5cf958e80f204496e6e08a42bd9f5',
|
||||
'com.android.tools.build:gradle-api:3.3.2:gradle-api-3.3.2.jar:3cbd47e41bb70330dd72ec2c9fe51e6173554b484a03829b5a2de9e00841e040',
|
||||
'com.android.tools.build:manifest-merger:26.3.2:manifest-merger-26.3.2.jar:05c4a6d8b02fb9f08744876477d0a68547c03a8a9069b1f086684fa04af97c33',
|
||||
'com.android.tools.ddms:ddmlib:26.3.2:ddmlib-26.3.2.jar:d248da8a563d6e46d2c7ebbf371a4877e00510f4ca763c0bb272d5a281bf8b85',
|
||||
'com.android.tools.external.com-intellij:intellij-core:26.3.2:intellij-core-26.3.2.jar:6c5ecc968230e9f4dcd0fef28885379feace1f0cd8130de6f61d649c86139bf3',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.3.2:kotlin-compiler-26.3.2.jar:1007d9b07ccb49cd8eaf30fda10ed4681d4714f2f9ab2ecda39b4e539cc51bbe',
|
||||
'com.android.tools.external.org-jetbrains:uast:26.3.2:uast-26.3.2.jar:5d1833e562ea4f38a89708dfde695f0a162cbd39d003d3dde818c3fdc2b05317',
|
||||
'com.android.tools.layoutlib:layoutlib-api:26.3.2:layoutlib-api-26.3.2.jar:d7e61e874ab95f5c350dd38b6a95b5c9dbe0083a02001884264cdb390cb255b8',
|
||||
'com.android.tools.lint:lint-api:26.3.2:lint-api-26.3.2.jar:5867dfd7fb4a4e161a816a5d29d045f9b542d34594c00a1efec46fb4cd0e1033',
|
||||
'com.android.tools.lint:lint-checks:26.3.2:lint-checks-26.3.2.jar:4b163b9c93790d2771e92ba8de58a0d9e0671ffcf2ccef3cf496efd442e27517',
|
||||
'com.android.tools.lint:lint-gradle-api:26.3.2:lint-gradle-api-26.3.2.jar:54cb282e0c054f9bed3f51302ce08b003c8ab7961dfd5a4f6de26c23cc23062f',
|
||||
'com.android.tools.lint:lint-gradle:26.3.2:lint-gradle-26.3.2.jar:bb139615f4ce97d42cc394b9389b49b76a6eb85be6785a5d272991543b519013',
|
||||
'com.android.tools.lint:lint:26.3.2:lint-26.3.2.jar:ef7b369f8a56a92ccb0f4c1c357666b9339e4a711a9d84747d446441746cfe4e',
|
||||
'com.android.tools:annotations:26.3.2:annotations-26.3.2.jar:5bcce8e98b6a2f5ccf13ebcefd8f734e0b35f8b19e456575665631442ce1f7a1',
|
||||
'com.android.tools:common:26.3.2:common-26.3.2.jar:d9f8e7f0669e9a701568e3db6a87c89cf12d8fa6811c9991e969f950215ecfac',
|
||||
'com.android.tools:dvlib:26.3.2:dvlib-26.3.2.jar:d84aad56161c7773579303d69714ded6897c64c6ddfd7d456e453231e4dfe811',
|
||||
'com.android.tools:repository:26.3.2:repository-26.3.2.jar:da611eeb06e9ab8750d25b9e2901e10db8e5ec6304eb4c8b7103d39e0921ea40',
|
||||
'com.android.tools:sdk-common:26.3.2:sdk-common-26.3.2.jar:82823a3bf25e64fac33a286490f0cf5ac50c2cdb3c540149b030896bb44bf96c',
|
||||
'com.android.tools:sdklib:26.3.2:sdklib-26.3.2.jar:424d15492af67321900963238646d27495ab60de2a5b19e6a416963bc5d6932b',
|
||||
'com.android.tools.analytics-library:protos:26.4.0:protos-26.4.0.jar:ad760915586797d39319f402837b378bff3bb4ed583e3e0c48c965631fb2135f',
|
||||
'com.android.tools.analytics-library:shared:26.4.0:shared-26.4.0.jar:1332106a905d48909c81268c9e414946de3e83487db394c6073b0a9b5c3d0ed2',
|
||||
'com.android.tools.analytics-library:tracker:26.4.0:tracker-26.4.0.jar:d0020cfbfd4cd75935f2972d6a24089840d4a10df6f3ef2a796093217dd37796',
|
||||
'com.android.tools.build:apksig:3.4.0:apksig-3.4.0.jar:91d5a1866139c69756280355a6f61b4d619d0516841580114f45a10f2177327e',
|
||||
'com.android.tools.build:apkzlib:3.4.0:apkzlib-3.4.0.jar:8653c85f5fdf1dde840e8b8af7396aeb79c34b66e541b5860059616006535592',
|
||||
'com.android.tools.build:builder-model:3.4.0:builder-model-3.4.0.jar:a88f138124a9f016a70bcb4760359a502f65c7deed56507ee4014f4dd9ea853b',
|
||||
'com.android.tools.build:builder-test-api:3.4.0:builder-test-api-3.4.0.jar:31089ab1ec19ca7687a010867d2f3807513c805b8226979706f4247b5d4df26f',
|
||||
'com.android.tools.build:builder:3.4.0:builder-3.4.0.jar:476221b5203a7f50089bf185ed95000a34b6f5020ef0a17815afd58606922679',
|
||||
'com.android.tools.build:gradle-api:3.4.0:gradle-api-3.4.0.jar:215eca38f6719213c2f492b4d622cdd11676c66c9871f8a2aed0c66d00175628',
|
||||
'com.android.tools.build:manifest-merger:26.4.0:manifest-merger-26.4.0.jar:29e45e690dedd165035e97c21c2ca94d0bd4ec16b6b210daa26669a582b6f220',
|
||||
'com.android.tools.ddms:ddmlib:26.4.0:ddmlib-26.4.0.jar:93f56fe4630c3166adbd6c51d7bb602d96abb91b07ba5b1165fdcd071e88c940',
|
||||
'com.android.tools.external.com-intellij:intellij-core:26.4.0:intellij-core-26.4.0.jar:30cb0e879d4424de9677a50b537fb628636b4a50f5470af5e52437980c41421f',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.4.0:kotlin-compiler-26.4.0.jar:dd1fe225c31a0e012dc025336363a5b783e2c5c20ffb69e77f8f57e89420d998',
|
||||
'com.android.tools.external.org-jetbrains:uast:26.4.0:uast-26.4.0.jar:f25f3285b775a983327583ff6584dea54e447813ef69e0ce08b05a45b5f4aab0',
|
||||
'com.android.tools.layoutlib:layoutlib-api:26.4.0:layoutlib-api-26.4.0.jar:52128f5cf293b224072be361919bfd416e59480ab7264ddcdbbf046b0d7a12e3',
|
||||
'com.android.tools.lint:lint-api:26.4.0:lint-api-26.4.0.jar:fdb8fca8ae4c254f438338d03d72605e00ed106f2d5550405af41ca1c8509401',
|
||||
'com.android.tools.lint:lint-checks:26.4.0:lint-checks-26.4.0.jar:4ff52d40488cd3e22b9c6b2eb67784e0c3269d0b42ef9d17689cd75a7b2bceb4',
|
||||
'com.android.tools.lint:lint-gradle-api:26.4.0:lint-gradle-api-26.4.0.jar:714b7a85c7d2aa10daeab16e969fe7530c659d0728a7f24021da456870418d0f',
|
||||
'com.android.tools.lint:lint-gradle:26.4.0:lint-gradle-26.4.0.jar:b8c130d273f522388734457e1b96790f41528fcec6fda9e8eaa4e4d95a07cfbb',
|
||||
'com.android.tools.lint:lint:26.4.0:lint-26.4.0.jar:83aa062fb0405b60ed358d858c8c2955e1bae44a455b498068c6a60988755f00',
|
||||
'com.android.tools:annotations:26.4.0:annotations-26.4.0.jar:a7955b8e19c3a2a861d6faa43a58b7c0d46ea9112188ee3e235c6f9f439ecc1a',
|
||||
'com.android.tools:common:26.4.0:common-26.4.0.jar:ea40b94b3c1284ea7700f011388e2906a8363a66abd902891722b3c557984852',
|
||||
'com.android.tools:dvlib:26.4.0:dvlib-26.4.0.jar:23af89c535b01ba36ceed1b6b309b672814eba624e643cd7dedf0519edad50cc',
|
||||
'com.android.tools:repository:26.4.0:repository-26.4.0.jar:3d1763ab46199374dc6d94129bba11c70f1d5857e2c81a3ac4898abca40b176b',
|
||||
'com.android.tools:sdk-common:26.4.0:sdk-common-26.4.0.jar:78a522525b30ffc6b7bf1299c831d24ce385f68a9f4878f8f752e9baefa31b0f',
|
||||
'com.android.tools:sdklib:26.4.0:sdklib-26.4.0.jar:b854c23892013a326d761cf071c72cf3e038ed0469d10f4a356829fa56e4c132',
|
||||
'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.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc',
|
||||
'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768',
|
||||
'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
|
||||
'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
|
||||
'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
|
||||
'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
|
||||
'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
|
||||
'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
|
||||
'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.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
|
||||
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
|
||||
'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
|
||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
|
||||
'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.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
|
||||
@@ -48,7 +48,7 @@ dependencyVerification {
|
||||
'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.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',
|
||||
'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',
|
||||
@@ -60,9 +60,9 @@ dependencyVerification {
|
||||
'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.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:httpcore:4.4.5:httpcore-4.4.5.jar:64d5453874cab7e40a7065cb01a9a9ca1053845a9786b478878b679e0580cec3',
|
||||
'org.apache.httpcomponents:httpmime:4.5.2:httpmime-4.5.2.jar:231a3f7e4962053db2be8461d5422e68fc458a3a7dd7d8ada803a348e21f8f07',
|
||||
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
|
||||
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
|
||||
'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.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||
|
||||
@@ -7,7 +7,7 @@ apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
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'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.util.StringUtils;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
/**
|
||||
@@ -38,7 +39,7 @@ public class Bytes implements Comparable<Bytes> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,8 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
|
||||
/**
|
||||
* 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.
|
||||
* If this is thrown, delivery will be attempted again at next startup,
|
||||
* whereas if a FormatException is thrown, the message will be permanently
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.bramble.api.client;
|
||||
|
||||
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.BdfList;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -96,14 +98,18 @@ public interface ClientHelper {
|
||||
|
||||
BdfList toList(Author a);
|
||||
|
||||
byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
||||
byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||
throws FormatException, GeneralSecurityException;
|
||||
|
||||
void verifySignature(byte[] signature, String label, BdfList signed,
|
||||
byte[] publicKey) throws FormatException, GeneralSecurityException;
|
||||
PublicKey publicKey)
|
||||
throws FormatException, GeneralSecurityException;
|
||||
|
||||
Author parseAndValidateAuthor(BdfList author) throws FormatException;
|
||||
|
||||
PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
|
||||
throws FormatException;
|
||||
|
||||
TransportProperties parseAndValidateTransportProperties(
|
||||
BdfDictionary properties) throws FormatException;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.AuthorId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@@ -8,7 +9,6 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||
|
||||
@Immutable
|
||||
@@ -21,21 +21,17 @@ public class Contact {
|
||||
@Nullable
|
||||
private final String alias;
|
||||
@Nullable
|
||||
private final byte[] handshakePublicKey;
|
||||
private final PublicKey handshakePublicKey;
|
||||
private final boolean verified;
|
||||
|
||||
public Contact(ContactId id, Author author, AuthorId localAuthorId,
|
||||
@Nullable String alias, @Nullable byte[] handshakePublicKey,
|
||||
@Nullable String alias, @Nullable PublicKey handshakePublicKey,
|
||||
boolean verified) {
|
||||
if (alias != null) {
|
||||
int aliasLength = toUtf8(alias).length;
|
||||
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (handshakePublicKey != null && (handshakePublicKey.length == 0 ||
|
||||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.id = id;
|
||||
this.author = author;
|
||||
this.localAuthorId = localAuthorId;
|
||||
@@ -62,7 +58,7 @@ public class Contact {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public byte[] getHandshakePublicKey() {
|
||||
public PublicKey getHandshakePublicKey() {
|
||||
return handshakePublicKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.contact;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ public class ContactId {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return o instanceof ContactId && id == ((ContactId) o).id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
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.db.DbException;
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
@@ -53,34 +56,34 @@ public interface ContactManager {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the static link that needs to be sent to the contact to be added.
|
||||
* Returns the handshake link that needs to be sent to a contact we want
|
||||
* to add.
|
||||
*/
|
||||
String getRemoteContactLink();
|
||||
String getHandshakeLink() throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the given link is syntactically valid.
|
||||
*/
|
||||
boolean isValidRemoteContactLink(String link);
|
||||
|
||||
/**
|
||||
* Requests a new contact to be added via the given {@code link}.
|
||||
* Creates a {@link PendingContact} from the given handshake link and
|
||||
* alias, adds it to the database and returns it.
|
||||
*
|
||||
* @param link The 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.
|
||||
* @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 addRemoteContactRequest(String link, String alias);
|
||||
PendingContact addPendingContact(String link, String alias)
|
||||
throws DbException, FormatException;
|
||||
|
||||
/**
|
||||
* Returns a list of {@link PendingContact}s.
|
||||
*/
|
||||
Collection<PendingContact> getPendingContacts();
|
||||
Collection<PendingContact> getPendingContacts() throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a {@link PendingContact} that is in state
|
||||
* {@link PendingContactState FAILED}.
|
||||
* Removes a {@link PendingContact}.
|
||||
*/
|
||||
void removePendingContact(PendingContact pendingContact);
|
||||
void removePendingContact(PendingContactId p) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the contact with the given ID.
|
||||
@@ -91,7 +94,7 @@ public interface ContactManager {
|
||||
* Returns the contact with the given remoteAuthorId
|
||||
* 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)
|
||||
throws DbException;
|
||||
@@ -100,7 +103,7 @@ public interface ContactManager {
|
||||
* Returns the contact with the given remoteAuthorId
|
||||
* 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,
|
||||
AuthorId localAuthorId) throws DbException;
|
||||
@@ -156,8 +159,20 @@ public interface ContactManager {
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
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;
|
||||
@@ -9,12 +10,12 @@ import javax.annotation.concurrent.Immutable;
|
||||
public class PendingContact {
|
||||
|
||||
private final PendingContactId id;
|
||||
private final byte[] publicKey;
|
||||
private final PublicKey publicKey;
|
||||
private final String alias;
|
||||
private final PendingContactState state;
|
||||
private final long timestamp;
|
||||
|
||||
public PendingContact(PendingContactId id, byte[] publicKey,
|
||||
public PendingContact(PendingContactId id, PublicKey publicKey,
|
||||
String alias, PendingContactState state, long timestamp) {
|
||||
this.id = id;
|
||||
this.publicKey = publicKey;
|
||||
@@ -27,7 +28,7 @@ public class PendingContact {
|
||||
return id;
|
||||
}
|
||||
|
||||
public byte[] getPublicKey() {
|
||||
public PublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -17,9 +18,8 @@ public class PendingContactId extends UniqueId {
|
||||
super(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return o instanceof PendingContactId && super.equals(o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.event.Event;
|
||||
@@ -8,11 +8,11 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class IntroductionSucceededEvent extends Event {
|
||||
public class ContactAddedRemotelyEvent extends Event {
|
||||
|
||||
private final Contact contact;
|
||||
|
||||
public IntroductionSucceededEvent(Contact contact) {
|
||||
public ContactAddedRemotelyEvent(Contact contact) {
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public interface CryptoComponent {
|
||||
* signature, to prevent it from being repurposed or colliding with a
|
||||
* signature created for another purpose
|
||||
*/
|
||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
@@ -68,7 +68,7 @@ public interface CryptoComponent {
|
||||
* @return true if the signature was valid, false otherwise.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -7,11 +7,21 @@ public interface CryptoConstants {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@ public class KeyPair {
|
||||
private final PrivateKey privateKey;
|
||||
|
||||
public KeyPair(PublicKey publicKey, PrivateKey privateKey) {
|
||||
if (!publicKey.getKeyType().equals(privateKey.getKeyType()))
|
||||
throw new IllegalArgumentException();
|
||||
this.publicKey = publicKey;
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@NotNullByDefault
|
||||
public interface PrivateKey {
|
||||
|
||||
/**
|
||||
* Returns the type of this key pair.
|
||||
*/
|
||||
String getKeyType();
|
||||
|
||||
/**
|
||||
* Returns the encoded representation of this key.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@NotNullByDefault
|
||||
public interface PublicKey {
|
||||
|
||||
/**
|
||||
* Returns the type of this key pair.
|
||||
*/
|
||||
String getKeyType();
|
||||
|
||||
/**
|
||||
* Returns the encoded representation of this key.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.api.crypto;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
/**
|
||||
@@ -11,35 +10,29 @@ import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
public interface TransportCrypto {
|
||||
|
||||
/**
|
||||
* Derives initial transport keys for the given transport in the given
|
||||
* time period from the given root key.
|
||||
* Derives initial rotation mode transport keys for the given transport in
|
||||
* the given time period from the given root key.
|
||||
*
|
||||
* @param alice whether the keys are for use by Alice or Bob.
|
||||
* @param active whether the keys are usable for outgoing streams.
|
||||
* @param alice Whether the keys are for use by Alice or Bob
|
||||
* @param active Whether the keys are usable for outgoing streams
|
||||
*/
|
||||
TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey,
|
||||
TransportKeys deriveRotationKeys(TransportId t, SecretKey rootKey,
|
||||
long timePeriod, boolean alice, boolean active);
|
||||
|
||||
/**
|
||||
* Rotates the given transport keys to the given time period. If the keys
|
||||
* are for the given period or any later period they are not rotated.
|
||||
*/
|
||||
TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod);
|
||||
|
||||
/**
|
||||
* Derives handshake keys for the given transport in the given time period
|
||||
* from the given root key.
|
||||
*
|
||||
* @param alice whether the keys are for use by Alice or Bob.
|
||||
* @param alice Whether the keys are for use by Alice or Bob
|
||||
*/
|
||||
HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
||||
TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
||||
long timePeriod, boolean alice);
|
||||
|
||||
/**
|
||||
* Updates the given handshake keys to the given time period. If the keys
|
||||
* 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.
|
||||
*/
|
||||
HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod);
|
||||
TransportKeys updateTransportKeys(TransportKeys k, long timePeriod);
|
||||
|
||||
/**
|
||||
* Encodes the pseudo-random tag that is used to recognise a stream.
|
||||
|
||||
@@ -4,10 +4,12 @@ import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
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.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
@@ -22,11 +24,8 @@ import org.briarproject.bramble.api.sync.MessageStatus;
|
||||
import org.briarproject.bramble.api.sync.Offer;
|
||||
import org.briarproject.bramble.api.sync.Request;
|
||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -114,23 +113,9 @@ public interface DatabaseComponent {
|
||||
void addGroup(Transaction txn, Group g) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given handshake keys for the given contact and returns a
|
||||
* key set ID.
|
||||
* Stores an identity.
|
||||
*/
|
||||
HandshakeKeySetId addHandshakeKeys(Transaction txn, ContactId c,
|
||||
HandshakeKeys k) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given handshake keys for the given pending contact and
|
||||
* returns a key set ID.
|
||||
*/
|
||||
HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
|
||||
HandshakeKeys k) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a local pseudonym.
|
||||
*/
|
||||
void addLocalAuthor(Transaction txn, LocalAuthor a) throws DbException;
|
||||
void addIdentity(Transaction txn, Identity i) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a local message.
|
||||
@@ -154,7 +139,14 @@ public interface DatabaseComponent {
|
||||
* Stores the given transport keys for the given contact and returns a
|
||||
* key set ID.
|
||||
*/
|
||||
TransportKeySetId 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;
|
||||
|
||||
/**
|
||||
@@ -174,12 +166,12 @@ public interface DatabaseComponent {
|
||||
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)
|
||||
throws DbException;
|
||||
boolean containsIdentity(Transaction txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given pending contact.
|
||||
@@ -274,7 +266,7 @@ public interface DatabaseComponent {
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
|
||||
Collection<ContactId> getContacts(Transaction txn, AuthorId local)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -309,26 +301,18 @@ public interface DatabaseComponent {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all handshake keys for the given transport.
|
||||
* Returns the identity for the local pseudonym with the given ID.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
|
||||
throws DbException;
|
||||
Identity getIdentity(Transaction txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the local pseudonym with the given ID.
|
||||
* Returns the identities for all local pseudonyms.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all local pseudonyms.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
|
||||
Collection<Identity> getIdentities(Transaction txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the message with the given ID.
|
||||
@@ -487,17 +471,11 @@ public interface DatabaseComponent {
|
||||
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Increments the outgoing stream counter for the given handshake keys.
|
||||
*/
|
||||
void incrementStreamCounter(Transaction txn, TransportId t,
|
||||
HandshakeKeySetId k) throws DbException;
|
||||
|
||||
/**
|
||||
* Increments the outgoing stream counter for the given transport keys.
|
||||
*/
|
||||
void incrementStreamCounter(Transaction txn, TransportId t,
|
||||
TransportKeySetId k) throws DbException;
|
||||
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Merges the given metadata with the existing metadata for the given
|
||||
@@ -553,15 +531,9 @@ public interface DatabaseComponent {
|
||||
void removeGroup(Transaction txn, Group g) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes the given handshake keys from the database.
|
||||
* Removes an identity (and all associated state) from the database.
|
||||
*/
|
||||
void removeHandshakeKeys(Transaction txn, TransportId t,
|
||||
HandshakeKeySetId k) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a local pseudonym (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.
|
||||
@@ -582,8 +554,8 @@ public interface DatabaseComponent {
|
||||
/**
|
||||
* Removes the given transport keys from the database.
|
||||
*/
|
||||
void removeTransportKeys(Transaction txn, TransportId t,
|
||||
TransportKeySetId k) throws DbException;
|
||||
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given contact as verified.
|
||||
@@ -620,31 +592,22 @@ public interface DatabaseComponent {
|
||||
Collection<MessageId> dependencies) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the reordering window for the given transport key set in the given
|
||||
* time period.
|
||||
* Sets the handshake key pair for the identity with the given ID.
|
||||
*/
|
||||
void setReorderingWindow(Transaction txn, TransportKeySetId k,
|
||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||
throws DbException;
|
||||
void setHandshakeKeyPair(Transaction txn, AuthorId local,
|
||||
PublicKey publicKey, PrivateKey privateKey) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the reordering window for the given handshake key set in the given
|
||||
* Sets the reordering window for the given transport keys in the given
|
||||
* time period.
|
||||
*/
|
||||
void setReorderingWindow(Transaction txn, HandshakeKeySetId k,
|
||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||
throws DbException;
|
||||
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
|
||||
long timePeriod, long base, byte[] bitmap) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given transport keys as usable for outgoing streams.
|
||||
*/
|
||||
void setTransportKeysActive(Transaction txn, TransportId t,
|
||||
TransportKeySetId k) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given handshake keys, deleting any keys they have replaced.
|
||||
*/
|
||||
void updateHandshakeKeys(Transaction txn, Collection<HandshakeKeySet> keys)
|
||||
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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
|
||||
* not indicate a database error.
|
||||
*/
|
||||
public class NoSuchLocalAuthorException extends DbException {
|
||||
public class NoSuchIdentityException extends DbException {
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.briarproject.bramble.api.identity;
|
||||
|
||||
import org.briarproject.bramble.api.Nameable;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
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_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||
|
||||
/**
|
||||
* A pseudonym for a user.
|
||||
@@ -24,14 +25,14 @@ public class Author implements Nameable {
|
||||
private final AuthorId id;
|
||||
private final int formatVersion;
|
||||
private final String name;
|
||||
private final byte[] publicKey;
|
||||
private final PublicKey publicKey;
|
||||
|
||||
public Author(AuthorId id, int formatVersion, String name,
|
||||
byte[] publicKey) {
|
||||
int nameLength = StringUtils.toUtf8(name).length;
|
||||
PublicKey publicKey) {
|
||||
int nameLength = toUtf8(name).length;
|
||||
if (nameLength == 0 || nameLength > MAX_AUTHOR_NAME_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if (publicKey.length == 0 || publicKey.length > MAX_PUBLIC_KEY_LENGTH)
|
||||
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||
throw new IllegalArgumentException();
|
||||
this.id = id;
|
||||
this.formatVersion = formatVersion;
|
||||
@@ -63,7 +64,7 @@ public class Author implements Nameable {
|
||||
/**
|
||||
* Returns the public key used to verify the pseudonym's signatures.
|
||||
*/
|
||||
public byte[] getPublicKey() {
|
||||
public PublicKey getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.api.identity;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -9,23 +10,16 @@ public interface AuthorFactory {
|
||||
* Creates an author with the current format version and the given name and
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
* name and keys.
|
||||
* name.
|
||||
*/
|
||||
LocalAuthor createLocalAuthor(String name, byte[] publicKey,
|
||||
byte[] privateKey);
|
||||
|
||||
/**
|
||||
* Creates a local author with the given format version, name and keys.
|
||||
*/
|
||||
LocalAuthor createLocalAuthor(int formatVersion, String name,
|
||||
byte[] publicKey, byte[] privateKey);
|
||||
LocalAuthor createLocalAuthor(String name);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,30 @@
|
||||
package org.briarproject.bramble.api.identity;
|
||||
|
||||
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.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
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
|
||||
LocalAuthor createLocalAuthor(String name);
|
||||
Identity createIdentity(String name);
|
||||
|
||||
/**
|
||||
* Registers the given local identity with the manager. The identity is
|
||||
* not stored until {@link #storeLocalAuthor()} is called.
|
||||
* Registers the given identity with the manager. This method should be
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Stores the local identity registered with
|
||||
* {@link #registerLocalAuthor(LocalAuthor)}, if any.
|
||||
*/
|
||||
void storeLocalAuthor() throws DbException;
|
||||
void registerIdentity(Identity i);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
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.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||
|
||||
/**
|
||||
* A pseudonym for the local user.
|
||||
@@ -14,62 +15,20 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
|
||||
@NotNullByDefault
|
||||
public class LocalAuthor extends Author {
|
||||
|
||||
private final byte[] privateKey;
|
||||
@Nullable
|
||||
private final byte[] handshakePublicKey, handshakePrivateKey;
|
||||
private final long created;
|
||||
private final PrivateKey privateKey;
|
||||
|
||||
public LocalAuthor(AuthorId id, int formatVersion, String name,
|
||||
byte[] publicKey, byte[] privateKey, long created) {
|
||||
PublicKey publicKey, PrivateKey privateKey) {
|
||||
super(id, formatVersion, name, publicKey);
|
||||
this.privateKey = privateKey;
|
||||
this.created = created;
|
||||
handshakePublicKey = null;
|
||||
handshakePrivateKey = null;
|
||||
}
|
||||
|
||||
public LocalAuthor(AuthorId id, int formatVersion, String name,
|
||||
byte[] publicKey, byte[] privateKey, byte[] handshakePublicKey,
|
||||
byte[] handshakePrivateKey, long created) {
|
||||
super(id, formatVersion, name, publicKey);
|
||||
if (handshakePublicKey.length == 0 ||
|
||||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH) {
|
||||
if (!privateKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.privateKey = privateKey;
|
||||
this.handshakePublicKey = handshakePublicKey;
|
||||
this.handshakePrivateKey = handshakePrivateKey;
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private key used to generate the pseudonym's signatures.
|
||||
*/
|
||||
public byte[] getPrivateKey() {
|
||||
public PrivateKey getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key used for handshaking, or null if no key exists.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] getHandshakePublicKey() {
|
||||
return handshakePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private key used for handshaking, or null if no key exists.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] getHandshakePrivateKey() {
|
||||
return handshakePrivateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time the pseudonym was created, in milliseconds since the
|
||||
* Unix epoch.
|
||||
*/
|
||||
public long getTimeCreated() {
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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
|
||||
@NotNullByDefault
|
||||
public class LocalAuthorAddedEvent extends Event {
|
||||
public class IdentityAddedEvent extends Event {
|
||||
|
||||
private final AuthorId authorId;
|
||||
|
||||
public LocalAuthorAddedEvent(AuthorId authorId) {
|
||||
public IdentityAddedEvent(AuthorId authorId) {
|
||||
this.authorId = authorId;
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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
|
||||
@NotNullByDefault
|
||||
public class LocalAuthorRemovedEvent extends Event {
|
||||
public class IdentityRemovedEvent extends Event {
|
||||
|
||||
private final AuthorId authorId;
|
||||
|
||||
public LocalAuthorRemovedEvent(AuthorId authorId) {
|
||||
public IdentityRemovedEvent(AuthorId authorId) {
|
||||
this.authorId = authorId;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,16 @@ package org.briarproject.bramble.api.lifecycle;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
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.sync.Client;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* Manages the lifecycle of the app, starting {@link Client Clients}, starting
|
||||
* and stopping {@link Service Services}, shutting down
|
||||
* {@link ExecutorService ExecutorServices}, and opening and closing the
|
||||
* {@link DatabaseComponent}.
|
||||
* Manages the lifecycle of the app: opening and closing the
|
||||
* {@link DatabaseComponent} starting and stopping {@link Service Services},
|
||||
* and shutting down {@link ExecutorService ExecutorServices}.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
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
|
||||
* should be called before {@link #startServices(SecretKey)}.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
* registered {@link Client Clients} and {@link Service Services}.
|
||||
* registered {@link Service Services}.
|
||||
*/
|
||||
StartResult startServices(SecretKey dbKey);
|
||||
|
||||
@@ -80,8 +81,7 @@ public interface LifecycleManager {
|
||||
|
||||
/**
|
||||
* Waits for the {@link DatabaseComponent} to be opened and all registered
|
||||
* {@link Client Clients} and {@link Service Services} to start before
|
||||
* returning.
|
||||
* {@link Service Services} to start before returning.
|
||||
*/
|
||||
void waitForStartup() throws InterruptedException;
|
||||
|
||||
@@ -97,4 +97,13 @@ public interface LifecycleManager {
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,20 @@ import javax.annotation.Nullable;
|
||||
public class NullSafety {
|
||||
|
||||
/**
|
||||
* Stand-in for `Objects.requireNonNull()`.
|
||||
* Stand-in for {@code Objects.requireNonNull()}.
|
||||
*/
|
||||
public static <T> T requireNonNull(@Nullable T t) {
|
||||
if (t == null) throw new NullPointerException();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -11,7 +11,8 @@ public interface IncomingMessageHook {
|
||||
/**
|
||||
* 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.
|
||||
* If this is thrown, delivery will be attempted again at next startup,
|
||||
* whereas if an InvalidMessageException is thrown,
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.briarproject.bramble.api.transport;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Abstract superclass for {@link TransportKeys} and {@link HandshakeKeys}.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class AbstractTransportKeys {
|
||||
|
||||
private final TransportId transportId;
|
||||
private final IncomingKeys inPrev, inCurr, inNext;
|
||||
private final OutgoingKeys outCurr;
|
||||
|
||||
AbstractTransportKeys(TransportId transportId, IncomingKeys inPrev,
|
||||
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
|
||||
if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1)
|
||||
throw new IllegalArgumentException();
|
||||
if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
|
||||
throw new IllegalArgumentException();
|
||||
if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
|
||||
throw new IllegalArgumentException();
|
||||
this.transportId = transportId;
|
||||
this.inPrev = inPrev;
|
||||
this.inCurr = inCurr;
|
||||
this.inNext = inNext;
|
||||
this.outCurr = outCurr;
|
||||
}
|
||||
|
||||
public TransportId getTransportId() {
|
||||
return transportId;
|
||||
}
|
||||
|
||||
public IncomingKeys getPreviousIncomingKeys() {
|
||||
return inPrev;
|
||||
}
|
||||
|
||||
public IncomingKeys getCurrentIncomingKeys() {
|
||||
return inCurr;
|
||||
}
|
||||
|
||||
public IncomingKeys getNextIncomingKeys() {
|
||||
return inNext;
|
||||
}
|
||||
|
||||
public OutgoingKeys getCurrentOutgoingKeys() {
|
||||
return outCurr;
|
||||
}
|
||||
|
||||
public long getTimePeriod() {
|
||||
return outCurr.getTimePeriod();
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package org.briarproject.bramble.api.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* A set of keys for handshaking with a given contact or pending contact over a
|
||||
* given transport. Unlike a {@link TransportKeySet} these keys do not provide
|
||||
* forward secrecy.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class HandshakeKeySet {
|
||||
|
||||
private final HandshakeKeySetId keySetId;
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
@Nullable
|
||||
private final PendingContactId pendingContactId;
|
||||
private final HandshakeKeys keys;
|
||||
|
||||
public HandshakeKeySet(HandshakeKeySetId keySetId, ContactId contactId,
|
||||
HandshakeKeys keys) {
|
||||
this.keySetId = keySetId;
|
||||
this.contactId = contactId;
|
||||
this.keys = keys;
|
||||
pendingContactId = null;
|
||||
}
|
||||
|
||||
public HandshakeKeySet(HandshakeKeySetId keySetId,
|
||||
PendingContactId pendingContactId, HandshakeKeys keys) {
|
||||
this.keySetId = keySetId;
|
||||
this.pendingContactId = pendingContactId;
|
||||
this.keys = keys;
|
||||
contactId = null;
|
||||
}
|
||||
|
||||
public HandshakeKeySetId getKeySetId() {
|
||||
return keySetId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PendingContactId getPendingContactId() {
|
||||
return pendingContactId;
|
||||
}
|
||||
|
||||
public HandshakeKeys getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return keySetId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof HandshakeKeySet &&
|
||||
keySetId.equals(((HandshakeKeySet) o).keySetId);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.briarproject.bramble.api.transport;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Keys for handshaking with a given contact or pending contact over a given
|
||||
* transport. Unlike {@link TransportKeys} these keys do not provide forward
|
||||
* secrecy.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class HandshakeKeys extends AbstractTransportKeys {
|
||||
|
||||
private final SecretKey rootKey;
|
||||
private final boolean alice;
|
||||
|
||||
public HandshakeKeys(TransportId transportId, IncomingKeys inPrev,
|
||||
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr,
|
||||
SecretKey rootKey, boolean alice) {
|
||||
super(transportId, inPrev, inCurr, inNext, outCurr);
|
||||
this.rootKey = rootKey;
|
||||
this.alice = alice;
|
||||
}
|
||||
|
||||
public SecretKey getRootKey() {
|
||||
return rootKey;
|
||||
}
|
||||
|
||||
public boolean isAlice() {
|
||||
return alice;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import javax.annotation.concurrent.Immutable;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||
|
||||
/**
|
||||
* Contains transport keys for receiving streams from a given contact over a
|
||||
* given transport in a given time period.
|
||||
* Contains transport keys for receiving streams from a given contact or
|
||||
* pending contact over a given transport in a given time period.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
@@ -18,23 +19,51 @@ public interface KeyManager {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* each transport and returns the key set IDs.
|
||||
* stores a set of rotation 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
|
||||
* @param active whether the derived keys can be used for outgoing streams
|
||||
* @param alice True if the local party is Alice
|
||||
* @param active Whether the derived keys can be used for outgoing streams
|
||||
*/
|
||||
Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||
Map<TransportId, KeySetId> addContactWithRotationKeys(Transaction txn,
|
||||
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;
|
||||
|
||||
/**
|
||||
* Marks the given transport keys as usable for outgoing streams.
|
||||
*/
|
||||
void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys)
|
||||
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -43,15 +72,28 @@ public interface KeyManager {
|
||||
*/
|
||||
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
|
||||
* contact over the given transport, or null if an error occurs or the
|
||||
* contact does not support the transport.
|
||||
* contact over the given transport, or null if an error occurs.
|
||||
*/
|
||||
@Nullable
|
||||
StreamContext getStreamContext(ContactId c, TransportId t)
|
||||
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
|
||||
* from the corresponding stream, or null if an error occurs or the tag was
|
||||
|
||||
@@ -5,17 +5,16 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Type-safe wrapper for an integer that uniquely identifies a
|
||||
* {@link HandshakeKeySet set of handshake keys} within the scope of the local
|
||||
* device.
|
||||
* Type-safe wrapper for an integer that uniquely identifies a set of
|
||||
* {@link TransportKeySet transport keys} within the scope of the local device.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class HandshakeKeySetId {
|
||||
public class KeySetId {
|
||||
|
||||
private final int id;
|
||||
|
||||
public HandshakeKeySetId(int id) {
|
||||
public KeySetId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@@ -30,7 +29,6 @@ public class HandshakeKeySetId {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof HandshakeKeySetId &&
|
||||
id == ((HandshakeKeySetId) o).id;
|
||||
return o instanceof KeySetId && id == ((KeySetId) o).id;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ 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
|
||||
* transport in a given time period.
|
||||
* Contains transport keys for sending streams to a given contact or pending
|
||||
* contact over a given transport in a given time period.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
|
||||
@@ -1,34 +1,53 @@
|
||||
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.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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 {
|
||||
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
@Nullable
|
||||
private final PendingContactId pendingContactId;
|
||||
private final TransportId transportId;
|
||||
private final SecretKey tagKey, headerKey;
|
||||
private final long streamNumber;
|
||||
private final boolean handshakeMode;
|
||||
|
||||
public StreamContext(ContactId contactId, TransportId transportId,
|
||||
SecretKey tagKey, SecretKey headerKey, long streamNumber) {
|
||||
public StreamContext(@Nullable ContactId contactId,
|
||||
@Nullable PendingContactId pendingContactId,
|
||||
TransportId transportId, SecretKey tagKey, SecretKey headerKey,
|
||||
long streamNumber, boolean handshakeMode) {
|
||||
requireExactlyOneNull(contactId, pendingContactId);
|
||||
this.contactId = contactId;
|
||||
this.pendingContactId = pendingContactId;
|
||||
this.transportId = transportId;
|
||||
this.tagKey = tagKey;
|
||||
this.headerKey = headerKey;
|
||||
this.streamNumber = streamNumber;
|
||||
this.handshakeMode = handshakeMode;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PendingContactId getPendingContactId() {
|
||||
return pendingContactId;
|
||||
}
|
||||
|
||||
public TransportId getTransportId() {
|
||||
return transportId;
|
||||
}
|
||||
@@ -44,4 +63,8 @@ public class StreamContext {
|
||||
public long getStreamNumber() {
|
||||
return streamNumber;
|
||||
}
|
||||
|
||||
public boolean isHandshakeMode() {
|
||||
return handshakeMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,52 @@
|
||||
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 over a given transport.
|
||||
* Unlike a {@link HandshakeKeySet} these keys provide forward secrecy.
|
||||
* A set of keys for communicating with a given contact or pending contact
|
||||
* over a given transport.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class TransportKeySet {
|
||||
|
||||
private final TransportKeySetId keySetId;
|
||||
private final KeySetId keySetId;
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
@Nullable
|
||||
private final PendingContactId pendingContactId;
|
||||
private final TransportKeys keys;
|
||||
|
||||
public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
|
||||
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 TransportKeySetId getKeySetId() {
|
||||
public KeySetId getKeySetId() {
|
||||
return keySetId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PendingContactId getPendingContactId() {
|
||||
return pendingContactId;
|
||||
}
|
||||
|
||||
public TransportKeys getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.briarproject.bramble.api.transport;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Type-safe wrapper for an integer that uniquely identifies a
|
||||
* {@link TransportKeySet set of transport keys} within the scope of the local
|
||||
* device.
|
||||
* <p/>
|
||||
* Key sets created on a given device must have increasing identifiers.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class TransportKeySetId {
|
||||
|
||||
private final int id;
|
||||
|
||||
public TransportKeySetId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof TransportKeySetId &&
|
||||
id == ((TransportKeySetId) o).id;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,108 @@
|
||||
package org.briarproject.bramble.api.transport;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Keys for communicating with a given contact over a given transport. Unlike
|
||||
* {@link HandshakeKeys} these keys provide forward secrecy.
|
||||
* Keys for communicating with a given contact or pending contact over a given
|
||||
* transport.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class TransportKeys extends AbstractTransportKeys {
|
||||
public class TransportKeys {
|
||||
|
||||
private final TransportId transportId;
|
||||
private final IncomingKeys inPrev, inCurr, inNext;
|
||||
private final OutgoingKeys outCurr;
|
||||
@Nullable
|
||||
private final SecretKey rootKey;
|
||||
private final boolean alice;
|
||||
|
||||
/**
|
||||
* Constructor for rotation mode.
|
||||
*/
|
||||
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
|
||||
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
|
||||
super(transportId, inPrev, inCurr, inNext, outCurr);
|
||||
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();
|
||||
if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
|
||||
throw new IllegalArgumentException();
|
||||
if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
|
||||
throw new IllegalArgumentException();
|
||||
this.transportId = transportId;
|
||||
this.inPrev = inPrev;
|
||||
this.inCurr = inCurr;
|
||||
this.inNext = inNext;
|
||||
this.outCurr = outCurr;
|
||||
this.rootKey = rootKey;
|
||||
this.alice = alice;
|
||||
}
|
||||
|
||||
public TransportId getTransportId() {
|
||||
return transportId;
|
||||
}
|
||||
|
||||
public IncomingKeys getPreviousIncomingKeys() {
|
||||
return inPrev;
|
||||
}
|
||||
|
||||
public IncomingKeys getCurrentIncomingKeys() {
|
||||
return inCurr;
|
||||
}
|
||||
|
||||
public IncomingKeys getNextIncomingKeys() {
|
||||
return inNext;
|
||||
}
|
||||
|
||||
public OutgoingKeys getCurrentOutgoingKeys() {
|
||||
return outCurr;
|
||||
}
|
||||
|
||||
public long getTimePeriod() {
|
||||
return outCurr.getTimePeriod();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,14 @@ public interface ClientVersioningManager {
|
||||
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,
|
||||
Visibility v) throws DbException;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -19,6 +20,7 @@ public class LogUtils {
|
||||
|
||||
/**
|
||||
* Logs the duration of a task.
|
||||
*
|
||||
* @param logger the logger to use
|
||||
* @param task a description of the task
|
||||
* @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) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,4 +153,13 @@ public class StringUtils {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,17 @@ 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.SignaturePrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
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.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
@@ -29,10 +37,10 @@ import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||
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.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.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
|
||||
@@ -99,27 +107,46 @@ public class TestUtils {
|
||||
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
|
||||
}
|
||||
|
||||
public static LocalAuthor getLocalAuthor() {
|
||||
return getLocalAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
|
||||
public static PublicKey getSignaturePublicKey() {
|
||||
byte[] key = getRandomBytes(MAX_SIGNATURE_PUBLIC_KEY_BYTES);
|
||||
return new SignaturePublicKey(key);
|
||||
}
|
||||
|
||||
public static LocalAuthor getLocalAuthor(int nameLength) {
|
||||
AuthorId id = new AuthorId(getRandomId());
|
||||
String name = getRandomString(nameLength);
|
||||
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey,
|
||||
public static PrivateKey getSignaturePrivateKey() {
|
||||
return new SignaturePrivateKey(getRandomBytes(123));
|
||||
}
|
||||
|
||||
public static PublicKey getAgreementPublicKey() {
|
||||
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);
|
||||
}
|
||||
|
||||
public static Author getAuthor() {
|
||||
return getAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
|
||||
public static LocalAuthor getLocalAuthor() {
|
||||
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());
|
||||
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
|
||||
String name = getRandomString(nameLength);
|
||||
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
PublicKey publicKey = getSignaturePublicKey();
|
||||
return new Author(id, FORMAT_VERSION, name, publicKey);
|
||||
}
|
||||
|
||||
@@ -152,10 +179,12 @@ public class TestUtils {
|
||||
|
||||
public static PendingContact getPendingContact(int nameLength) {
|
||||
PendingContactId id = new PendingContactId(getRandomId());
|
||||
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
PublicKey publicKey = getAgreementPublicKey();
|
||||
String alias = getRandomString(nameLength);
|
||||
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
|
||||
timestamp);
|
||||
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() {
|
||||
@@ -176,7 +205,7 @@ public class TestUtils {
|
||||
boolean verified) {
|
||||
return new Contact(c, remote, local,
|
||||
getRandomString(MAX_AUTHOR_NAME_LENGTH),
|
||||
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
|
||||
getAgreementPublicKey(), verified);
|
||||
}
|
||||
|
||||
public static double getMedian(Collection<? extends Number> samples) {
|
||||
|
||||
@@ -2,7 +2,7 @@ dependencyVerification {
|
||||
verify = [
|
||||
'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.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',
|
||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
|
||||
@@ -17,7 +17,7 @@ dependencies {
|
||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||
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 '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-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'
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
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.LocalAuthor;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.util.IoUtils;
|
||||
@@ -161,8 +161,8 @@ class AccountManagerImpl implements AccountManager {
|
||||
synchronized (stateChangeLock) {
|
||||
if (hasDatabaseKey())
|
||||
throw new AssertionError("Already have a database key");
|
||||
LocalAuthor localAuthor = identityManager.createLocalAuthor(name);
|
||||
identityManager.registerLocalAuthor(localAuthor);
|
||||
Identity identity = identityManager.createIdentity(name);
|
||||
identityManager.registerIdentity(identity);
|
||||
SecretKey key = crypto.generateSecretKey();
|
||||
if (!encryptAndStoreDatabaseKey(key, password)) return false;
|
||||
databaseKey = key;
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.briarproject.bramble.client;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
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.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
@@ -305,14 +308,15 @@ class ClientHelperImpl implements ClientHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(String label, BdfList toSign, byte[] privateKey)
|
||||
public byte[] sign(String label, BdfList toSign, PrivateKey privateKey)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
return crypto.sign(label, toByteArray(toSign), privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
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),
|
||||
publicKey)) {
|
||||
throw new GeneralSecurityException("Invalid signature");
|
||||
@@ -327,11 +331,29 @@ class ClientHelperImpl implements ClientHelper {
|
||||
if (formatVersion != FORMAT_VERSION) throw new FormatException();
|
||||
String name = author.getString(1);
|
||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] publicKey = author.getRaw(2);
|
||||
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
|
||||
byte[] publicKeyBytes = author.getRaw(2);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey parseAndValidateAgreementPublicKey(byte[] publicKeyBytes)
|
||||
throws FormatException {
|
||||
KeyParser parser = crypto.getAgreementKeyParser();
|
||||
try {
|
||||
return parser.parsePublicKey(publicKeyBytes);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportProperties parseAndValidateTransportProperties(
|
||||
BdfDictionary properties) throws FormatException {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
@@ -20,45 +21,42 @@ import org.briarproject.bramble.api.transport.KeyManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||
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_PUBLIC_KEY_LENGTH;
|
||||
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.UNVERIFIED;
|
||||
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;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class ContactManagerImpl implements ContactManager {
|
||||
|
||||
private static final int LINK_LENGTH = 64;
|
||||
private static final String REMOTE_CONTACT_LINK =
|
||||
"briar://" + getRandomBase32String(LINK_LENGTH);
|
||||
private static final Pattern LINK_REGEX =
|
||||
Pattern.compile("(briar://)?([a-z2-7]{" + LINK_LENGTH + "})");
|
||||
"briar://" + getRandomBase32String(BASE32_LINK_BYTES);
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final KeyManager keyManager;
|
||||
private final IdentityManager identityManager;
|
||||
private final PendingContactFactory pendingContactFactory;
|
||||
private final List<ContactHook> hooks;
|
||||
|
||||
@Inject
|
||||
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
|
||||
IdentityManager identityManager) {
|
||||
IdentityManager identityManager,
|
||||
PendingContactFactory pendingContactFactory) {
|
||||
this.db = db;
|
||||
this.keyManager = keyManager;
|
||||
this.identityManager = identityManager;
|
||||
this.pendingContactFactory = pendingContactFactory;
|
||||
hooks = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
@@ -72,7 +70,8 @@ class ContactManagerImpl implements ContactManager {
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
|
||||
boolean active) throws DbException {
|
||||
ContactId c = db.addContact(txn, remote, local, verified);
|
||||
keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
|
||||
keyManager.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||
alice, active);
|
||||
Contact contact = db.getContact(txn, c);
|
||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||
return c;
|
||||
@@ -97,45 +96,28 @@ class ContactManagerImpl implements ContactManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemoteContactLink() {
|
||||
public String getHandshakeLink() {
|
||||
// TODO replace with real implementation
|
||||
return REMOTE_CONTACT_LINK;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static String getRandomBase32String(int length) {
|
||||
Random random = new Random();
|
||||
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);
|
||||
@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 boolean isValidRemoteContactLink(String link) {
|
||||
return LINK_REGEX.matcher(link).matches();
|
||||
public Collection<PendingContact> getPendingContacts() throws DbException {
|
||||
return db.transactionWithResult(true, db::getPendingContacts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingContact addRemoteContactRequest(String link, String alias) {
|
||||
// TODO replace with real implementation
|
||||
PendingContactId id = new PendingContactId(link.getBytes());
|
||||
return new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH], alias,
|
||||
WAITING_FOR_CONNECTION, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PendingContact> getPendingContacts() {
|
||||
// TODO replace with real implementation
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePendingContact(PendingContact pendingContact) {
|
||||
// TODO replace with real implementation
|
||||
public void removePendingContact(PendingContactId p) throws DbException {
|
||||
db.transaction(false, txn -> db.removePendingContact(txn, p));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,4 +28,10 @@ public class ContactModule {
|
||||
ContactExchangeTaskImpl contactExchangeTask) {
|
||||
return contactExchangeTask;
|
||||
}
|
||||
|
||||
@Provides
|
||||
PendingContactFactory providePendingContactFactory(
|
||||
PendingContactFactoryImpl pendingContactFactory) {
|
||||
return pendingContactFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
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.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
@@ -7,21 +9,24 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class Curve25519KeyParser implements KeyParser {
|
||||
class AgreementKeyParser implements KeyParser {
|
||||
|
||||
@Override
|
||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||
throws GeneralSecurityException {
|
||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||
return new Curve25519PublicKey(encodedKey);
|
||||
return new AgreementPublicKey(encodedKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||
throws GeneralSecurityException {
|
||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||
return new Curve25519PrivateKey(clamp(encodedKey));
|
||||
return new AgreementPrivateKey(clamp(encodedKey));
|
||||
}
|
||||
|
||||
static byte[] clamp(byte[] b) {
|
||||
@@ -4,12 +4,16 @@ import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
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.KeyPair;
|
||||
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.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.system.SecureRandomProvider;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
@@ -31,6 +35,8 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
@@ -80,8 +86,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
signatureKeyPairGenerator = new KeyPairGenerator();
|
||||
signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS,
|
||||
secureRandom);
|
||||
agreementKeyParser = new Curve25519KeyParser();
|
||||
signatureKeyParser = new EdKeyParser();
|
||||
agreementKeyParser = new AgreementKeyParser();
|
||||
signatureKeyParser = new SignatureKeyParser();
|
||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||
}
|
||||
|
||||
@@ -125,9 +131,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
// Package access for testing
|
||||
byte[] performRawKeyAgreement(PrivateKey priv, PublicKey pub)
|
||||
throws GeneralSecurityException {
|
||||
if (!(priv instanceof Curve25519PrivateKey))
|
||||
if (!priv.getKeyType().equals(KEY_TYPE_AGREEMENT))
|
||||
throw new IllegalArgumentException();
|
||||
if (!(pub instanceof Curve25519PublicKey))
|
||||
if (!pub.getKeyType().equals(KEY_TYPE_AGREEMENT))
|
||||
throw new IllegalArgumentException();
|
||||
long start = now();
|
||||
byte[] secret = curve25519.calculateAgreement(pub.getEncoded(),
|
||||
@@ -143,8 +149,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
@Override
|
||||
public KeyPair generateAgreementKeyPair() {
|
||||
Curve25519KeyPair keyPair = curve25519.generateKeyPair();
|
||||
PublicKey pub = new Curve25519PublicKey(keyPair.getPublicKey());
|
||||
PrivateKey priv = new Curve25519PrivateKey(keyPair.getPrivateKey());
|
||||
PublicKey pub = new AgreementPublicKey(keyPair.getPublicKey());
|
||||
PrivateKey priv = new AgreementPrivateKey(keyPair.getPrivateKey());
|
||||
return new KeyPair(pub, priv);
|
||||
}
|
||||
|
||||
@@ -158,9 +164,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
java.security.KeyPair keyPair =
|
||||
signatureKeyPairGenerator.generateKeyPair();
|
||||
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
||||
PublicKey publicKey = new SignaturePublicKey(edPublicKey.getAbyte());
|
||||
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
||||
PrivateKey privateKey = new SignaturePrivateKey(edPrivateKey.getSeed());
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
@@ -195,21 +201,22 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||
throws GeneralSecurityException {
|
||||
PrivateKey key = signatureKeyParser.parsePrivateKey(privateKey);
|
||||
Signature sig = new EdSignature();
|
||||
sig.initSign(key);
|
||||
sig.initSign(privateKey);
|
||||
updateSignature(sig, label, toSign);
|
||||
return sig.sign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifySignature(byte[] signature, String label,
|
||||
byte[] signed, byte[] publicKey) throws GeneralSecurityException {
|
||||
PublicKey key = signatureKeyParser.parsePublicKey(publicKey);
|
||||
byte[] signed, PublicKey publicKey)
|
||||
throws GeneralSecurityException {
|
||||
if (!publicKey.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||
throw new IllegalArgumentException();
|
||||
Signature sig = new EdSignature();
|
||||
sig.initVerify(key);
|
||||
sig.initVerify(publicKey);
|
||||
updateSignature(sig, label, signed);
|
||||
return sig.verify(signature);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
|
||||
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||
|
||||
@NotNullByDefault
|
||||
class EdSignature implements Signature {
|
||||
@@ -39,7 +40,7 @@ class EdSignature implements Signature {
|
||||
|
||||
@Override
|
||||
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
||||
if (!(k instanceof EdPrivateKey))
|
||||
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||
throw new IllegalArgumentException();
|
||||
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
|
||||
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||
@@ -48,7 +49,7 @@ class EdSignature implements Signature {
|
||||
|
||||
@Override
|
||||
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
||||
if (!(k instanceof EdPublicKey))
|
||||
if (!k.getKeyType().equals(KEY_TYPE_SIGNATURE))
|
||||
throw new IllegalArgumentException();
|
||||
EdDSAPublicKey publicKey = new EdDSAPublicKey(
|
||||
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||
|
||||
@@ -49,6 +49,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class MessageEncrypter {
|
||||
|
||||
private static final String KEY_TYPE = "SEC1_brainpoolp512r1";
|
||||
private static final ECDomainParameters PARAMETERS;
|
||||
private static final int MESSAGE_KEY_BITS = 512;
|
||||
private static final int MAC_KEY_BITS = 256;
|
||||
@@ -69,7 +70,7 @@ public class MessageEncrypter {
|
||||
MessageEncrypter(SecureRandom random) {
|
||||
generator = new ECKeyPairGenerator();
|
||||
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();
|
||||
ephemeralGenerator = new EphemeralKeyPairGenerator(generator, encoder);
|
||||
ephemeralParser = new PublicKeyParser(PARAMETERS);
|
||||
@@ -80,11 +81,11 @@ public class MessageEncrypter {
|
||||
// Return a wrapper that uses the SEC 1 encoding
|
||||
ECPublicKeyParameters ecPublicKey =
|
||||
(ECPublicKeyParameters) keyPair.getPublic();
|
||||
PublicKey publicKey = new Sec1PublicKey(ecPublicKey);
|
||||
PublicKey publicKey = new Sec1PublicKey(KEY_TYPE, ecPublicKey);
|
||||
ECPrivateKeyParameters ecPrivateKey =
|
||||
(ECPrivateKeyParameters) keyPair.getPrivate();
|
||||
PrivateKey privateKey =
|
||||
new Sec1PrivateKey(ecPrivateKey, MESSAGE_KEY_BITS);
|
||||
new Sec1PrivateKey(KEY_TYPE, ecPrivateKey, MESSAGE_KEY_BITS);
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,13 @@ class Sec1KeyParser implements KeyParser {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(Sec1KeyParser.class.getName());
|
||||
|
||||
private final String keyType;
|
||||
private final ECDomainParameters params;
|
||||
private final BigInteger modulus;
|
||||
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.keyBits = keyBits;
|
||||
modulus = ((ECCurve.Fp) params.getCurve()).getQ();
|
||||
@@ -80,7 +82,7 @@ class Sec1KeyParser implements KeyParser {
|
||||
throw new GeneralSecurityException();
|
||||
// Construct a public key from the point (x, y) and the params
|
||||
ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params);
|
||||
PublicKey p = new Sec1PublicKey(k);
|
||||
PublicKey p = new Sec1PublicKey(keyType, k);
|
||||
logDuration(LOG, "Parsing public key", start);
|
||||
return p;
|
||||
}
|
||||
@@ -97,7 +99,7 @@ class Sec1KeyParser implements KeyParser {
|
||||
throw new GeneralSecurityException();
|
||||
// Construct a private key from the private value and the 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);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -10,14 +10,21 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
class Sec1PrivateKey implements PrivateKey {
|
||||
|
||||
private final String keyType;
|
||||
private final ECPrivateKeyParameters key;
|
||||
private final int bytesPerInt;
|
||||
|
||||
Sec1PrivateKey(ECPrivateKeyParameters key, int keyBits) {
|
||||
Sec1PrivateKey(String keyType, ECPrivateKeyParameters key, int keyBits) {
|
||||
this.keyType = keyType;
|
||||
this.key = key;
|
||||
bytesPerInt = (keyBits + 7) / 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
byte[] encodedKey = new byte[bytesPerInt];
|
||||
|
||||
@@ -15,12 +15,19 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
class Sec1PublicKey implements PublicKey {
|
||||
|
||||
private final String keyType;
|
||||
private final ECPublicKeyParameters key;
|
||||
|
||||
Sec1PublicKey(ECPublicKeyParameters key) {
|
||||
Sec1PublicKey(String keyType, ECPublicKeyParameters key) {
|
||||
this.keyType = keyType;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return key.getQ().getEncoded(false);
|
||||
|
||||
@@ -3,24 +3,29 @@ package org.briarproject.bramble.crypto;
|
||||
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.crypto.SignaturePrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class EdKeyParser implements KeyParser {
|
||||
class SignatureKeyParser implements KeyParser {
|
||||
|
||||
@Override
|
||||
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||
throws GeneralSecurityException {
|
||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||
return new EdPublicKey(encodedKey);
|
||||
return new SignaturePublicKey(encodedKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||
throws GeneralSecurityException {
|
||||
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||
return new EdPrivateKey(encodedKey);
|
||||
return new SignaturePrivateKey(encodedKey);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
@@ -42,7 +41,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys deriveTransportKeys(TransportId t,
|
||||
public TransportKeys deriveRotationKeys(TransportId t,
|
||||
SecretKey rootKey, long timePeriod, boolean weAreAlice,
|
||||
boolean active) {
|
||||
// Keys for the previous period are derived from the root key
|
||||
@@ -70,31 +69,6 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
|
||||
if (k.getTimePeriod() >= timePeriod) return k;
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
long startPeriod = outCurr.getTimePeriod();
|
||||
boolean active = outCurr.isActive();
|
||||
// Rotate the keys
|
||||
for (long p = startPeriod + 1; p <= timePeriod; p++) {
|
||||
inPrev = inCurr;
|
||||
inCurr = inNext;
|
||||
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
||||
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
|
||||
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
||||
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
||||
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
||||
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
|
||||
}
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||
outCurr);
|
||||
}
|
||||
|
||||
private SecretKey rotateKey(SecretKey k, long timePeriod) {
|
||||
byte[] period = new byte[INT_64_BYTES];
|
||||
writeUint64(timePeriod, period, 0);
|
||||
@@ -117,7 +91,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
||||
public TransportKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
|
||||
long timePeriod, boolean weAreAlice) {
|
||||
if (timePeriod < 1) throw new IllegalArgumentException();
|
||||
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
|
||||
@@ -128,7 +102,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
weAreAlice, timePeriod + 1);
|
||||
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
||||
weAreAlice, timePeriod);
|
||||
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
|
||||
weAreAlice);
|
||||
}
|
||||
|
||||
@@ -171,7 +145,13 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
|
||||
public TransportKeys updateTransportKeys(TransportKeys k, long timePeriod) {
|
||||
if (k.isHandshakeMode()) return updateHandshakeKeys(k, timePeriod);
|
||||
else return updateRotationKeys(k, timePeriod);
|
||||
}
|
||||
|
||||
private TransportKeys updateHandshakeKeys(TransportKeys k,
|
||||
long timePeriod) {
|
||||
long elapsed = timePeriod - k.getTimePeriod();
|
||||
TransportId t = k.getTransportId();
|
||||
SecretKey rootKey = k.getRootKey();
|
||||
@@ -188,7 +168,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
weAreAlice, timePeriod + 1);
|
||||
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
||||
weAreAlice, timePeriod);
|
||||
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr,
|
||||
rootKey, weAreAlice);
|
||||
} else if (elapsed == 2) {
|
||||
// The keys are two periods old - shift by two periods, keeping
|
||||
@@ -200,7 +180,7 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
weAreAlice, timePeriod + 1);
|
||||
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
|
||||
weAreAlice, timePeriod);
|
||||
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
|
||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr,
|
||||
rootKey, weAreAlice);
|
||||
} else {
|
||||
// The keys are more than two periods old - derive fresh keys
|
||||
@@ -208,6 +188,30 @@ class TransportCryptoImpl implements TransportCrypto {
|
||||
}
|
||||
}
|
||||
|
||||
private TransportKeys updateRotationKeys(TransportKeys k, long timePeriod) {
|
||||
if (k.getTimePeriod() >= timePeriod) return k;
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
long startPeriod = outCurr.getTimePeriod();
|
||||
boolean active = outCurr.isActive();
|
||||
// Rotate the keys
|
||||
for (long p = startPeriod + 1; p <= timePeriod; p++) {
|
||||
inPrev = inCurr;
|
||||
inCurr = inNext;
|
||||
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
||||
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
|
||||
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
|
||||
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
|
||||
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
|
||||
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p, active);
|
||||
}
|
||||
// Collect and return the keys
|
||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||
outCurr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||
long streamNumber) {
|
||||
|
||||
@@ -5,6 +5,8 @@ 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.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
@@ -15,7 +17,7 @@ import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
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.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
@@ -27,11 +29,8 @@ import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -106,23 +105,9 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given handshake keys for the given contact and returns a
|
||||
* key set ID.
|
||||
* Stores an identity.
|
||||
*/
|
||||
HandshakeKeySetId addHandshakeKeys(T txn, ContactId c, HandshakeKeys k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given handshake keys for the given pending contact and
|
||||
* returns a key set ID.
|
||||
*/
|
||||
HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p,
|
||||
HandshakeKeys k) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a local pseudonym.
|
||||
*/
|
||||
void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
|
||||
void addIdentity(T txn, Identity i) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a message.
|
||||
@@ -160,7 +145,14 @@ interface Database<T> {
|
||||
* Stores the given transport keys for the given contact and returns a
|
||||
* key set ID.
|
||||
*/
|
||||
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
|
||||
KeySetId addTransportKeys(T 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(T txn, PendingContactId p, TransportKeys k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -187,11 +179,12 @@ interface Database<T> {
|
||||
boolean containsGroup(T txn, GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given local pseudonym.
|
||||
* Returns true if the database contains an identity for the given
|
||||
* pseudonym.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
|
||||
boolean containsIdentity(T txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given message.
|
||||
@@ -272,7 +265,7 @@ interface Database<T> {
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
|
||||
Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the group with the given ID.
|
||||
@@ -315,26 +308,18 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all handshake keys for the given transport.
|
||||
* Returns the identity for local pseudonym with the given ID.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
|
||||
throws DbException;
|
||||
Identity getIdentity(T txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the local pseudonym with the given ID.
|
||||
* Returns the identities for all local pseudonyms.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all local pseudonyms.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
|
||||
Collection<Identity> getIdentities(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the message with the given ID.
|
||||
@@ -544,16 +529,10 @@ interface Database<T> {
|
||||
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Increments the outgoing stream counter for the given handshake keys.
|
||||
*/
|
||||
void incrementStreamCounter(T txn, TransportId t, HandshakeKeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Increments the outgoing stream counter for the given transport keys.
|
||||
*/
|
||||
void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
|
||||
void incrementStreamCounter(T txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -623,15 +602,9 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Removes the given handshake keys from the database.
|
||||
* Removes an identity (and all associated state) from the database.
|
||||
*/
|
||||
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a local pseudonym (and all associated state) from the database.
|
||||
*/
|
||||
void removeLocalAuthor(T txn, AuthorId a) throws DbException;
|
||||
void removeIdentity(T txn, AuthorId a) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a message (and all associated state) from the database.
|
||||
@@ -658,8 +631,7 @@ interface Database<T> {
|
||||
/**
|
||||
* Removes the given transport keys from the database.
|
||||
*/
|
||||
void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
|
||||
throws DbException;
|
||||
void removeTransportKeys(T txn, TransportId t, KeySetId k) throws DbException;
|
||||
|
||||
/**
|
||||
* Resets the transmission count and expiry time of the given message with
|
||||
@@ -685,6 +657,12 @@ interface Database<T> {
|
||||
void setGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the handshake key pair for the identity with the given ID.
|
||||
*/
|
||||
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey,
|
||||
PrivateKey privateKey) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as shared.
|
||||
*/
|
||||
@@ -703,23 +681,16 @@ interface Database<T> {
|
||||
PendingContactState state) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the reordering window for the given transport key set in the given
|
||||
* Sets the reordering window for the given transport keys in the given
|
||||
* time period.
|
||||
*/
|
||||
void setReorderingWindow(T txn, TransportKeySetId k, TransportId t,
|
||||
long timePeriod, long base, byte[] bitmap) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the reordering window for the given handshake key set in the given
|
||||
* time period.
|
||||
*/
|
||||
void setReorderingWindow(T txn, HandshakeKeySetId k, TransportId t,
|
||||
void setReorderingWindow(T txn, KeySetId k, TransportId t,
|
||||
long timePeriod, long base, byte[] bitmap) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given transport keys as usable for outgoing streams.
|
||||
*/
|
||||
void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
|
||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
@@ -731,12 +702,7 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Updates the given handshake keys.
|
||||
*/
|
||||
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
|
||||
|
||||
/**
|
||||
* Updates the given transport keys following key rotation.
|
||||
* Stores the given transport keys, deleting any keys they have replaced.
|
||||
*/
|
||||
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||
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.db.CommitAction;
|
||||
import org.briarproject.bramble.api.db.CommitAction.Visitor;
|
||||
@@ -20,7 +24,7 @@ import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
||||
import org.briarproject.bramble.api.db.NoSuchIdentityException;
|
||||
import org.briarproject.bramble.api.db.NoSuchMessageException;
|
||||
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
|
||||
import org.briarproject.bramble.api.db.NoSuchTransportException;
|
||||
@@ -32,9 +36,9 @@ import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.identity.event.LocalAuthorAddedEvent;
|
||||
import org.briarproject.bramble.api.identity.event.LocalAuthorRemovedEvent;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.identity.event.IdentityAddedEvent;
|
||||
import org.briarproject.bramble.api.identity.event.IdentityRemovedEvent;
|
||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -62,11 +66,8 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -237,9 +238,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsLocalAuthor(txn, local))
|
||||
throw new NoSuchLocalAuthorException();
|
||||
if (db.containsLocalAuthor(txn, remote.getId()))
|
||||
if (!db.containsIdentity(txn, local))
|
||||
throw new NoSuchIdentityException();
|
||||
if (db.containsIdentity(txn, remote.getId()))
|
||||
throw new ContactExistsException();
|
||||
if (db.containsContact(txn, remote.getId(), local))
|
||||
throw new ContactExistsException();
|
||||
@@ -259,37 +260,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
|
||||
ContactId c, HandshakeKeys k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
if (!db.containsTransport(txn, k.getTransportId()))
|
||||
throw new NoSuchTransportException();
|
||||
return db.addHandshakeKeys(txn, c, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
|
||||
PendingContactId p, HandshakeKeys k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsPendingContact(txn, p))
|
||||
throw new NoSuchPendingContactException();
|
||||
if (!db.containsTransport(txn, k.getTransportId()))
|
||||
throw new NoSuchTransportException();
|
||||
return db.addHandshakeKeys(txn, p, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAuthor(Transaction transaction, LocalAuthor a)
|
||||
public void addIdentity(Transaction transaction, Identity i)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsLocalAuthor(txn, a.getId())) {
|
||||
db.addLocalAuthor(txn, a);
|
||||
transaction.attach(new LocalAuthorAddedEvent(a.getId()));
|
||||
if (!db.containsIdentity(txn, i.getId())) {
|
||||
db.addIdentity(txn, i);
|
||||
transaction.attach(new IdentityAddedEvent(i.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +295,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
if (db.containsPendingContact(txn, p.getId()))
|
||||
throw new PendingContactExistsException();
|
||||
db.addPendingContact(txn, p);
|
||||
transaction.attach(new PendingContactStateChangedEvent(p.getId(),
|
||||
p.getState()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -330,8 +309,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeySetId addTransportKeys(Transaction transaction,
|
||||
ContactId c, TransportKeys k) throws DbException {
|
||||
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
|
||||
TransportKeys k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
@@ -341,12 +320,24 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.addTransportKeys(txn, c, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addTransportKeys(Transaction transaction,
|
||||
PendingContactId p, TransportKeys k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsPendingContact(txn, p))
|
||||
throw new NoSuchPendingContactException();
|
||||
if (!db.containsTransport(txn, k.getTransportId()))
|
||||
throw new NoSuchTransportException();
|
||||
return db.addTransportKeys(txn, p, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsContact(Transaction transaction, AuthorId remote,
|
||||
AuthorId local) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsLocalAuthor(txn, local))
|
||||
throw new NoSuchLocalAuthorException();
|
||||
if (!db.containsIdentity(txn, local))
|
||||
throw new NoSuchIdentityException();
|
||||
return db.containsContact(txn, remote, local);
|
||||
}
|
||||
|
||||
@@ -358,10 +349,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsLocalAuthor(Transaction transaction, AuthorId local)
|
||||
public boolean containsIdentity(Transaction transaction, AuthorId a)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
return db.containsLocalAuthor(txn, local);
|
||||
return db.containsIdentity(txn, a);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -503,11 +494,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public Collection<ContactId> getContacts(Transaction transaction,
|
||||
AuthorId a) throws DbException {
|
||||
AuthorId local) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsLocalAuthor(txn, a))
|
||||
throw new NoSuchLocalAuthorException();
|
||||
return db.getContacts(txn, a);
|
||||
if (!db.containsIdentity(txn, local))
|
||||
throw new NoSuchIdentityException();
|
||||
return db.getContacts(txn, local);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -545,28 +536,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<HandshakeKeySet> getHandshakeKeys(Transaction transaction,
|
||||
TransportId t) throws DbException {
|
||||
public Identity getIdentity(Transaction transaction, AuthorId a)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
throw new NoSuchTransportException();
|
||||
return db.getHandshakeKeys(txn, t);
|
||||
if (!db.containsIdentity(txn, a))
|
||||
throw new NoSuchIdentityException();
|
||||
return db.getIdentity(txn, a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
|
||||
public Collection<Identity> getIdentities(Transaction transaction)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsLocalAuthor(txn, a))
|
||||
throw new NoSuchLocalAuthorException();
|
||||
return db.getLocalAuthor(txn, a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LocalAuthor> getLocalAuthors(Transaction transaction)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
return db.getLocalAuthors(txn);
|
||||
return db.getIdentities(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -735,17 +717,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
||||
HandshakeKeySetId k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
throw new NoSuchTransportException();
|
||||
db.incrementStreamCounter(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
||||
TransportKeySetId k) throws DbException {
|
||||
KeySetId k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
@@ -895,24 +867,14 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHandshakeKeys(Transaction transaction,
|
||||
TransportId t, HandshakeKeySetId k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
throw new NoSuchTransportException();
|
||||
db.removeHandshakeKeys(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocalAuthor(Transaction transaction, AuthorId a)
|
||||
public void removeIdentity(Transaction transaction, AuthorId a)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsLocalAuthor(txn, a))
|
||||
throw new NoSuchLocalAuthorException();
|
||||
db.removeLocalAuthor(txn, a);
|
||||
transaction.attach(new LocalAuthorRemovedEvent(a));
|
||||
if (!db.containsIdentity(txn, a))
|
||||
throw new NoSuchIdentityException();
|
||||
db.removeIdentity(txn, a);
|
||||
transaction.attach(new IdentityRemovedEvent(a));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -934,6 +896,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
if (!db.containsPendingContact(txn, p))
|
||||
throw new NoSuchPendingContactException();
|
||||
db.removePendingContact(txn, p);
|
||||
transaction.attach(new PendingContactRemovedEvent(p));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -947,8 +910,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTransportKeys(Transaction transaction,
|
||||
TransportId t, TransportKeySetId k) throws DbException {
|
||||
public void removeTransportKeys(Transaction transaction, TransportId t,
|
||||
KeySetId k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
@@ -1036,20 +999,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReorderingWindow(Transaction transaction,
|
||||
TransportKeySetId k, TransportId t, long timePeriod, long base,
|
||||
byte[] bitmap) throws DbException {
|
||||
public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
|
||||
PublicKey publicKey, PrivateKey privateKey) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
throw new NoSuchTransportException();
|
||||
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
|
||||
if (!db.containsIdentity(txn, local))
|
||||
throw new NoSuchIdentityException();
|
||||
db.setHandshakeKeyPair(txn, local, publicKey, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReorderingWindow(Transaction transaction,
|
||||
HandshakeKeySetId k, TransportId t, long timePeriod, long base,
|
||||
byte[] bitmap) throws DbException {
|
||||
public void setReorderingWindow(Transaction transaction, KeySetId k,
|
||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
@@ -1059,7 +1021,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public void setTransportKeysActive(Transaction transaction, TransportId t,
|
||||
TransportKeySetId k) throws DbException {
|
||||
KeySetId k) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsTransport(txn, t))
|
||||
@@ -1067,18 +1029,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.setTransportKeysActive(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHandshakeKeys(Transaction transaction,
|
||||
Collection<HandshakeKeySet> keys) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
for (HandshakeKeySet ks : keys) {
|
||||
TransportId t = ks.getKeys().getTransportId();
|
||||
if (db.containsTransport(txn, t))
|
||||
db.updateHandshakeKeys(txn, ks);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTransportKeys(Transaction transaction,
|
||||
Collection<TransportKeySet> keys) throws DbException {
|
||||
|
||||
@@ -20,9 +20,11 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
|
||||
|
||||
/**
|
||||
* Contains all the H2-specific code for the database.
|
||||
@@ -61,8 +63,18 @@ class H2Database extends JdbcDatabase {
|
||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
this.key = key;
|
||||
boolean reopen = !config.getDatabaseDirectory().mkdirs();
|
||||
File dir = config.getDatabaseDirectory();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of account directory before opening DB:");
|
||||
logFileOrDir(LOG, INFO, dir.getParentFile());
|
||||
}
|
||||
boolean reopen = !dir.mkdirs();
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
|
||||
super.open("org.h2.Driver", reopen, key, listener);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of account directory after opening DB:");
|
||||
logFileOrDir(LOG, INFO, dir.getParentFile());
|
||||
}
|
||||
return reopen;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,13 @@ 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.SignaturePrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.SignaturePublicKey;
|
||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DbClosedException;
|
||||
@@ -15,6 +21,7 @@ import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -29,13 +36,10 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||
import org.briarproject.bramble.api.sync.validation.MessageState;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||
import org.briarproject.bramble.api.transport.HandshakeKeys;
|
||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
|
||||
import java.sql.Connection;
|
||||
@@ -61,11 +65,13 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.sql.Types.BINARY;
|
||||
import static java.sql.Types.BOOLEAN;
|
||||
import static java.sql.Types.INTEGER;
|
||||
import static java.sql.Types.VARCHAR;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
@@ -92,7 +98,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
// Package access for testing
|
||||
static final int CODE_SCHEMA_VERSION = 43;
|
||||
static final int CODE_SCHEMA_VERSION = 44;
|
||||
|
||||
// Time period offsets for incoming transport keys
|
||||
private static final int OFFSET_PREV = -1;
|
||||
@@ -253,16 +259,28 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " maxLatency INT NOT NULL,"
|
||||
+ " PRIMARY KEY (transportId))";
|
||||
|
||||
private static final String CREATE_PENDING_CONTACTS =
|
||||
"CREATE TABLE pendingContacts"
|
||||
+ " (pendingContactId _HASH NOT NULL,"
|
||||
+ " publicKey _BINARY NOT NULL,"
|
||||
+ " alias _STRING NOT NULL,"
|
||||
+ " state INT NOT NULL,"
|
||||
+ " timestamp BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (pendingContactId))";
|
||||
|
||||
private static final String CREATE_OUTGOING_KEYS =
|
||||
"CREATE TABLE outgoingKeys"
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " keySetId _COUNTER,"
|
||||
+ " timePeriod BIGINT NOT NULL,"
|
||||
+ " contactId INT NOT NULL,"
|
||||
+ " contactId INT," // Null if contact is pending
|
||||
+ " pendingContactId _HASH," // Null if not pending
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " stream BIGINT NOT NULL,"
|
||||
+ " active BOOLEAN NOT NULL,"
|
||||
+ " rootKey _SECRET," // Null for rotation keys
|
||||
+ " alice BOOLEAN," // Null for rotation keys
|
||||
+ " PRIMARY KEY (transportId, keySetId),"
|
||||
+ " FOREIGN KEY (transportId)"
|
||||
+ " REFERENCES transports (transportId)"
|
||||
@@ -270,6 +288,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " UNIQUE (keySetId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
+ " FOREIGN KEY (pendingContactId)"
|
||||
+ " REFERENCES pendingContacts (pendingContactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_INCOMING_KEYS =
|
||||
@@ -290,57 +311,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " REFERENCES outgoingKeys (keySetId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_PENDING_CONTACTS =
|
||||
"CREATE TABLE pendingContacts"
|
||||
+ " (pendingContactId _HASH NOT NULL,"
|
||||
+ " publicKey _BINARY NOT NULL,"
|
||||
+ " alias _STRING NOT NULL,"
|
||||
+ " state INT NOT NULL,"
|
||||
+ " timestamp BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (pendingContactId))";
|
||||
|
||||
private static final String CREATE_OUTGOING_HANDSHAKE_KEYS =
|
||||
"CREATE TABLE outgoingHandshakeKeys"
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " keySetId _COUNTER,"
|
||||
+ " timePeriod BIGINT NOT NULL,"
|
||||
+ " contactId INT," // Null if contact is pending
|
||||
+ " pendingContactId _HASH," // Null if not pending
|
||||
+ " rootKey _SECRET NOT NULL,"
|
||||
+ " alice BOOLEAN NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " stream BIGINT NOT NULL,"
|
||||
+ " PRIMARY KEY (transportId, keySetId),"
|
||||
+ " FOREIGN KEY (transportId)"
|
||||
+ " REFERENCES transports (transportId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
+ " UNIQUE (keySetId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
+ " FOREIGN KEY (pendingContactId)"
|
||||
+ " REFERENCES pendingContacts (pendingContactId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String CREATE_INCOMING_HANDSHAKE_KEYS =
|
||||
"CREATE TABLE incomingHandshakeKeys"
|
||||
+ " (transportId _STRING NOT NULL,"
|
||||
+ " keySetId INT NOT NULL,"
|
||||
+ " timePeriod BIGINT NOT NULL,"
|
||||
+ " tagKey _SECRET NOT NULL,"
|
||||
+ " headerKey _SECRET NOT NULL,"
|
||||
+ " base BIGINT NOT NULL,"
|
||||
+ " bitmap _BINARY NOT NULL,"
|
||||
+ " periodOffset INT NOT NULL,"
|
||||
+ " PRIMARY KEY (transportId, keySetId, periodOffset),"
|
||||
+ " FOREIGN KEY (transportId)"
|
||||
+ " REFERENCES transports (transportId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
+ " FOREIGN KEY (keySetId)"
|
||||
+ " REFERENCES outgoingHandshakeKeys (keySetId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
|
||||
private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
|
||||
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
|
||||
+ " ON contacts (authorId)";
|
||||
@@ -366,7 +336,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " ON statuses (contactId, timestamp)";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(JdbcDatabase.class.getName());
|
||||
getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
// Different database libraries use different names for certain types
|
||||
private final MessageFactory messageFactory;
|
||||
@@ -486,7 +456,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
new Migration39_40(),
|
||||
new Migration40_41(dbTypes),
|
||||
new Migration41_42(dbTypes),
|
||||
new Migration42_43(dbTypes)
|
||||
new Migration42_43(dbTypes),
|
||||
new Migration43_44(dbTypes)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -534,13 +505,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(CREATE_PENDING_CONTACTS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(
|
||||
CREATE_OUTGOING_HANDSHAKE_KEYS));
|
||||
s.executeUpdate(dbTypes.replaceTypes(
|
||||
CREATE_INCOMING_HANDSHAKE_KEYS));
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
@@ -676,7 +643,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.setBytes(1, remote.getId().getBytes());
|
||||
ps.setInt(2, remote.getFormatVersion());
|
||||
ps.setString(3, remote.getName());
|
||||
ps.setBytes(4, remote.getPublicKey());
|
||||
ps.setBytes(4, remote.getPublicKey().getEncoded());
|
||||
ps.setBytes(5, local.getBytes());
|
||||
ps.setBoolean(6, verified);
|
||||
int affected = ps.executeUpdate();
|
||||
@@ -777,105 +744,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeKeySetId addHandshakeKeys(Connection txn, ContactId c,
|
||||
HandshakeKeys k) throws DbException {
|
||||
return addHandshakeKeys(txn, c, null, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeKeySetId addHandshakeKeys(Connection txn,
|
||||
PendingContactId p, HandshakeKeys k) throws DbException {
|
||||
return addHandshakeKeys(txn, null, p, k);
|
||||
}
|
||||
|
||||
private HandshakeKeySetId addHandshakeKeys(Connection txn,
|
||||
@Nullable ContactId c, @Nullable PendingContactId p,
|
||||
HandshakeKeys k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Store the outgoing keys
|
||||
String sql = "INSERT INTO outgoingHandshakeKeys (contactId,"
|
||||
+ " pendingContactId, transportId, rootKey, alice,"
|
||||
+ " timePeriod, tagKey, headerKey, stream)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
if (c == null) ps.setNull(1, INTEGER);
|
||||
else ps.setInt(1, c.getInt());
|
||||
if (p == null) ps.setNull(2, BINARY);
|
||||
else ps.setBytes(2, p.getBytes());
|
||||
ps.setString(3, k.getTransportId().getString());
|
||||
ps.setBytes(4, k.getRootKey().getBytes());
|
||||
ps.setBoolean(5, k.isAlice());
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
ps.setLong(6, outCurr.getTimePeriod());
|
||||
ps.setBytes(7, outCurr.getTagKey().getBytes());
|
||||
ps.setBytes(8, outCurr.getHeaderKey().getBytes());
|
||||
ps.setLong(9, outCurr.getStreamCounter());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Get the new (highest) key set ID
|
||||
sql = "SELECT keySetId FROM outgoingHandshakeKeys"
|
||||
+ " ORDER BY keySetId DESC LIMIT 1";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
if (!rs.next()) throw new DbStateException();
|
||||
HandshakeKeySetId keySetId = new HandshakeKeySetId(rs.getInt(1));
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Store the incoming keys
|
||||
sql = "INSERT INTO incomingHandshakeKeys (keySetId, transportId,"
|
||||
+ " timePeriod, tagKey, headerKey, base, bitmap,"
|
||||
+ " periodOffset)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, keySetId.getInt());
|
||||
ps.setString(2, k.getTransportId().getString());
|
||||
// Previous time period
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
ps.setLong(3, inPrev.getTimePeriod());
|
||||
ps.setBytes(4, inPrev.getTagKey().getBytes());
|
||||
ps.setBytes(5, inPrev.getHeaderKey().getBytes());
|
||||
ps.setLong(6, inPrev.getWindowBase());
|
||||
ps.setBytes(7, inPrev.getWindowBitmap());
|
||||
ps.setInt(8, OFFSET_PREV);
|
||||
ps.addBatch();
|
||||
// Current time period
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
ps.setLong(3, inCurr.getTimePeriod());
|
||||
ps.setBytes(4, inCurr.getTagKey().getBytes());
|
||||
ps.setBytes(5, inCurr.getHeaderKey().getBytes());
|
||||
ps.setLong(6, inCurr.getWindowBase());
|
||||
ps.setBytes(7, inCurr.getWindowBitmap());
|
||||
ps.setInt(8, OFFSET_CURR);
|
||||
ps.addBatch();
|
||||
// Next time period
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
ps.setLong(3, inNext.getTimePeriod());
|
||||
ps.setBytes(4, inNext.getTagKey().getBytes());
|
||||
ps.setBytes(5, inNext.getHeaderKey().getBytes());
|
||||
ps.setLong(6, inNext.getWindowBase());
|
||||
ps.setBytes(7, inNext.getWindowBitmap());
|
||||
ps.setInt(8, OFFSET_NEXT);
|
||||
ps.addBatch();
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
if (batchAffected.length != 3) throw new DbStateException();
|
||||
for (int rows : batchAffected)
|
||||
if (rows != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
return keySetId;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalAuthor(Connection txn, LocalAuthor a)
|
||||
throws DbException {
|
||||
public void addIdentity(Connection txn, Identity i) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "INSERT INTO localAuthors"
|
||||
@@ -883,16 +752,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " handshakePublicKey, handshakePrivateKey, created)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, a.getId().getBytes());
|
||||
ps.setInt(2, a.getFormatVersion());
|
||||
ps.setString(3, a.getName());
|
||||
ps.setBytes(4, a.getPublicKey());
|
||||
ps.setBytes(5, a.getPrivateKey());
|
||||
if (a.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
|
||||
else ps.setBytes(6, a.getHandshakePublicKey());
|
||||
if (a.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
|
||||
else ps.setBytes(7, a.getHandshakePrivateKey());
|
||||
ps.setLong(8, a.getTimeCreated());
|
||||
LocalAuthor local = i.getLocalAuthor();
|
||||
ps.setBytes(1, local.getId().getBytes());
|
||||
ps.setInt(2, local.getFormatVersion());
|
||||
ps.setString(3, local.getName());
|
||||
ps.setBytes(4, local.getPublicKey().getEncoded());
|
||||
ps.setBytes(5, local.getPrivateKey().getEncoded());
|
||||
if (i.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
|
||||
else ps.setBytes(6, i.getHandshakePublicKey().getEncoded());
|
||||
if (i.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
|
||||
else ps.setBytes(7, i.getHandshakePrivateKey().getEncoded());
|
||||
ps.setLong(8, i.getTimeCreated());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -1067,7 +937,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " VALUES (?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, p.getId().getBytes());
|
||||
ps.setBytes(2, p.getPublicKey());
|
||||
ps.setBytes(2, p.getPublicKey().getEncoded());
|
||||
ps.setString(3, p.getAlias());
|
||||
ps.setInt(4, p.getState().getValue());
|
||||
ps.setLong(5, p.getTimestamp());
|
||||
@@ -1100,24 +970,47 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeySetId addTransportKeys(Connection txn, ContactId c,
|
||||
public KeySetId addTransportKeys(Connection txn, ContactId c,
|
||||
TransportKeys k) throws DbException {
|
||||
return addTransportKeys(txn, c, null, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addTransportKeys(Connection txn,
|
||||
PendingContactId p, TransportKeys k) throws DbException {
|
||||
return addTransportKeys(txn, null, p, k);
|
||||
}
|
||||
|
||||
private KeySetId addTransportKeys(Connection txn,
|
||||
@Nullable ContactId c, @Nullable PendingContactId p,
|
||||
TransportKeys k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Store the outgoing keys
|
||||
String sql = "INSERT INTO outgoingKeys (contactId, transportId,"
|
||||
+ " timePeriod, tagKey, headerKey, stream, active)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
String sql = "INSERT INTO outgoingKeys (transportId, timePeriod,"
|
||||
+ " contactId, pendingContactId, tagKey, headerKey,"
|
||||
+ " stream, active, rootKey, alice)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
ps.setString(2, k.getTransportId().getString());
|
||||
ps.setString(1, k.getTransportId().getString());
|
||||
ps.setLong(2, k.getTimePeriod());
|
||||
if (c == null) ps.setNull(3, INTEGER);
|
||||
else ps.setInt(3, c.getInt());
|
||||
if (p == null) ps.setNull(4, BINARY);
|
||||
else ps.setBytes(4, p.getBytes());
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
ps.setLong(3, outCurr.getTimePeriod());
|
||||
ps.setBytes(4, outCurr.getTagKey().getBytes());
|
||||
ps.setBytes(5, outCurr.getHeaderKey().getBytes());
|
||||
ps.setLong(6, outCurr.getStreamCounter());
|
||||
ps.setBoolean(7, outCurr.isActive());
|
||||
ps.setBytes(5, outCurr.getTagKey().getBytes());
|
||||
ps.setBytes(6, outCurr.getHeaderKey().getBytes());
|
||||
ps.setLong(7, outCurr.getStreamCounter());
|
||||
ps.setBoolean(8, outCurr.isActive());
|
||||
if (k.isHandshakeMode()) {
|
||||
ps.setBytes(9, k.getRootKey().getBytes());
|
||||
ps.setBoolean(10, k.isAlice());
|
||||
} else {
|
||||
ps.setNull(9, BINARY);
|
||||
ps.setNull(10, BOOLEAN);
|
||||
}
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -1127,18 +1020,18 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
if (!rs.next()) throw new DbStateException();
|
||||
TransportKeySetId keySetId = new TransportKeySetId(rs.getInt(1));
|
||||
KeySetId keySetId = new KeySetId(rs.getInt(1));
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Store the incoming keys
|
||||
sql = "INSERT INTO incomingKeys (keySetId, transportId,"
|
||||
sql = "INSERT INTO incomingKeys (transportId, keySetId,"
|
||||
+ " timePeriod, tagKey, headerKey, base, bitmap,"
|
||||
+ " periodOffset)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, keySetId.getInt());
|
||||
ps.setString(2, k.getTransportId().getString());
|
||||
ps.setString(1, k.getTransportId().getString());
|
||||
ps.setInt(2, keySetId.getInt());
|
||||
// Previous time period
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
ps.setLong(3, inPrev.getTimePeriod());
|
||||
@@ -1248,7 +1141,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsLocalAuthor(Connection txn, AuthorId a)
|
||||
public boolean containsIdentity(Connection txn, AuthorId a)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
@@ -1443,14 +1336,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int formatVersion = rs.getInt(2);
|
||||
String name = rs.getString(3);
|
||||
String alias = rs.getString(4);
|
||||
byte[] publicKey = rs.getBytes(5);
|
||||
byte[] handshakePublicKey = rs.getBytes(6);
|
||||
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5));
|
||||
byte[] handshakePub = rs.getBytes(6);
|
||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
||||
boolean verified = rs.getBoolean(8);
|
||||
rs.close();
|
||||
ps.close();
|
||||
Author author =
|
||||
new Author(authorId, formatVersion, name, publicKey);
|
||||
PublicKey handshakePublicKey = handshakePub == null ?
|
||||
null : new AgreementPublicKey(handshakePub);
|
||||
return new Contact(c, author, localAuthorId, alias,
|
||||
handshakePublicKey, verified);
|
||||
} catch (SQLException e) {
|
||||
@@ -1478,12 +1373,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int formatVersion = rs.getInt(3);
|
||||
String name = rs.getString(4);
|
||||
String alias = rs.getString(5);
|
||||
byte[] publicKey = rs.getBytes(6);
|
||||
byte[] handshakePublicKey = rs.getBytes(7);
|
||||
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(6));
|
||||
byte[] handshakePub = rs.getBytes(7);
|
||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(8));
|
||||
boolean verified = rs.getBoolean(9);
|
||||
Author author =
|
||||
new Author(authorId, formatVersion, name, publicKey);
|
||||
PublicKey handshakePublicKey = handshakePub == null ?
|
||||
null : new AgreementPublicKey(handshakePub);
|
||||
contacts.add(new Contact(contactId, author, localAuthorId,
|
||||
alias, handshakePublicKey, verified));
|
||||
}
|
||||
@@ -1539,12 +1436,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int formatVersion = rs.getInt(2);
|
||||
String name = rs.getString(3);
|
||||
String alias = rs.getString(4);
|
||||
byte[] publicKey = rs.getBytes(5);
|
||||
byte[] handshakePublicKey = rs.getBytes(6);
|
||||
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(5));
|
||||
byte[] handshakePub = rs.getBytes(6);
|
||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
||||
boolean verified = rs.getBoolean(8);
|
||||
Author author =
|
||||
new Author(remote, formatVersion, name, publicKey);
|
||||
PublicKey handshakePublicKey = handshakePub == null ?
|
||||
null : new AgreementPublicKey(handshakePub);
|
||||
contacts.add(new Contact(contactId, author, localAuthorId,
|
||||
alias, handshakePublicKey, verified));
|
||||
}
|
||||
@@ -1661,8 +1560,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor getLocalAuthor(Connection txn, AuthorId a)
|
||||
throws DbException {
|
||||
public Identity getIdentity(Connection txn, AuthorId a) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
@@ -1676,18 +1574,22 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (!rs.next()) throw new DbStateException();
|
||||
int formatVersion = rs.getInt(1);
|
||||
String name = rs.getString(2);
|
||||
byte[] publicKey = rs.getBytes(3);
|
||||
byte[] privateKey = rs.getBytes(4);
|
||||
byte[] handshakePublicKey = rs.getBytes(5);
|
||||
byte[] handshakePrivateKey = rs.getBytes(6);
|
||||
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(3));
|
||||
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(4));
|
||||
byte[] handshakePub = rs.getBytes(5);
|
||||
byte[] handshakePriv = rs.getBytes(6);
|
||||
long created = rs.getLong(7);
|
||||
LocalAuthor localAuthor = new LocalAuthor(a, formatVersion, name,
|
||||
publicKey, privateKey, handshakePublicKey,
|
||||
handshakePrivateKey, created);
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
return localAuthor;
|
||||
LocalAuthor local = new LocalAuthor(a, formatVersion, name,
|
||||
publicKey, privateKey);
|
||||
PublicKey handshakePublicKey = handshakePub == null ?
|
||||
null : new AgreementPublicKey(handshakePub);
|
||||
PrivateKey handshakePrivateKey = handshakePriv == null ?
|
||||
null : new AgreementPrivateKey(handshakePriv);
|
||||
return new Identity(local, handshakePublicKey, handshakePrivateKey,
|
||||
created);
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
@@ -1696,110 +1598,39 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<HandshakeKeySet> getHandshakeKeys(Connection txn,
|
||||
TransportId t) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Retrieve the incoming keys
|
||||
String sql = "SELECT timePeriod, tagKey, headerKey, base, bitmap"
|
||||
+ " FROM incomingHandshakeKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY keySetId, periodOffset";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
rs = ps.executeQuery();
|
||||
List<IncomingKeys> inKeys = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
long timePeriod = rs.getLong(1);
|
||||
SecretKey tagKey = new SecretKey(rs.getBytes(2));
|
||||
SecretKey headerKey = new SecretKey(rs.getBytes(3));
|
||||
long windowBase = rs.getLong(4);
|
||||
byte[] windowBitmap = rs.getBytes(5);
|
||||
inKeys.add(new IncomingKeys(tagKey, headerKey, timePeriod,
|
||||
windowBase, windowBitmap));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Retrieve the outgoing keys in the same order
|
||||
sql = "SELECT keySetId, contactId, pendingContactId, timePeriod,"
|
||||
+ " tagKey, headerKey, rootKey, alice, stream"
|
||||
+ " FROM outgoingHandshakeKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY keySetId";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
rs = ps.executeQuery();
|
||||
Collection<HandshakeKeySet> keys = new ArrayList<>();
|
||||
for (int i = 0; rs.next(); i++) {
|
||||
// There should be three times as many incoming keys
|
||||
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
||||
HandshakeKeySetId keySetId =
|
||||
new HandshakeKeySetId(rs.getInt(1));
|
||||
ContactId contactId = null;
|
||||
int cId = rs.getInt(2);
|
||||
if (!rs.wasNull()) contactId = new ContactId(cId);
|
||||
PendingContactId pendingContactId = null;
|
||||
byte[] pId = rs.getBytes(3);
|
||||
if (!rs.wasNull()) pendingContactId = new PendingContactId(pId);
|
||||
long timePeriod = rs.getLong(4);
|
||||
SecretKey tagKey = new SecretKey(rs.getBytes(5));
|
||||
SecretKey headerKey = new SecretKey(rs.getBytes(6));
|
||||
SecretKey rootKey = new SecretKey(rs.getBytes(7));
|
||||
boolean alice = rs.getBoolean(8);
|
||||
long streamCounter = rs.getLong(9);
|
||||
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
||||
timePeriod, streamCounter, true);
|
||||
IncomingKeys inPrev = inKeys.get(i * 3);
|
||||
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
|
||||
IncomingKeys inNext = inKeys.get(i * 3 + 2);
|
||||
HandshakeKeys handshakeKeys = new HandshakeKeys(t, inPrev,
|
||||
inCurr, inNext, outCurr, rootKey, alice);
|
||||
if (contactId == null) {
|
||||
if (pendingContactId == null) throw new DbStateException();
|
||||
keys.add(new HandshakeKeySet(keySetId, pendingContactId,
|
||||
handshakeKeys));
|
||||
} else {
|
||||
if (pendingContactId != null) throw new DbStateException();
|
||||
keys.add(new HandshakeKeySet(keySetId, contactId,
|
||||
handshakeKeys));
|
||||
}
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return keys;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LocalAuthor> getLocalAuthors(Connection txn)
|
||||
public Collection<Identity> getIdentities(Connection txn)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT authorId, formatVersion, name, publicKey,"
|
||||
+ " privateKey, created"
|
||||
+ " privateKey, handshakePublicKey, handshakePrivateKey,"
|
||||
+ " created"
|
||||
+ " FROM localAuthors";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
List<LocalAuthor> authors = new ArrayList<>();
|
||||
List<Identity> identities = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
||||
int formatVersion = rs.getInt(2);
|
||||
String name = rs.getString(3);
|
||||
byte[] publicKey = rs.getBytes(4);
|
||||
byte[] privateKey = rs.getBytes(5);
|
||||
long created = rs.getLong(6);
|
||||
authors.add(new LocalAuthor(authorId, formatVersion, name,
|
||||
publicKey, privateKey, created));
|
||||
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(4));
|
||||
PrivateKey privateKey = new SignaturePrivateKey(rs.getBytes(5));
|
||||
byte[] handshakePub = rs.getBytes(6);
|
||||
byte[] handshakePriv = rs.getBytes(7);
|
||||
long created = rs.getLong(8);
|
||||
LocalAuthor local = new LocalAuthor(authorId, formatVersion,
|
||||
name, publicKey, privateKey);
|
||||
PublicKey handshakePublicKey = handshakePub == null ?
|
||||
null : new AgreementPublicKey(handshakePub);
|
||||
PrivateKey handshakePrivateKey = handshakePriv == null ?
|
||||
null : new AgreementPrivateKey(handshakePriv);
|
||||
identities.add(new Identity(local, handshakePublicKey,
|
||||
handshakePrivateKey, created));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return authors;
|
||||
return identities;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
@@ -2390,7 +2221,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
List<PendingContact> pendingContacts = new ArrayList<>();
|
||||
while (rs.next()) {
|
||||
PendingContactId id = new PendingContactId(rs.getBytes(1));
|
||||
byte[] publicKey = rs.getBytes(2);
|
||||
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(2));
|
||||
String alias = rs.getString(3);
|
||||
PendingContactState state =
|
||||
PendingContactState.fromValue(rs.getInt(4));
|
||||
@@ -2497,8 +2328,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Retrieve the outgoing keys in the same order
|
||||
sql = "SELECT keySetId, contactId, timePeriod,"
|
||||
+ " tagKey, headerKey, stream, active"
|
||||
sql = "SELECT keySetId, timePeriod, contactId, pendingContactId,"
|
||||
+ " tagKey, headerKey, stream, active, rootKey, alice"
|
||||
+ " FROM outgoingKeys"
|
||||
+ " WHERE transportId = ?"
|
||||
+ " ORDER BY keySetId";
|
||||
@@ -2509,23 +2340,34 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
for (int i = 0; rs.next(); i++) {
|
||||
// There should be three times as many incoming keys
|
||||
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
||||
TransportKeySetId keySetId =
|
||||
new TransportKeySetId(rs.getInt(1));
|
||||
ContactId contactId = new ContactId(rs.getInt(2));
|
||||
long timePeriod = rs.getLong(3);
|
||||
SecretKey tagKey = new SecretKey(rs.getBytes(4));
|
||||
SecretKey headerKey = new SecretKey(rs.getBytes(5));
|
||||
long streamCounter = rs.getLong(6);
|
||||
boolean active = rs.getBoolean(7);
|
||||
KeySetId keySetId = new KeySetId(rs.getInt(1));
|
||||
long timePeriod = rs.getLong(2);
|
||||
int cId = rs.getInt(3);
|
||||
ContactId contactId = rs.wasNull() ? null : new ContactId(cId);
|
||||
byte[] pId = rs.getBytes(4);
|
||||
PendingContactId pendingContactId = pId == null ?
|
||||
null : new PendingContactId(pId);
|
||||
SecretKey tagKey = new SecretKey(rs.getBytes(5));
|
||||
SecretKey headerKey = new SecretKey(rs.getBytes(6));
|
||||
long streamCounter = rs.getLong(7);
|
||||
boolean active = rs.getBoolean(8);
|
||||
byte[] rootKey = rs.getBytes(9);
|
||||
boolean alice = rs.getBoolean(10);
|
||||
OutgoingKeys outCurr = new OutgoingKeys(tagKey, headerKey,
|
||||
timePeriod, streamCounter, active);
|
||||
IncomingKeys inPrev = inKeys.get(i * 3);
|
||||
IncomingKeys inCurr = inKeys.get(i * 3 + 1);
|
||||
IncomingKeys inNext = inKeys.get(i * 3 + 2);
|
||||
TransportKeys transportKeys = new TransportKeys(t, inPrev,
|
||||
inCurr, inNext, outCurr);
|
||||
TransportKeys transportKeys;
|
||||
if (rootKey == null) {
|
||||
transportKeys = new TransportKeys(t, inPrev, inCurr,
|
||||
inNext, outCurr);
|
||||
} else {
|
||||
transportKeys = new TransportKeys(t, inPrev, inCurr,
|
||||
inNext, outCurr, new SecretKey(rootKey), alice);
|
||||
}
|
||||
keys.add(new TransportKeySet(keySetId, contactId,
|
||||
transportKeys));
|
||||
pendingContactId, transportKeys));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
@@ -2539,26 +2381,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
@Override
|
||||
public void incrementStreamCounter(Connection txn, TransportId t,
|
||||
HandshakeKeySetId k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE outgoingHandshakeKeys SET stream = stream + 1"
|
||||
+ " WHERE transportId = ? AND keySetId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
ps.setInt(2, k.getInt());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementStreamCounter(Connection txn, TransportId t,
|
||||
TransportKeySetId k) throws DbException {
|
||||
KeySetId k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
||||
@@ -2937,29 +2760,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHandshakeKeys(Connection txn, TransportId t,
|
||||
HandshakeKeySetId k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
// Delete any existing outgoing keys - this will also remove any
|
||||
// incoming keys with the same key set ID
|
||||
String sql = "DELETE FROM outgoingHandshakeKeys"
|
||||
+ " WHERE transportId = ? AND keySetId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(1, t.getString());
|
||||
ps.setInt(2, k.getInt());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocalAuthor(Connection txn, AuthorId a)
|
||||
throws DbException {
|
||||
public void removeIdentity(Connection txn, AuthorId a) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "DELETE FROM localAuthors WHERE authorId = ?";
|
||||
@@ -3070,8 +2871,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTransportKeys(Connection txn, TransportId t,
|
||||
TransportKeySetId k) throws DbException {
|
||||
public void removeTransportKeys(Connection txn, TransportId t, KeySetId k)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
// Delete any existing outgoing keys - this will also remove any
|
||||
@@ -3176,6 +2977,27 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHandshakeKeyPair(Connection txn, AuthorId local,
|
||||
PublicKey publicKey, PrivateKey privateKey) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE localAuthors"
|
||||
+ " SET handshakePublicKey = ?, handshakePrivateKey = ?"
|
||||
+ " WHERE authorId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, publicKey.getEncoded());
|
||||
ps.setBytes(2, privateKey.getEncoded());
|
||||
ps.setBytes(3, local.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageShared(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
@@ -3266,7 +3088,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.setInt(1, state.getValue());
|
||||
ps.setBytes(2, p.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected != 1) throw new DbStateException();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
@@ -3275,7 +3097,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReorderingWindow(Connection txn, TransportKeySetId k,
|
||||
public void setReorderingWindow(Connection txn, KeySetId k,
|
||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -3298,33 +3120,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReorderingWindow(Connection txn, HandshakeKeySetId k,
|
||||
TransportId t, long timePeriod, long base, byte[] bitmap)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE incomingHandshakeKeys SET base = ?, bitmap = ?"
|
||||
+ " WHERE transportId = ? AND keySetId = ?"
|
||||
+ " AND timePeriod = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, base);
|
||||
ps.setBytes(2, bitmap);
|
||||
ps.setString(3, t.getString());
|
||||
ps.setInt(4, k.getInt());
|
||||
ps.setLong(5, timePeriod);
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransportKeysActive(Connection txn, TransportId t,
|
||||
TransportKeySetId k) throws DbException {
|
||||
KeySetId k) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE outgoingKeys SET active = true"
|
||||
@@ -3444,71 +3242,4 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHandshakeKeys(Connection txn, HandshakeKeySet ks)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
// Update the outgoing keys
|
||||
String sql = "UPDATE outgoingHandshakeKeys SET timePeriod = ?,"
|
||||
+ " tagKey = ?, headerKey = ?, stream = ?"
|
||||
+ " WHERE transportId = ? AND keySetId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
HandshakeKeys k = ks.getKeys();
|
||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||
ps.setLong(1, outCurr.getTimePeriod());
|
||||
ps.setBytes(2, outCurr.getTagKey().getBytes());
|
||||
ps.setBytes(3, outCurr.getHeaderKey().getBytes());
|
||||
ps.setLong(4, outCurr.getStreamCounter());
|
||||
ps.setString(5, k.getTransportId().getString());
|
||||
ps.setInt(6, ks.getKeySetId().getInt());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Update the incoming keys
|
||||
sql = "UPDATE incomingHandshakeKeys SET timePeriod = ?,"
|
||||
+ " tagKey = ?, headerKey = ?, base = ?, bitmap = ?"
|
||||
+ " WHERE transportId = ? AND keySetId = ?"
|
||||
+ " AND periodOffset = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setString(6, k.getTransportId().getString());
|
||||
ps.setInt(7, ks.getKeySetId().getInt());
|
||||
// Previous time period
|
||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||
ps.setLong(1, inPrev.getTimePeriod());
|
||||
ps.setBytes(2, inPrev.getTagKey().getBytes());
|
||||
ps.setBytes(3, inPrev.getHeaderKey().getBytes());
|
||||
ps.setLong(4, inPrev.getWindowBase());
|
||||
ps.setBytes(5, inPrev.getWindowBitmap());
|
||||
ps.setInt(8, OFFSET_PREV);
|
||||
ps.addBatch();
|
||||
// Current time period
|
||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||
ps.setLong(1, inCurr.getTimePeriod());
|
||||
ps.setBytes(2, inCurr.getTagKey().getBytes());
|
||||
ps.setBytes(3, inCurr.getHeaderKey().getBytes());
|
||||
ps.setLong(4, inCurr.getWindowBase());
|
||||
ps.setBytes(5, inCurr.getWindowBitmap());
|
||||
ps.setInt(8, OFFSET_CURR);
|
||||
ps.addBatch();
|
||||
// Next time period
|
||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||
ps.setLong(1, inNext.getTimePeriod());
|
||||
ps.setBytes(2, inNext.getTagKey().getBytes());
|
||||
ps.setBytes(3, inNext.getHeaderKey().getBytes());
|
||||
ps.setLong(4, inNext.getWindowBase());
|
||||
ps.setBytes(5, inNext.getWindowBitmap());
|
||||
ps.setInt(8, OFFSET_NEXT);
|
||||
ps.addBatch();
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
if (batchAffected.length != 3) throw new DbStateException();
|
||||
for (int rows : batchAffected)
|
||||
if (rows < 0 || rows > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
|
||||
class Migration43_44 implements Migration<Connection> {
|
||||
|
||||
private static final Logger LOG = getLogger(Migration43_44.class.getName());
|
||||
|
||||
private final DatabaseTypes dbTypes;
|
||||
|
||||
Migration43_44(DatabaseTypes dbTypes) {
|
||||
this.dbTypes = dbTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartVersion() {
|
||||
return 43;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndVersion() {
|
||||
return 44;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
s.execute("DROP TABLE outgoingHandshakeKeys");
|
||||
s.execute("DROP TABLE incomingHandshakeKeys");
|
||||
s.execute("ALTER TABLE outgoingKeys"
|
||||
+ " ALTER COLUMN contactId DROP NOT NULL");
|
||||
s.execute(dbTypes.replaceTypes("ALTER TABLE outgoingKeys"
|
||||
+ " ADD COLUMN pendingContactId _HASH"));
|
||||
s.execute("ALTER TABLE outgoingKeys"
|
||||
+ " ADD FOREIGN KEY (pendingContactId)"
|
||||
+ " REFERENCES pendingContacts (pendingContactId)"
|
||||
+ " ON DELETE CASCADE");
|
||||
s.execute(dbTypes.replaceTypes("ALTER TABLE outgoingKeys"
|
||||
+ " ADD COLUMN rootKey _SECRET"));
|
||||
s.execute("ALTER TABLE outgoingKeys"
|
||||
+ " ADD COLUMN alice BOOLEAN");
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.briarproject.bramble.identity;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
@@ -16,50 +16,46 @@ import javax.inject.Inject;
|
||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.identity.AuthorId.LABEL;
|
||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||
import static org.briarproject.bramble.util.ByteUtils.writeUint32;
|
||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class AuthorFactoryImpl implements AuthorFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
AuthorFactoryImpl(CryptoComponent crypto, Clock clock) {
|
||||
AuthorFactoryImpl(CryptoComponent crypto) {
|
||||
this.crypto = crypto;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Author createAuthor(String name, byte[] publicKey) {
|
||||
public Author createAuthor(String name, PublicKey publicKey) {
|
||||
return createAuthor(FORMAT_VERSION, name, publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Author createAuthor(int formatVersion, String name,
|
||||
byte[] publicKey) {
|
||||
PublicKey publicKey) {
|
||||
AuthorId id = getId(formatVersion, name, publicKey);
|
||||
return new Author(id, formatVersion, name, publicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor createLocalAuthor(String name, byte[] publicKey,
|
||||
byte[] privateKey) {
|
||||
return createLocalAuthor(FORMAT_VERSION, name, publicKey, privateKey);
|
||||
public LocalAuthor createLocalAuthor(String name) {
|
||||
KeyPair signatureKeyPair = crypto.generateSignatureKeyPair();
|
||||
PublicKey publicKey = signatureKeyPair.getPublic();
|
||||
PrivateKey privateKey = signatureKeyPair.getPrivate();
|
||||
AuthorId id = getId(FORMAT_VERSION, name, publicKey);
|
||||
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor createLocalAuthor(int formatVersion, String name,
|
||||
byte[] publicKey, byte[] privateKey) {
|
||||
AuthorId id = getId(formatVersion, name, publicKey);
|
||||
return new LocalAuthor(id, formatVersion, name, publicKey, privateKey,
|
||||
clock.currentTimeMillis());
|
||||
}
|
||||
|
||||
private AuthorId getId(int formatVersion, String name, byte[] publicKey) {
|
||||
private AuthorId getId(int formatVersion, String name,
|
||||
PublicKey publicKey) {
|
||||
byte[] formatVersionBytes = new byte[INT_32_BYTES];
|
||||
ByteUtils.writeUint32(formatVersion, formatVersionBytes, 0);
|
||||
writeUint32(formatVersion, formatVersionBytes, 0);
|
||||
return new AuthorId(crypto.hash(LABEL, formatVersionBytes,
|
||||
StringUtils.toUtf8(name), publicKey));
|
||||
toUtf8(name), publicKey.getEncoded()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,101 +2,170 @@ package org.briarproject.bramble.identity;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
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.identity.AuthorFactory;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class IdentityManagerImpl implements IdentityManager {
|
||||
class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(IdentityManagerImpl.class.getName());
|
||||
getLogger(IdentityManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final CryptoComponent crypto;
|
||||
private final AuthorFactory authorFactory;
|
||||
private final Clock clock;
|
||||
|
||||
// The local author is immutable so we can cache it
|
||||
/**
|
||||
* The user's identity, or null if no identity has been registered or
|
||||
* loaded. If non-null, this identity always has handshake keys.
|
||||
*/
|
||||
@Nullable
|
||||
private volatile LocalAuthor cachedAuthor;
|
||||
private volatile Identity cachedIdentity = null;
|
||||
|
||||
/**
|
||||
* True if {@code cachedIdentity} was registered via
|
||||
* {@link #registerIdentity(Identity)} and should be stored when
|
||||
* {@link #onDatabaseOpened(Transaction)} is called.
|
||||
*/
|
||||
|
||||
private volatile boolean shouldStoreIdentity = false;
|
||||
|
||||
/**
|
||||
* True if the handshake keys in {@code cachedIdentity} were generated
|
||||
* when the identity was loaded and should be stored when
|
||||
* {@link #onDatabaseOpened(Transaction)} is called.
|
||||
*/
|
||||
private volatile boolean shouldStoreKeys = false;
|
||||
|
||||
@Inject
|
||||
IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto,
|
||||
AuthorFactory authorFactory) {
|
||||
AuthorFactory authorFactory, Clock clock) {
|
||||
this.db = db;
|
||||
this.crypto = crypto;
|
||||
this.authorFactory = authorFactory;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor createLocalAuthor(String name) {
|
||||
public Identity createIdentity(String name) {
|
||||
long start = now();
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name,
|
||||
publicKey, privateKey);
|
||||
logDuration(LOG, "Creating local author", start);
|
||||
return localAuthor;
|
||||
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name);
|
||||
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
|
||||
PublicKey handshakePub = handshakeKeyPair.getPublic();
|
||||
PrivateKey handshakePriv = handshakeKeyPair.getPrivate();
|
||||
logDuration(LOG, "Creating identity", start);
|
||||
return new Identity(localAuthor, handshakePub, handshakePriv,
|
||||
clock.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerLocalAuthor(LocalAuthor a) {
|
||||
cachedAuthor = a;
|
||||
LOG.info("Local author registered");
|
||||
public void registerIdentity(Identity i) {
|
||||
if (!i.hasHandshakeKeyPair()) throw new IllegalArgumentException();
|
||||
cachedIdentity = i;
|
||||
shouldStoreIdentity = true;
|
||||
LOG.info("Identity registered");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeLocalAuthor() throws DbException {
|
||||
LocalAuthor cached = cachedAuthor;
|
||||
if (cached == null) {
|
||||
LOG.info("No local author to store");
|
||||
return;
|
||||
public void onDatabaseOpened(Transaction txn) throws DbException {
|
||||
Identity cached = getCachedIdentity(txn);
|
||||
if (shouldStoreIdentity) {
|
||||
// The identity was registered at startup - store it
|
||||
db.addIdentity(txn, cached);
|
||||
LOG.info("Identity stored");
|
||||
} else if (shouldStoreKeys) {
|
||||
// Handshake keys were generated when loading the identity -
|
||||
// store them
|
||||
PublicKey handshakePub =
|
||||
requireNonNull(cached.getHandshakePublicKey());
|
||||
PrivateKey handshakePriv =
|
||||
requireNonNull(cached.getHandshakePrivateKey());
|
||||
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
|
||||
handshakePriv);
|
||||
LOG.info("Handshake key pair stored");
|
||||
}
|
||||
db.transaction(false, txn -> db.addLocalAuthor(txn, cached));
|
||||
LOG.info("Local author stored");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalAuthor getLocalAuthor() throws DbException {
|
||||
if (cachedAuthor == null) {
|
||||
cachedAuthor =
|
||||
db.transactionWithResult(true, this::loadLocalAuthor);
|
||||
LOG.info("Local author loaded");
|
||||
}
|
||||
LocalAuthor cached = cachedAuthor;
|
||||
if (cached == null) throw new AssertionError();
|
||||
return cached;
|
||||
Identity cached = cachedIdentity;
|
||||
if (cached == null)
|
||||
cached = db.transactionWithResult(true, this::getCachedIdentity);
|
||||
return cached.getLocalAuthor();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
|
||||
if (cachedAuthor == null) {
|
||||
cachedAuthor = loadLocalAuthor(txn);
|
||||
LOG.info("Local author loaded");
|
||||
}
|
||||
LocalAuthor cached = cachedAuthor;
|
||||
if (cached == null) throw new AssertionError();
|
||||
return getCachedIdentity(txn).getLocalAuthor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair getHandshakeKeys(Transaction txn) throws DbException {
|
||||
Identity cached = getCachedIdentity(txn);
|
||||
PublicKey handshakePub = requireNonNull(cached.getHandshakePublicKey());
|
||||
PrivateKey handshakePriv =
|
||||
requireNonNull(cached.getHandshakePrivateKey());
|
||||
return new KeyPair(handshakePub, handshakePriv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the identity if necessary and returns it. If
|
||||
* {@code cachedIdentity} was not already set by calling
|
||||
* {@link #registerIdentity(Identity)}, this method sets it. If
|
||||
* {@code cachedIdentity} was already set, either by calling
|
||||
* {@link #registerIdentity(Identity)} or by a previous call to this
|
||||
* method, then this method returns the cached identity without hitting
|
||||
* the database.
|
||||
*/
|
||||
private Identity getCachedIdentity(Transaction txn) throws DbException {
|
||||
Identity cached = cachedIdentity;
|
||||
if (cached == null)
|
||||
cachedIdentity = cached = loadIdentityWithKeyPair(txn);
|
||||
return cached;
|
||||
}
|
||||
|
||||
private LocalAuthor loadLocalAuthor(Transaction txn) throws DbException {
|
||||
return db.getLocalAuthors(txn).iterator().next();
|
||||
/**
|
||||
* Loads and returns the identity, generating a handshake key pair if
|
||||
* necessary and setting {@code shouldStoreKeys} if a handshake key pair
|
||||
* was generated.
|
||||
*/
|
||||
private Identity loadIdentityWithKeyPair(Transaction txn)
|
||||
throws DbException {
|
||||
Collection<Identity> identities = db.getIdentities(txn);
|
||||
if (identities.size() != 1) throw new DbException();
|
||||
Identity i = identities.iterator().next();
|
||||
LOG.info("Identity loaded");
|
||||
if (i.hasHandshakeKeyPair()) return i;
|
||||
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
|
||||
PublicKey handshakePub = handshakeKeyPair.getPublic();
|
||||
PrivateKey handshakePriv = handshakeKeyPair.getPrivate();
|
||||
LOG.info("Handshake key pair generated");
|
||||
shouldStoreKeys = true;
|
||||
return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv,
|
||||
i.getTimeCreated());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.identity;
|
||||
|
||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -24,8 +25,9 @@ public class IdentityModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
IdentityManager provideIdentityManager(
|
||||
IdentityManager provideIdentityManager(LifecycleManager lifecycleManager,
|
||||
IdentityManagerImpl identityManager) {
|
||||
lifecycleManager.registerOpenDatabaseHook(identityManager);
|
||||
return identityManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.BdfReader;
|
||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||
|
||||
@@ -7,13 +7,11 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Client;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
@@ -28,6 +26,7 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
@@ -49,14 +48,13 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
||||
class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(LifecycleManagerImpl.class.getName());
|
||||
getLogger(LifecycleManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final EventBus eventBus;
|
||||
private final List<Service> services;
|
||||
private final List<Client> clients;
|
||||
private final List<OpenDatabaseHook> openDatabaseHooks;
|
||||
private final List<ExecutorService> executors;
|
||||
private final IdentityManager identityManager;
|
||||
private final Semaphore startStopSemaphore = new Semaphore(1);
|
||||
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch startupLatch = new CountDownLatch(1);
|
||||
@@ -65,13 +63,11 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
private volatile LifecycleState state = STARTING;
|
||||
|
||||
@Inject
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
||||
IdentityManager identityManager) {
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) {
|
||||
this.db = db;
|
||||
this.eventBus = eventBus;
|
||||
this.identityManager = identityManager;
|
||||
services = new CopyOnWriteArrayList<>();
|
||||
clients = new CopyOnWriteArrayList<>();
|
||||
openDatabaseHooks = new CopyOnWriteArrayList<>();
|
||||
executors = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
@@ -83,10 +79,12 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerClient(Client c) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Registering client " + c.getClass().getSimpleName());
|
||||
clients.add(c);
|
||||
public void registerOpenDatabaseHook(OpenDatabaseHook hook) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Registering open database hook "
|
||||
+ hook.getClass().getSimpleName());
|
||||
}
|
||||
openDatabaseHooks.add(hook);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,28 +100,28 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
return ALREADY_RUNNING;
|
||||
}
|
||||
try {
|
||||
LOG.info("Starting services");
|
||||
LOG.info("Opening database");
|
||||
long start = now();
|
||||
|
||||
boolean reopened = db.open(dbKey, this);
|
||||
if (reopened) logDuration(LOG, "Reopening database", start);
|
||||
else logDuration(LOG, "Creating database", start);
|
||||
identityManager.storeLocalAuthor();
|
||||
|
||||
db.transaction(false, txn -> {
|
||||
for (OpenDatabaseHook hook : openDatabaseHooks) {
|
||||
long start1 = now();
|
||||
hook.onDatabaseOpened(txn);
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
logDuration(LOG, "Calling open database hook "
|
||||
+ hook.getClass().getSimpleName(), start1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LOG.info("Starting services");
|
||||
state = STARTING_SERVICES;
|
||||
dbLatch.countDown();
|
||||
eventBus.broadcast(new LifecycleEvent(STARTING_SERVICES));
|
||||
|
||||
db.transaction(false, txn -> {
|
||||
for (Client c : clients) {
|
||||
long start1 = now();
|
||||
c.createLocalState(txn);
|
||||
if (LOG.isLoggable(FINE)) {
|
||||
logDuration(LOG, "Starting client "
|
||||
+ c.getClass().getSimpleName(), start1);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (Service s : services) {
|
||||
start = now();
|
||||
s.startService();
|
||||
|
||||
@@ -96,6 +96,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
TransportConnectionReader r) throws IOException {
|
||||
InputStream streamReader = streamReaderFactory.createStreamReader(
|
||||
r.getInputStream(), ctx);
|
||||
// TODO: Pending contacts, handshake mode
|
||||
return syncSessionFactory.createIncomingSession(ctx.getContactId(),
|
||||
streamReader);
|
||||
}
|
||||
@@ -104,6 +105,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), ctx);
|
||||
// TODO: Pending contacts, handshake mode
|
||||
return syncSessionFactory.createSimplexOutgoingSession(
|
||||
ctx.getContactId(), w.getMaxLatency(), streamWriter);
|
||||
}
|
||||
@@ -112,6 +114,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
TransportConnectionWriter w) throws IOException {
|
||||
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
|
||||
w.getOutputStream(), ctx);
|
||||
// TODO: Pending contacts, handshake mode
|
||||
return syncSessionFactory.createDuplexOutgoingSession(
|
||||
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
|
||||
streamWriter);
|
||||
@@ -145,6 +148,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
disposeReader(false, false);
|
||||
return;
|
||||
}
|
||||
// TODO: Pending contacts
|
||||
ContactId contactId = ctx.getContactId();
|
||||
connectionRegistry.registerConnection(contactId, transportId, true);
|
||||
try {
|
||||
@@ -388,7 +392,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
return;
|
||||
}
|
||||
// Check that the stream comes from the expected contact
|
||||
if (!ctx.getContactId().equals(contactId)) {
|
||||
if (!contactId.equals(ctx.getContactId())) {
|
||||
LOG.warning("Wrong contact ID for returning stream");
|
||||
disposeReader(true, true);
|
||||
return;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class PropertiesModule {
|
||||
ValidationManager validationManager, ContactManager contactManager,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
TransportPropertyManagerImpl transportPropertyManager) {
|
||||
lifecycleManager.registerClient(transportPropertyManager);
|
||||
lifecycleManager.registerOpenDatabaseHook(transportPropertyManager);
|
||||
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
|
||||
transportPropertyManager);
|
||||
contactManager.registerContactHook(transportPropertyManager);
|
||||
|
||||
@@ -13,11 +13,11 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.api.sync.Client;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
@@ -40,7 +40,8 @@ import javax.inject.Inject;
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
Client, ContactHook, ClientVersioningHook, IncomingMessageHook {
|
||||
OpenDatabaseHook, ContactHook, ClientVersioningHook,
|
||||
IncomingMessageHook {
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ClientHelper clientHelper;
|
||||
@@ -67,7 +68,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
public void onDatabaseOpened(Transaction txn) throws DbException {
|
||||
if (db.containsGroup(txn, localGroup.getId())) return;
|
||||
db.addGroup(txn, localGroup);
|
||||
// Set things up for any pre-existing contacts
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -17,8 +18,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||
import org.briarproject.bramble.api.transport.KeyManager;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -28,6 +29,7 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -88,29 +90,53 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
|
||||
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
|
||||
boolean active) throws DbException {
|
||||
Map<TransportId, TransportKeySetId> ids = new HashMap<>();
|
||||
public Map<TransportId, KeySetId> addContactWithRotationKeys(
|
||||
Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
|
||||
boolean alice, boolean active) throws DbException {
|
||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
|
||||
ids.put(t, m.addContactWithRotationKeys(txn, c, rootKey, timestamp,
|
||||
alice, active));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateKeys(Transaction txn, Map<TransportId,
|
||||
TransportKeySetId> keys) throws DbException {
|
||||
for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) {
|
||||
public Map<TransportId, KeySetId> addContactWithHandshakeKeys(
|
||||
Transaction txn, ContactId c, SecretKey rootKey, boolean alice)
|
||||
throws DbException {
|
||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = managers.get(t);
|
||||
if (m == null) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||
} else {
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addContactWithHandshakeKeys(txn, c, rootKey, alice));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addPendingContact(Transaction txn,
|
||||
PendingContactId p, SecretKey rootKey, boolean alice)
|
||||
throws DbException {
|
||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
TransportKeyManager m = e.getValue();
|
||||
ids.put(t, m.addPendingContact(txn, p, rootKey, alice));
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
||||
throws DbException {
|
||||
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
|
||||
withManager(e.getKey(), m -> {
|
||||
m.activateKeys(txn, e.getValue());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,28 +146,34 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
return m != null && m.canSendOutgoingStreams(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSendOutgoingStreams(PendingContactId p, TransportId t) {
|
||||
TransportKeyManager m = managers.get(t);
|
||||
return m != null && m.canSendOutgoingStreams(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamContext getStreamContext(ContactId c, TransportId t)
|
||||
throws DbException {
|
||||
TransportKeyManager m = managers.get(t);
|
||||
if (m == null) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||
return null;
|
||||
}
|
||||
return db.transactionWithNullableResult(false, txn ->
|
||||
m.getStreamContext(txn, c));
|
||||
return withManager(t, m ->
|
||||
db.transactionWithNullableResult(false, txn ->
|
||||
m.getStreamContext(txn, c)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamContext getStreamContext(PendingContactId p, TransportId t)
|
||||
throws DbException {
|
||||
return withManager(t, m ->
|
||||
db.transactionWithNullableResult(false, txn ->
|
||||
m.getStreamContext(txn, p)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamContext getStreamContext(TransportId t, byte[] tag)
|
||||
throws DbException {
|
||||
TransportKeyManager m = managers.get(t);
|
||||
if (m == null) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||
return null;
|
||||
}
|
||||
return db.transactionWithNullableResult(false, txn ->
|
||||
m.getStreamContext(txn, tag));
|
||||
return withManager(t, m ->
|
||||
db.transactionWithNullableResult(false, txn ->
|
||||
m.getStreamContext(txn, tag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,4 +188,20 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
for (TransportKeyManager m : managers.values()) m.removeContact(c);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T withManager(TransportId t, ManagerTask<T> task)
|
||||
throws DbException {
|
||||
TransportKeyManager m = managers.get(t);
|
||||
if (m == null) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
|
||||
return null;
|
||||
}
|
||||
return task.run(m);
|
||||
}
|
||||
|
||||
private interface ManagerTask<T> {
|
||||
@Nullable
|
||||
T run(TransportKeyManager m) throws DbException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
|
||||
class MutableKeySet {
|
||||
|
||||
private final TransportKeySetId keySetId;
|
||||
private final ContactId contactId;
|
||||
private final MutableTransportKeys transportKeys;
|
||||
|
||||
MutableKeySet(TransportKeySetId keySetId, ContactId contactId,
|
||||
MutableTransportKeys transportKeys) {
|
||||
this.keySetId = keySetId;
|
||||
this.contactId = contactId;
|
||||
this.transportKeys = transportKeys;
|
||||
}
|
||||
|
||||
TransportKeySetId getKeySetId() {
|
||||
return keySetId;
|
||||
}
|
||||
|
||||
ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
MutableTransportKeys getTransportKeys() {
|
||||
return transportKeys;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class MutableTransportKeySet {
|
||||
|
||||
private final KeySetId keySetId;
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
@Nullable
|
||||
private final PendingContactId pendingContactId;
|
||||
private final MutableTransportKeys keys;
|
||||
|
||||
MutableTransportKeySet(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
@Nullable PendingContactId pendingContactId,
|
||||
MutableTransportKeys keys) {
|
||||
requireExactlyOneNull(contactId, pendingContactId);
|
||||
this.keySetId = keySetId;
|
||||
this.contactId = contactId;
|
||||
this.pendingContactId = pendingContactId;
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
KeySetId getKeySetId() {
|
||||
return keySetId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
PendingContactId getPendingContactId() {
|
||||
return pendingContactId;
|
||||
}
|
||||
|
||||
MutableTransportKeys getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.briarproject.bramble.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.transport.TransportKeys;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
@@ -13,6 +15,9 @@ class MutableTransportKeys {
|
||||
private final TransportId transportId;
|
||||
private final MutableIncomingKeys inPrev, inCurr, inNext;
|
||||
private final MutableOutgoingKeys outCurr;
|
||||
@Nullable
|
||||
private final SecretKey rootKey;
|
||||
private final boolean alice;
|
||||
|
||||
MutableTransportKeys(TransportKeys k) {
|
||||
transportId = k.getTransportId();
|
||||
@@ -20,11 +25,24 @@ class MutableTransportKeys {
|
||||
inCurr = new MutableIncomingKeys(k.getCurrentIncomingKeys());
|
||||
inNext = new MutableIncomingKeys(k.getNextIncomingKeys());
|
||||
outCurr = new MutableOutgoingKeys(k.getCurrentOutgoingKeys());
|
||||
if (k.isHandshakeMode()) {
|
||||
rootKey = k.getRootKey();
|
||||
alice = k.isAlice();
|
||||
} else {
|
||||
rootKey = null;
|
||||
alice = false;
|
||||
}
|
||||
}
|
||||
|
||||
TransportKeys snapshot() {
|
||||
return new TransportKeys(transportId, inPrev.snapshot(),
|
||||
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
|
||||
if (rootKey == null) {
|
||||
return new TransportKeys(transportId, inPrev.snapshot(),
|
||||
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot());
|
||||
} else {
|
||||
return new TransportKeys(transportId, inPrev.snapshot(),
|
||||
inCurr.snapshot(), inNext.snapshot(), outCurr.snapshot(),
|
||||
rootKey, alice);
|
||||
}
|
||||
}
|
||||
|
||||
TransportId getTransportId() {
|
||||
@@ -46,4 +64,18 @@ class MutableTransportKeys {
|
||||
MutableOutgoingKeys getCurrentOutgoingKeys() {
|
||||
return outCurr;
|
||||
}
|
||||
|
||||
boolean isHandshakeMode() {
|
||||
return rootKey != null;
|
||||
}
|
||||
|
||||
SecretKey getRootKey() {
|
||||
if (rootKey == null) throw new UnsupportedOperationException();
|
||||
return rootKey;
|
||||
}
|
||||
|
||||
boolean isAlice() {
|
||||
if (rootKey == null) throw new UnsupportedOperationException();
|
||||
return alice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.briarproject.bramble.transport;
|
||||
|
||||
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.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -15,20 +16,34 @@ interface TransportKeyManager {
|
||||
|
||||
void start(Transaction txn) throws DbException;
|
||||
|
||||
TransportKeySetId addContact(Transaction txn, ContactId c,
|
||||
KeySetId addContactWithRotationKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||
throws DbException;
|
||||
|
||||
void activateKeys(Transaction txn, TransportKeySetId k) throws DbException;
|
||||
KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, boolean alice) throws DbException;
|
||||
|
||||
KeySetId addPendingContact(Transaction txn, PendingContactId p,
|
||||
SecretKey rootKey, boolean alice) throws DbException;
|
||||
|
||||
void activateKeys(Transaction txn, KeySetId k) throws DbException;
|
||||
|
||||
void removeContact(ContactId c);
|
||||
|
||||
void removePendingContact(PendingContactId p);
|
||||
|
||||
boolean canSendOutgoingStreams(ContactId c);
|
||||
|
||||
boolean canSendOutgoingStreams(PendingContactId p);
|
||||
|
||||
@Nullable
|
||||
StreamContext getStreamContext(Transaction txn, ContactId c)
|
||||
throws DbException;
|
||||
|
||||
@Nullable
|
||||
StreamContext getStreamContext(Transaction txn, PendingContactId p)
|
||||
throws DbException;
|
||||
|
||||
@Nullable
|
||||
StreamContext getStreamContext(Transaction txn, byte[] tag)
|
||||
throws DbException;
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.transport;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
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.TransportCrypto;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -11,9 +12,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.Scheduler;
|
||||
import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.StreamContext;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySet;
|
||||
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.transport.ReorderingWindow.Change;
|
||||
|
||||
@@ -28,10 +29,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireExactlyOneNull;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||
@@ -43,7 +48,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(TransportKeyManagerImpl.class.getName());
|
||||
getLogger(TransportKeyManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final TransportCrypto transportCrypto;
|
||||
@@ -55,10 +60,16 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
// The following are locking: lock
|
||||
private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>();
|
||||
@GuardedBy("lock")
|
||||
private final Map<KeySetId, MutableTransportKeySet> keys = new HashMap<>();
|
||||
@GuardedBy("lock")
|
||||
private final Map<Bytes, TagContext> inContexts = new HashMap<>();
|
||||
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
|
||||
@GuardedBy("lock")
|
||||
private final Map<ContactId, MutableTransportKeySet>
|
||||
contactOutContexts = new HashMap<>();
|
||||
@GuardedBy("lock")
|
||||
private final Map<PendingContactId, MutableTransportKeySet>
|
||||
pendingContactOutContexts = new HashMap<>();
|
||||
|
||||
TransportKeyManagerImpl(DatabaseComponent db,
|
||||
TransportCrypto transportCrypto, Executor dbExecutor,
|
||||
@@ -82,62 +93,71 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
// Load the transport keys from the DB
|
||||
Collection<TransportKeySet> loaded =
|
||||
db.getTransportKeys(txn, transportId);
|
||||
// Rotate the keys to the current time period
|
||||
RotationResult rotationResult = rotateKeys(loaded, now);
|
||||
// Update the keys to the current time period
|
||||
UpdateResult updateResult = updateKeys(loaded, now);
|
||||
// Initialise mutable state for all contacts
|
||||
addKeys(rotationResult.current);
|
||||
// Write any rotated keys back to the DB
|
||||
if (!rotationResult.rotated.isEmpty())
|
||||
db.updateTransportKeys(txn, rotationResult.rotated);
|
||||
addKeys(updateResult.current);
|
||||
// Write any updated keys back to the DB
|
||||
if (!updateResult.updated.isEmpty())
|
||||
db.updateTransportKeys(txn, updateResult.updated);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
// Schedule the next key rotation
|
||||
scheduleKeyRotation(now);
|
||||
// Schedule the next key update
|
||||
scheduleKeyUpdate(now);
|
||||
}
|
||||
|
||||
private RotationResult rotateKeys(Collection<TransportKeySet> keys,
|
||||
private UpdateResult updateKeys(Collection<TransportKeySet> keys,
|
||||
long now) {
|
||||
RotationResult rotationResult = new RotationResult();
|
||||
UpdateResult updateResult = new UpdateResult();
|
||||
long timePeriod = now / timePeriodLength;
|
||||
for (TransportKeySet ks : keys) {
|
||||
TransportKeys k = ks.getKeys();
|
||||
TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
|
||||
TransportKeys k1 = transportCrypto.updateTransportKeys(k,
|
||||
timePeriod);
|
||||
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
|
||||
ks.getContactId(), k1);
|
||||
ks.getContactId(), null, k1);
|
||||
if (k1.getTimePeriod() > k.getTimePeriod())
|
||||
rotationResult.rotated.add(ks1);
|
||||
rotationResult.current.add(ks1);
|
||||
updateResult.updated.add(ks1);
|
||||
updateResult.current.add(ks1);
|
||||
}
|
||||
return rotationResult;
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
// Locking: lock
|
||||
@GuardedBy("lock")
|
||||
private void addKeys(Collection<TransportKeySet> keys) {
|
||||
for (TransportKeySet ks : keys) {
|
||||
addKeys(ks.getKeySetId(), ks.getContactId(),
|
||||
ks.getPendingContactId(),
|
||||
new MutableTransportKeys(ks.getKeys()));
|
||||
}
|
||||
}
|
||||
|
||||
// Locking: lock
|
||||
private void addKeys(TransportKeySetId keySetId, ContactId contactId,
|
||||
MutableTransportKeys m) {
|
||||
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
|
||||
keys.put(keySetId, ks);
|
||||
encodeTags(keySetId, contactId, m.getPreviousIncomingKeys());
|
||||
encodeTags(keySetId, contactId, m.getCurrentIncomingKeys());
|
||||
encodeTags(keySetId, contactId, m.getNextIncomingKeys());
|
||||
@GuardedBy("lock")
|
||||
private void addKeys(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
@Nullable PendingContactId pendingContactId,
|
||||
MutableTransportKeys keys) {
|
||||
requireExactlyOneNull(contactId, pendingContactId);
|
||||
MutableTransportKeySet ks = new MutableTransportKeySet(keySetId,
|
||||
contactId, pendingContactId, keys);
|
||||
this.keys.put(keySetId, ks);
|
||||
boolean handshakeMode = keys.isHandshakeMode();
|
||||
encodeTags(keySetId, contactId, pendingContactId,
|
||||
keys.getPreviousIncomingKeys(), handshakeMode);
|
||||
encodeTags(keySetId, contactId, pendingContactId,
|
||||
keys.getCurrentIncomingKeys(), handshakeMode);
|
||||
encodeTags(keySetId, contactId, pendingContactId,
|
||||
keys.getNextIncomingKeys(), handshakeMode);
|
||||
considerReplacingOutgoingKeys(ks);
|
||||
}
|
||||
|
||||
// Locking: lock
|
||||
private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
|
||||
MutableIncomingKeys inKeys) {
|
||||
@GuardedBy("lock")
|
||||
private void encodeTags(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
@Nullable PendingContactId pendingContactId,
|
||||
MutableIncomingKeys inKeys, boolean handshakeMode) {
|
||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||
TagContext tagCtx =
|
||||
new TagContext(keySetId, contactId, inKeys, streamNumber);
|
||||
TagContext tagCtx = new TagContext(keySetId, contactId,
|
||||
pendingContactId, inKeys, streamNumber, handshakeMode);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||
streamNumber);
|
||||
@@ -145,27 +165,41 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Locking: lock
|
||||
private void considerReplacingOutgoingKeys(MutableKeySet ks) {
|
||||
// Use the active outgoing keys with the highest key set ID
|
||||
if (ks.getTransportKeys().getCurrentOutgoingKeys().isActive()) {
|
||||
MutableKeySet old = outContexts.get(ks.getContactId());
|
||||
if (old == null ||
|
||||
@GuardedBy("lock")
|
||||
private void considerReplacingOutgoingKeys(MutableTransportKeySet ks) {
|
||||
// Use the active outgoing keys with the highest key set ID, preferring
|
||||
// rotation keys to handshake keys
|
||||
if (ks.getKeys().getCurrentOutgoingKeys().isActive()) {
|
||||
MutableTransportKeySet old = getOutgoingKeySet(ks.getContactId(),
|
||||
ks.getPendingContactId());
|
||||
if (old == null || (old.getKeys().isHandshakeMode() &&
|
||||
!ks.getKeys().isHandshakeMode()) ||
|
||||
old.getKeySetId().getInt() < ks.getKeySetId().getInt()) {
|
||||
outContexts.put(ks.getContactId(), ks);
|
||||
if (ks.getContactId() == null)
|
||||
pendingContactOutContexts.put(ks.getPendingContactId(), ks);
|
||||
else contactOutContexts.put(ks.getContactId(), ks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleKeyRotation(long now) {
|
||||
long delay = timePeriodLength - now % timePeriodLength;
|
||||
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
private MutableTransportKeySet getOutgoingKeySet(@Nullable ContactId c,
|
||||
@Nullable PendingContactId p) {
|
||||
requireExactlyOneNull(c, p);
|
||||
if (c == null) return pendingContactOutContexts.get(p);
|
||||
else return contactOutContexts.get(c);
|
||||
}
|
||||
|
||||
private void rotateKeys() {
|
||||
private void scheduleKeyUpdate(long now) {
|
||||
long delay = timePeriodLength - now % timePeriodLength;
|
||||
scheduler.schedule((Runnable) this::updateKeys, delay, MILLISECONDS);
|
||||
}
|
||||
|
||||
private void updateKeys() {
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
db.transaction(false, this::rotateKeys);
|
||||
db.transaction(false, this::updateKeys);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
@@ -173,7 +207,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportKeySetId addContact(Transaction txn, ContactId c,
|
||||
public KeySetId addContactWithRotationKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||
throws DbException {
|
||||
lock.lock();
|
||||
@@ -181,15 +215,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
// Work out what time period the timestamp belongs to
|
||||
long timePeriod = timestamp / timePeriodLength;
|
||||
// Derive the transport keys
|
||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||
TransportKeys k = transportCrypto.deriveRotationKeys(transportId,
|
||||
rootKey, timePeriod, alice, active);
|
||||
// Rotate the keys to the current time period if necessary
|
||||
// Update the keys to the current time period if necessary
|
||||
timePeriod = clock.currentTimeMillis() / timePeriodLength;
|
||||
k = transportCrypto.rotateTransportKeys(k, timePeriod);
|
||||
k = transportCrypto.updateTransportKeys(k, timePeriod);
|
||||
// Write the keys back to the DB
|
||||
TransportKeySetId keySetId = db.addTransportKeys(txn, c, k);
|
||||
KeySetId keySetId = db.addTransportKeys(txn, c, k);
|
||||
// Initialise mutable state for the contact
|
||||
addKeys(keySetId, c, new MutableTransportKeys(k));
|
||||
addKeys(keySetId, c, null, new MutableTransportKeys(k));
|
||||
return keySetId;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
@@ -197,13 +231,52 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateKeys(Transaction txn, TransportKeySetId k)
|
||||
throws DbException {
|
||||
public KeySetId addContactWithHandshakeKeys(Transaction txn, ContactId c,
|
||||
SecretKey rootKey, boolean alice) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
MutableKeySet ks = keys.get(k);
|
||||
// Work out what time period we're in
|
||||
long timePeriod = clock.currentTimeMillis() / timePeriodLength;
|
||||
// Derive the transport keys
|
||||
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||
rootKey, timePeriod, alice);
|
||||
// Write the keys back to the DB
|
||||
KeySetId keySetId = db.addTransportKeys(txn, c, k);
|
||||
// Initialise mutable state for the contact
|
||||
addKeys(keySetId, c, null, new MutableTransportKeys(k));
|
||||
return keySetId;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeySetId addPendingContact(Transaction txn, PendingContactId p,
|
||||
SecretKey rootKey, boolean alice) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
// Work out what time period we're in
|
||||
long timePeriod = clock.currentTimeMillis() / timePeriodLength;
|
||||
// Derive the transport keys
|
||||
TransportKeys k = transportCrypto.deriveHandshakeKeys(transportId,
|
||||
rootKey, timePeriod, alice);
|
||||
// Write the keys back to the DB
|
||||
KeySetId keySetId = db.addTransportKeys(txn, p, k);
|
||||
// Initialise mutable state for the pending contact
|
||||
addKeys(keySetId, null, p, new MutableTransportKeys(k));
|
||||
return keySetId;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
MutableTransportKeySet ks = keys.get(k);
|
||||
if (ks == null) throw new IllegalArgumentException();
|
||||
MutableTransportKeys m = ks.getTransportKeys();
|
||||
MutableTransportKeys m = ks.getKeys();
|
||||
m.getCurrentOutgoingKeys().activate();
|
||||
considerReplacingOutgoingKeys(ks);
|
||||
db.setTransportKeysActive(txn, m.getTransportId(), k);
|
||||
@@ -218,13 +291,29 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
try {
|
||||
// Remove mutable state for the contact
|
||||
Iterator<TagContext> it = inContexts.values().iterator();
|
||||
while (it.hasNext()) if (it.next().contactId.equals(c)) it.remove();
|
||||
outContexts.remove(c);
|
||||
Iterator<MutableKeySet> it1 = keys.values().iterator();
|
||||
while (it1.hasNext()) {
|
||||
ContactId c1 = it1.next().getContactId();
|
||||
if (c1 != null && c1.equals(c)) it1.remove();
|
||||
}
|
||||
while (it.hasNext())
|
||||
if (c.equals(it.next().contactId)) it.remove();
|
||||
contactOutContexts.remove(c);
|
||||
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
|
||||
while (it1.hasNext())
|
||||
if (c.equals(it1.next().getContactId())) it1.remove();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePendingContact(PendingContactId p) {
|
||||
lock.lock();
|
||||
try {
|
||||
// Remove mutable state for the pending contact
|
||||
Iterator<TagContext> it = inContexts.values().iterator();
|
||||
while (it.hasNext())
|
||||
if (p.equals(it.next().pendingContactId)) it.remove();
|
||||
pendingContactOutContexts.remove(p);
|
||||
Iterator<MutableTransportKeySet> it1 = keys.values().iterator();
|
||||
while (it1.hasNext())
|
||||
if (p.equals(it1.next().getPendingContactId())) it1.remove();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
@@ -232,12 +321,21 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
|
||||
@Override
|
||||
public boolean canSendOutgoingStreams(ContactId c) {
|
||||
return canSendOutgoingStreams(c, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSendOutgoingStreams(PendingContactId p) {
|
||||
return canSendOutgoingStreams(null, p);
|
||||
}
|
||||
|
||||
private boolean canSendOutgoingStreams(@Nullable ContactId c,
|
||||
@Nullable PendingContactId p) {
|
||||
lock.lock();
|
||||
try {
|
||||
MutableKeySet ks = outContexts.get(c);
|
||||
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
|
||||
if (ks == null) return false;
|
||||
MutableOutgoingKeys outKeys =
|
||||
ks.getTransportKeys().getCurrentOutgoingKeys();
|
||||
MutableOutgoingKeys outKeys = ks.getKeys().getCurrentOutgoingKeys();
|
||||
if (!outKeys.isActive()) throw new AssertionError();
|
||||
return outKeys.getStreamCounter() <= MAX_32_BIT_UNSIGNED;
|
||||
} finally {
|
||||
@@ -248,19 +346,32 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
@Override
|
||||
public StreamContext getStreamContext(Transaction txn, ContactId c)
|
||||
throws DbException {
|
||||
return getStreamContext(txn, c, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamContext getStreamContext(Transaction txn, PendingContactId p)
|
||||
throws DbException {
|
||||
return getStreamContext(txn, null, p);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private StreamContext getStreamContext(Transaction txn,
|
||||
@Nullable ContactId c, @Nullable PendingContactId p)
|
||||
throws DbException {
|
||||
lock.lock();
|
||||
try {
|
||||
// Look up the outgoing keys for the contact
|
||||
MutableKeySet ks = outContexts.get(c);
|
||||
MutableTransportKeySet ks = getOutgoingKeySet(c, p);
|
||||
if (ks == null) return null;
|
||||
MutableOutgoingKeys outKeys =
|
||||
ks.getTransportKeys().getCurrentOutgoingKeys();
|
||||
MutableTransportKeys keys = ks.getKeys();
|
||||
MutableOutgoingKeys outKeys = keys.getCurrentOutgoingKeys();
|
||||
if (!outKeys.isActive()) throw new AssertionError();
|
||||
if (outKeys.getStreamCounter() > MAX_32_BIT_UNSIGNED) return null;
|
||||
// Create a stream context
|
||||
StreamContext ctx = new StreamContext(c, transportId,
|
||||
StreamContext ctx = new StreamContext(c, p, transportId,
|
||||
outKeys.getTagKey(), outKeys.getHeaderKey(),
|
||||
outKeys.getStreamCounter());
|
||||
outKeys.getStreamCounter(), keys.isHandshakeMode());
|
||||
// Increment the stream counter and write it back to the DB
|
||||
outKeys.incrementStreamCounter();
|
||||
db.incrementStreamCounter(txn, transportId, ks.getKeySetId());
|
||||
@@ -280,9 +391,10 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
if (tagCtx == null) return null;
|
||||
MutableIncomingKeys inKeys = tagCtx.inKeys;
|
||||
// Create a stream context
|
||||
StreamContext ctx = new StreamContext(tagCtx.contactId, transportId,
|
||||
StreamContext ctx = new StreamContext(tagCtx.contactId,
|
||||
tagCtx.pendingContactId, transportId,
|
||||
inKeys.getTagKey(), inKeys.getHeaderKey(),
|
||||
tagCtx.streamNumber);
|
||||
tagCtx.streamNumber, tagCtx.handshakeMode);
|
||||
// Update the reordering window
|
||||
ReorderingWindow window = inKeys.getWindow();
|
||||
Change change = window.setSeen(tagCtx.streamNumber);
|
||||
@@ -292,7 +404,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
|
||||
PROTOCOL_VERSION, streamNumber);
|
||||
TagContext tagCtx1 = new TagContext(tagCtx.keySetId,
|
||||
tagCtx.contactId, inKeys, streamNumber);
|
||||
tagCtx.contactId, tagCtx.pendingContactId, inKeys,
|
||||
streamNumber, tagCtx.handshakeMode);
|
||||
inContexts.put(new Bytes(addTag), tagCtx1);
|
||||
}
|
||||
// Remove tags for any stream numbers removed from the window
|
||||
@@ -308,9 +421,9 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
inKeys.getTimePeriod(), window.getBase(),
|
||||
window.getBitmap());
|
||||
// If the outgoing keys are inactive, activate them
|
||||
MutableKeySet ks = keys.get(tagCtx.keySetId);
|
||||
MutableTransportKeySet ks = keys.get(tagCtx.keySetId);
|
||||
MutableOutgoingKeys outKeys =
|
||||
ks.getTransportKeys().getCurrentOutgoingKeys();
|
||||
ks.getKeys().getCurrentOutgoingKeys();
|
||||
if (!outKeys.isActive()) {
|
||||
LOG.info("Activating outgoing keys");
|
||||
outKeys.activate();
|
||||
@@ -323,51 +436,62 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateKeys(Transaction txn) throws DbException {
|
||||
private void updateKeys(Transaction txn) throws DbException {
|
||||
long now = clock.currentTimeMillis();
|
||||
lock.lock();
|
||||
try {
|
||||
// Rotate the keys to the current time period
|
||||
// Update the keys to the current time period
|
||||
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
|
||||
for (MutableKeySet ks : keys.values()) {
|
||||
for (MutableTransportKeySet ks : keys.values()) {
|
||||
snapshot.add(new TransportKeySet(ks.getKeySetId(),
|
||||
ks.getContactId(), ks.getTransportKeys().snapshot()));
|
||||
ks.getContactId(), ks.getPendingContactId(),
|
||||
ks.getKeys().snapshot()));
|
||||
}
|
||||
RotationResult rotationResult = rotateKeys(snapshot, now);
|
||||
UpdateResult updateResult = updateKeys(snapshot, now);
|
||||
// Rebuild the mutable state for all contacts
|
||||
inContexts.clear();
|
||||
outContexts.clear();
|
||||
contactOutContexts.clear();
|
||||
pendingContactOutContexts.clear();
|
||||
keys.clear();
|
||||
addKeys(rotationResult.current);
|
||||
// Write any rotated keys back to the DB
|
||||
if (!rotationResult.rotated.isEmpty())
|
||||
db.updateTransportKeys(txn, rotationResult.rotated);
|
||||
addKeys(updateResult.current);
|
||||
// Write any updated keys back to the DB
|
||||
if (!updateResult.updated.isEmpty())
|
||||
db.updateTransportKeys(txn, updateResult.updated);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
// Schedule the next key rotation
|
||||
scheduleKeyRotation(now);
|
||||
// Schedule the next key update
|
||||
scheduleKeyUpdate(now);
|
||||
}
|
||||
|
||||
private static class TagContext {
|
||||
|
||||
private final TransportKeySetId keySetId;
|
||||
private final KeySetId keySetId;
|
||||
@Nullable
|
||||
private final ContactId contactId;
|
||||
@Nullable
|
||||
private final PendingContactId pendingContactId;
|
||||
private final MutableIncomingKeys inKeys;
|
||||
private final long streamNumber;
|
||||
private final boolean handshakeMode;
|
||||
|
||||
private TagContext(TransportKeySetId keySetId, ContactId contactId,
|
||||
MutableIncomingKeys inKeys, long streamNumber) {
|
||||
private TagContext(KeySetId keySetId, @Nullable ContactId contactId,
|
||||
@Nullable PendingContactId pendingContactId,
|
||||
MutableIncomingKeys inKeys, long streamNumber,
|
||||
boolean handshakeMode) {
|
||||
requireExactlyOneNull(contactId, pendingContactId);
|
||||
this.keySetId = keySetId;
|
||||
this.contactId = contactId;
|
||||
this.pendingContactId = pendingContactId;
|
||||
this.inKeys = inKeys;
|
||||
this.streamNumber = streamNumber;
|
||||
this.handshakeMode = handshakeMode;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RotationResult {
|
||||
private static class UpdateResult {
|
||||
|
||||
private final Collection<TransportKeySet> current = new ArrayList<>();
|
||||
private final Collection<TransportKeySet> rotated = new ArrayList<>();
|
||||
private final Collection<TransportKeySet> updated = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Client;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
||||
@@ -53,8 +53,8 @@ import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_
|
||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
||||
|
||||
@NotNullByDefault
|
||||
class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
|
||||
Service, ContactHook, IncomingMessageHook {
|
||||
class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||
Service, OpenDatabaseHook, ContactHook, IncomingMessageHook {
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ClientHelper clientHelper;
|
||||
@@ -124,7 +124,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
public void onDatabaseOpened(Transaction txn) throws DbException {
|
||||
if (db.containsGroup(txn, localGroup.getId())) return;
|
||||
db.addGroup(txn, localGroup);
|
||||
// Set things up for any pre-existing contacts
|
||||
|
||||
@@ -34,7 +34,7 @@ public class VersioningModule {
|
||||
ClientVersioningManagerImpl clientVersioningManager,
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
ValidationManager validationManager) {
|
||||
lifecycleManager.registerClient(clientVersioningManager);
|
||||
lifecycleManager.registerOpenDatabaseHook(clientVersioningManager);
|
||||
lifecycleManager.registerService(clientVersioningManager);
|
||||
contactManager.registerContactHook(clientVersioningManager);
|
||||
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user