mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
320 Commits
831_refact
...
elliptic-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b38c33bf58 | ||
|
|
5bb00597ef | ||
|
|
5d528fce74 | ||
|
|
c80edc99b2 | ||
|
|
33378d9920 | ||
|
|
85a6e394b9 | ||
|
|
f2f98f28a3 | ||
|
|
d92e042971 | ||
|
|
6d6e47409f | ||
|
|
0084e51263 | ||
|
|
32e0b39771 | ||
|
|
7bb51f77ec | ||
|
|
c777a57a7d | ||
|
|
def5966767 | ||
|
|
14b18e9d42 | ||
|
|
fcff8d92f3 | ||
|
|
ea0e00f4ac | ||
|
|
f199105f6c | ||
|
|
b23c0b599b | ||
|
|
0327d4f38a | ||
|
|
4397a45519 | ||
|
|
365e159539 | ||
|
|
8171dd8bc9 | ||
|
|
c4beb60c22 | ||
|
|
4b88f0d9f1 | ||
|
|
116419f505 | ||
|
|
87b2624aa8 | ||
|
|
71fe6f3148 | ||
|
|
21df6cb809 | ||
|
|
1f0c385a5c | ||
|
|
986ea05fb2 | ||
|
|
90e395506f | ||
|
|
cf54360a93 | ||
|
|
a5d4ea4477 | ||
|
|
030b52261d | ||
|
|
a50e13c2e3 | ||
|
|
c8326103b4 | ||
|
|
0f2beee813 | ||
|
|
d2348a4e7d | ||
|
|
cc87e6fd1f | ||
|
|
1843aea2a7 | ||
|
|
9f7021acd3 | ||
|
|
ddea031cbf | ||
|
|
f0d8532f71 | ||
|
|
4883d157dc | ||
|
|
a1bec1e927 | ||
|
|
48918f4727 | ||
|
|
303b5bd395 | ||
|
|
37d4d79c64 | ||
|
|
05bc3f6a71 | ||
|
|
8b3960781a | ||
|
|
97733a52c8 | ||
|
|
89dcbec599 | ||
|
|
6497809fe1 | ||
|
|
f3de4f53c5 | ||
|
|
166fc2948c | ||
|
|
9f3a63d8c4 | ||
|
|
748fa77d94 | ||
|
|
4ca86ee4eb | ||
|
|
ec2f372933 | ||
|
|
4267800db2 | ||
|
|
bb8cb9bcbb | ||
|
|
d5b9e15ee1 | ||
|
|
43ee3246f6 | ||
|
|
b56724dee5 | ||
|
|
92748ac872 | ||
|
|
b89686c287 | ||
|
|
a34692630b | ||
|
|
735208562a | ||
|
|
49826fdc56 | ||
|
|
e8c54a609c | ||
|
|
ece2c51358 | ||
|
|
3ec8af4661 | ||
|
|
77a08596fe | ||
|
|
879f699b2b | ||
|
|
d7383a3361 | ||
|
|
a5b321a93b | ||
|
|
5fa6b0ca1c | ||
|
|
27328afe3c | ||
|
|
2d26af1ae2 | ||
|
|
6db8f33e8f | ||
|
|
d6a7e6d52c | ||
|
|
df99b3b666 | ||
|
|
0f1c9f4fe2 | ||
|
|
5dcd5f79dc | ||
|
|
8a81171739 | ||
|
|
1c4f20f76f | ||
|
|
f84fa588f6 | ||
|
|
e30e34f342 | ||
|
|
fc93ced067 | ||
|
|
bb7df72d31 | ||
|
|
f8425658e4 | ||
|
|
53c8cf09b6 | ||
|
|
9f29bf4949 | ||
|
|
98e2adf794 | ||
|
|
2a43e0b0ed | ||
|
|
773ae73820 | ||
|
|
009db57bc5 | ||
|
|
5e98126e77 | ||
|
|
bd7ebfd83a | ||
|
|
10f41ef157 | ||
|
|
1dd4960109 | ||
|
|
75413b6c86 | ||
|
|
b2180582a7 | ||
|
|
8211ce7ae3 | ||
|
|
e6b1597fa7 | ||
|
|
8937d3cd9c | ||
|
|
51f320d147 | ||
|
|
e402a894bb | ||
|
|
9b577f1219 | ||
|
|
220f678403 | ||
|
|
4173fc4daa | ||
|
|
c6756d2145 | ||
|
|
6731f6eeb5 | ||
|
|
6f7f8b40e3 | ||
|
|
1a83b2c99b | ||
|
|
f641fae1c7 | ||
|
|
deb43d9872 | ||
|
|
cee4e1305e | ||
|
|
a1f989c43c | ||
|
|
b67abadbac | ||
|
|
8c29c85696 | ||
|
|
4fe4c298d7 | ||
|
|
13d35229d5 | ||
|
|
f0137b41b6 | ||
|
|
b221d21903 | ||
|
|
8bac202626 | ||
|
|
973151c949 | ||
|
|
ed26ab78a5 | ||
|
|
8454b2d235 | ||
|
|
91d0f89f60 | ||
|
|
e074672e86 | ||
|
|
6c1901fe5b | ||
|
|
49052be627 | ||
|
|
5b5b540630 | ||
|
|
9993bac3a1 | ||
|
|
3c95988693 | ||
|
|
fc5c3b470e | ||
|
|
53f05a72ba | ||
|
|
2c10ae7d06 | ||
|
|
6b9010c557 | ||
|
|
1bf0fdfa81 | ||
|
|
237759aac0 | ||
|
|
2a141e0a97 | ||
|
|
d6900be68e | ||
|
|
a35d7c7204 | ||
|
|
86287f9241 | ||
|
|
0b2e3dd96f | ||
|
|
90aa1d1ce7 | ||
|
|
ef2286ab53 | ||
|
|
47b25f3221 | ||
|
|
c30bfa12ce | ||
|
|
d0fc04251d | ||
|
|
dcbb41eb7a | ||
|
|
5c51259269 | ||
|
|
7eefa07052 | ||
|
|
999bdf8866 | ||
|
|
911c0c0fd9 | ||
|
|
99d8cc64a6 | ||
|
|
ba727d7568 | ||
|
|
ed01048f9f | ||
|
|
043ee3c58e | ||
|
|
6e0af7deda | ||
|
|
9591db2097 | ||
|
|
329a4c64f6 | ||
|
|
79015bc5ae | ||
|
|
27422ab9f9 | ||
|
|
abcb682498 | ||
|
|
5044127c46 | ||
|
|
0e4b8ca62e | ||
|
|
822017c69c | ||
|
|
eb6561b93d | ||
|
|
eb9d0c00a8 | ||
|
|
d24b1884a2 | ||
|
|
078534889e | ||
|
|
e92713006a | ||
|
|
18f43f3bc1 | ||
|
|
a4118b40e1 | ||
|
|
de29fbc324 | ||
|
|
3197dcf9b5 | ||
|
|
35aad409fd | ||
|
|
08ce6a7331 | ||
|
|
33a0099065 | ||
|
|
34d20fafda | ||
|
|
aafddcd0f0 | ||
|
|
0d6983b4ef | ||
|
|
69bfb72171 | ||
|
|
1aa33ec9b2 | ||
|
|
6702df1e22 | ||
|
|
c1748c9a86 | ||
|
|
9df624c62a | ||
|
|
0ee6197d7f | ||
|
|
b03a7dce3e | ||
|
|
6c59d7dd5f | ||
|
|
050191f0ef | ||
|
|
4b5a19ce5d | ||
|
|
7c4dd991b9 | ||
|
|
8455569e88 | ||
|
|
d25676559c | ||
|
|
a9437f7985 | ||
|
|
8141a97fc9 | ||
|
|
db842bd7e4 | ||
|
|
6dbec3a864 | ||
|
|
29f658cf4d | ||
|
|
ca83744a84 | ||
|
|
d91a9e2be4 | ||
|
|
8408c3f467 | ||
|
|
544c83a64c | ||
|
|
3800cd5e4f | ||
|
|
259f2cd419 | ||
|
|
20eb022c36 | ||
|
|
531e555b52 | ||
|
|
a9024aa34b | ||
|
|
d4e3b7842c | ||
|
|
167fddfbcc | ||
|
|
a48d642648 | ||
|
|
9a70f054c7 | ||
|
|
ca43d13bd6 | ||
|
|
5b71004179 | ||
|
|
63befccdbf | ||
|
|
4ecf7c02d0 | ||
|
|
f25badc18c | ||
|
|
6e931e9ba5 | ||
|
|
7e749124bf | ||
|
|
5822eb7808 | ||
|
|
7a7e086541 | ||
|
|
abab3167c2 | ||
|
|
8d08570568 | ||
|
|
2007078f13 | ||
|
|
dfb71a7978 | ||
|
|
480b0e3a03 | ||
|
|
8f8751f4ac | ||
|
|
de2ea112ee | ||
|
|
6f99a53fd9 | ||
|
|
a8a9b9032d | ||
|
|
6b15fb89de | ||
|
|
a711d6b8a1 | ||
|
|
5678f8aaa4 | ||
|
|
2fe37f6c26 | ||
|
|
a879747968 | ||
|
|
95e8fd7ee0 | ||
|
|
4416aaaa4c | ||
|
|
500d5f0efe | ||
|
|
fc8978fd90 | ||
|
|
73df126bd4 | ||
|
|
9146488c7d | ||
|
|
613a7fe376 | ||
|
|
ecb62f00d4 | ||
|
|
c4540a03cd | ||
|
|
3e31da99b5 | ||
|
|
098c1d0b1e | ||
|
|
178e908c86 | ||
|
|
ecf7cf14ae | ||
|
|
09e2a15a73 | ||
|
|
ab387860a6 | ||
|
|
f63fc94f2b | ||
|
|
41e5928cca | ||
|
|
8303175494 | ||
|
|
151eb6935b | ||
|
|
6a419c0c7b | ||
|
|
1795b32121 | ||
|
|
01971768ce | ||
|
|
ef7483ab01 | ||
|
|
527d11473d | ||
|
|
775dadc9a0 | ||
|
|
800b10a988 | ||
|
|
c977bf047d | ||
|
|
660a25f21d | ||
|
|
e7fd6d23af | ||
|
|
46982897f0 | ||
|
|
d24de68d64 | ||
|
|
7514c46a3f | ||
|
|
6632c0f8e3 | ||
|
|
79aafcda69 | ||
|
|
05af21e8dc | ||
|
|
0dc62cbbdc | ||
|
|
f3a084cfd2 | ||
|
|
8b32f82566 | ||
|
|
d598b6ed44 | ||
|
|
f5dc6f24b9 | ||
|
|
37454392da | ||
|
|
de7f9111d3 | ||
|
|
96d2889a6c | ||
|
|
f6412d1e9a | ||
|
|
b377cd6b1c | ||
|
|
f6cdbda5bb | ||
|
|
855c600a3e | ||
|
|
ea6e8303b0 | ||
|
|
d4934040d9 | ||
|
|
3449677b24 | ||
|
|
1ad3a6646e | ||
|
|
2d10f6b2bd | ||
|
|
5b05424d83 | ||
|
|
0826022d82 | ||
|
|
a901bfb9cb | ||
|
|
03cdce122a | ||
|
|
f2e0e16969 | ||
|
|
0c441e2ff3 | ||
|
|
21302304a5 | ||
|
|
6839d8b844 | ||
|
|
aee65a716c | ||
|
|
6a07d8f2c9 | ||
|
|
3c1ea81cd0 | ||
|
|
025f417bc7 | ||
|
|
c9dcd906c9 | ||
|
|
7024e04d15 | ||
|
|
0b8ac947db | ||
|
|
948410a064 | ||
|
|
2841339cac | ||
|
|
e8e82bd805 | ||
|
|
6876f40a0e | ||
|
|
5f4e1ecdfd | ||
|
|
044719432a | ||
|
|
d1a929da85 | ||
|
|
2a8978a60d | ||
|
|
c0afad7a26 | ||
|
|
37281c6c23 | ||
|
|
76a5e25656 | ||
|
|
3575b74837 | ||
|
|
f1c7996960 |
29
.gitlab-ci.yml
Normal file
29
.gitlab-ci.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
image: registry.gitlab.com/fdroid/ci-images-base:latest
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .gradle/wrapper
|
||||||
|
- .gradle/caches
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- set -e
|
||||||
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
# Accept the license for the Android build tools
|
||||||
|
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;26.0.2"
|
||||||
|
# Download OpenJDK 6 so we can compile against its standard library
|
||||||
|
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
|
||||||
|
- if [ ! -d openjdk ]
|
||||||
|
- then
|
||||||
|
- wget -q http://ftp.uk.debian.org/debian/pool/main/o/openjdk-6/$JDK_FILE
|
||||||
|
- dpkg-deb -x $JDK_FILE openjdk
|
||||||
|
- fi
|
||||||
|
- export JAVA_6_HOME=$PWD/openjdk/usr/lib/jvm/java-6-openjdk-amd64
|
||||||
|
|
||||||
|
test:
|
||||||
|
script:
|
||||||
|
- ./gradlew test
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
# this file changes every time but should not be cached
|
||||||
|
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||||
|
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||||
@@ -6,99 +6,91 @@ apply plugin: 'witness'
|
|||||||
apply plugin: 'de.undercouch.download'
|
apply plugin: 'de.undercouch.download'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 27
|
||||||
buildToolsVersion "23.0.3"
|
buildToolsVersion '26.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 22
|
targetSdkVersion 26
|
||||||
versionCode 1
|
versionCode 1700
|
||||||
versionName "1.0"
|
versionName "0.17.0"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_7
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_7
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-core')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||||
provided 'javax.annotation:jsr250-api:1.0'
|
|
||||||
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
|
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
def torBinaryDir = 'src/main/res/raw'
|
dependencyVerification {
|
||||||
|
verify = [
|
||||||
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
|
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||||
|
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||||
|
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
|
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
|
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||||
|
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||||
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
|
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
|
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
|
||||||
|
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
|
||||||
|
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
|
||||||
|
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
|
||||||
|
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
|
||||||
|
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
task downloadTorGeoIp(type: Download) {
|
ext.torBinaryDir = 'src/main/res/raw'
|
||||||
src 'https://briarproject.org/build/geoip-2015-12-01.zip'
|
ext.torVersion = '0.2.9.12'
|
||||||
dest "$torBinaryDir/geoip.zip"
|
ext.geoipVersion = '2017-09-06'
|
||||||
|
ext.torDownloadUrl = 'https://briarproject.org/build/'
|
||||||
|
|
||||||
|
def torBinaries = [
|
||||||
|
"tor_arm" : '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688',
|
||||||
|
"tor_arm_pie": '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283',
|
||||||
|
"tor_x86" : '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab',
|
||||||
|
"tor_x86_pie": '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288',
|
||||||
|
"geoip" : 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
|
||||||
|
]
|
||||||
|
|
||||||
|
def downloadBinary(name) {
|
||||||
|
return tasks.create("downloadBinary${name}", Download) {
|
||||||
|
src "${torDownloadUrl}${name}.zip"
|
||||||
|
.replace('tor_', "tor-${torVersion}-")
|
||||||
|
.replace('geoip', "geoip-${geoipVersion}")
|
||||||
|
.replaceAll('_', '-')
|
||||||
|
dest "${torBinaryDir}/${name}.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryArm(type: Download) {
|
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-arm.zip'
|
|
||||||
dest "$torBinaryDir/tor_arm.zip"
|
|
||||||
onlyIfNewer true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryArmPie(type: Download) {
|
def verifyBinary(name, chksum) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-arm-pie.zip'
|
return tasks.create([
|
||||||
dest "$torBinaryDir/tor_arm_pie.zip"
|
name : "verifyBinary${name}",
|
||||||
onlyIfNewer true
|
type : Verify,
|
||||||
}
|
dependsOn: downloadBinary(name)]) {
|
||||||
|
src "${torBinaryDir}/${name}.zip"
|
||||||
task downloadTorBinaryX86(type: Download) {
|
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-x86.zip'
|
|
||||||
dest "$torBinaryDir/tor_x86.zip"
|
|
||||||
onlyIfNewer true
|
|
||||||
}
|
|
||||||
|
|
||||||
task downloadTorBinaryX86Pie(type: Download) {
|
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-x86-pie.zip'
|
|
||||||
dest "$torBinaryDir/tor_x86_pie.zip"
|
|
||||||
onlyIfNewer true
|
|
||||||
}
|
|
||||||
|
|
||||||
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
|
|
||||||
src "$torBinaryDir/geoip.zip"
|
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
|
checksum chksum
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
|
|
||||||
src "$torBinaryDir/tor_arm.zip"
|
|
||||||
algorithm 'SHA-256'
|
|
||||||
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
|
|
||||||
}
|
|
||||||
|
|
||||||
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
|
|
||||||
src "$torBinaryDir/tor_arm_pie.zip"
|
|
||||||
algorithm 'SHA-256'
|
|
||||||
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
|
|
||||||
}
|
|
||||||
|
|
||||||
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
|
|
||||||
src "$torBinaryDir/tor_x86.zip"
|
|
||||||
algorithm 'SHA-256'
|
|
||||||
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
|
|
||||||
}
|
|
||||||
|
|
||||||
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
|
|
||||||
src "$torBinaryDir/tor_x86_pie.zip"
|
|
||||||
algorithm 'SHA-256'
|
|
||||||
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
preBuild.dependsOn {
|
torBinaries.every { key, value ->
|
||||||
[
|
preBuild.dependsOn.add(verifyBinary(key, value))
|
||||||
'verifyTorGeoIp',
|
|
||||||
'verifyTorBinaryArm',
|
|
||||||
'verifyTorBinaryArmPie',
|
|
||||||
'verifyTorBinaryX86',
|
|
||||||
'verifyTorBinaryX86Pie'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -8,6 +8,8 @@
|
|||||||
-dontwarn dagger.**
|
-dontwarn dagger.**
|
||||||
-dontnote dagger.**
|
-dontnote dagger.**
|
||||||
|
|
||||||
|
-keep class net.i2p.crypto.eddsa.** { *; }
|
||||||
|
|
||||||
-dontwarn sun.misc.Unsafe
|
-dontwarn sun.misc.Unsafe
|
||||||
-dontnote com.google.common.**
|
-dontnote com.google.common.**
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.READ_LOGS"/>
|
<uses-permission android:name="android.permission.READ_LOGS"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<!-- Since API 23, this is needed to add contacts via Bluetooth -->
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ public class AndroidPluginModule {
|
|||||||
EventBus eventBus) {
|
EventBus eventBus) {
|
||||||
Context appContext = app.getApplicationContext();
|
Context appContext = app.getApplicationContext();
|
||||||
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
|
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
|
||||||
androidExecutor, appContext, random, backoffFactory);
|
androidExecutor, appContext, random, eventBus, backoffFactory);
|
||||||
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
|
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
|
||||||
locationUtils, reporter, eventBus, torSocketFactory,
|
locationUtils, reporter, eventBus, torSocketFactory,
|
||||||
backoffFactory);
|
backoffFactory);
|
||||||
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
|
||||||
backoffFactory, appContext);
|
backoffFactory, appContext);
|
||||||
final Collection<DuplexPluginFactory> duplex =
|
Collection<DuplexPluginFactory> duplex =
|
||||||
Arrays.asList(bluetooth, tor, lan);
|
Arrays.asList(bluetooth, tor, lan);
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
PluginConfig pluginConfig = new PluginConfig() {
|
PluginConfig pluginConfig = new PluginConfig() {
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import android.content.IntentFilter;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
@@ -23,6 +24,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
@@ -30,23 +33,14 @@ import org.briarproject.bramble.util.StringUtils;
|
|||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CompletionService;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -61,8 +55,6 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
|
|||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||||
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||||
@@ -75,14 +67,10 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class DroidtoothPlugin implements DuplexPlugin {
|
class DroidtoothPlugin implements DuplexPlugin, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(DroidtoothPlugin.class.getName());
|
Logger.getLogger(DroidtoothPlugin.class.getName());
|
||||||
private static final String FOUND =
|
|
||||||
"android.bluetooth.device.action.FOUND";
|
|
||||||
private static final String DISCOVERY_FINISHED =
|
|
||||||
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
|
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
@@ -136,12 +124,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
// with a message queue, so submit it to the AndroidExecutor
|
// with a message queue, so submit it to the AndroidExecutor
|
||||||
try {
|
try {
|
||||||
adapter = androidExecutor.runOnBackgroundThread(
|
adapter = androidExecutor.runOnBackgroundThread(
|
||||||
new Callable<BluetoothAdapter>() {
|
BluetoothAdapter::getDefaultAdapter).get();
|
||||||
@Override
|
|
||||||
public BluetoothAdapter call() throws Exception {
|
|
||||||
return BluetoothAdapter.getDefaultAdapter();
|
|
||||||
}
|
|
||||||
}).get();
|
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
LOG.warning("Interrupted while getting BluetoothAdapter");
|
LOG.warning("Interrupted while getting BluetoothAdapter");
|
||||||
@@ -166,9 +149,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
} else {
|
} else {
|
||||||
// Enable Bluetooth if settings allow
|
// Enable Bluetooth if settings allow
|
||||||
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
|
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
|
||||||
wasEnabledByUs = true;
|
enableAdapter();
|
||||||
if (adapter.enable()) LOG.info("Enabling Bluetooth");
|
|
||||||
else LOG.info("Could not enable Bluetooth");
|
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Not enabling Bluetooth");
|
LOG.info("Not enabling Bluetooth");
|
||||||
}
|
}
|
||||||
@@ -176,9 +157,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!isRunning()) return;
|
if (!isRunning()) return;
|
||||||
String address = AndroidUtils.getBluetoothAddress(appContext,
|
String address = AndroidUtils.getBluetoothAddress(appContext,
|
||||||
adapter);
|
adapter);
|
||||||
@@ -196,8 +175,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
||||||
"RFCOMM", getUuid());
|
"RFCOMM", getUuid());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isRunning()) {
|
if (!isRunning()) {
|
||||||
@@ -209,7 +187,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
backoff.reset();
|
backoff.reset();
|
||||||
callback.transportEnabled();
|
callback.transportEnabled();
|
||||||
acceptContactConnections();
|
acceptContactConnections();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,13 +236,27 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return new DroidtoothTransportConnection(this, s);
|
return new DroidtoothTransportConnection(this, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enableAdapter() {
|
||||||
|
if (adapter != null && !adapter.isEnabled()) {
|
||||||
|
if (adapter.enable()) {
|
||||||
|
LOG.info("Enabling Bluetooth");
|
||||||
|
wasEnabledByUs = true;
|
||||||
|
} else {
|
||||||
|
LOG.info("Could not enable Bluetooth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
running = false;
|
running = false;
|
||||||
if (receiver != null) appContext.unregisterReceiver(receiver);
|
if (receiver != null) appContext.unregisterReceiver(receiver);
|
||||||
tryToClose(socket);
|
tryToClose(socket);
|
||||||
// Disable Bluetooth if we enabled it and it's still enabled
|
disableAdapter();
|
||||||
if (wasEnabledByUs && adapter.isEnabled()) {
|
}
|
||||||
|
|
||||||
|
private void disableAdapter() {
|
||||||
|
if (adapter != null && adapter.isEnabled() && wasEnabledByUs) {
|
||||||
if (adapter.disable()) LOG.info("Disabling Bluetooth");
|
if (adapter.disable()) LOG.info("Disabling Bluetooth");
|
||||||
else LOG.info("Could not disable Bluetooth");
|
else LOG.info("Could not disable Bluetooth");
|
||||||
}
|
}
|
||||||
@@ -294,22 +285,19 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
Map<ContactId, TransportProperties> remote =
|
Map<ContactId, TransportProperties> remote =
|
||||||
callback.getRemoteProperties();
|
callback.getRemoteProperties();
|
||||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
final ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
if (connected.contains(c)) continue;
|
if (connected.contains(c)) continue;
|
||||||
final String address = e.getValue().get(PROP_ADDRESS);
|
String address = e.getValue().get(PROP_ADDRESS);
|
||||||
if (StringUtils.isNullOrEmpty(address)) continue;
|
if (StringUtils.isNullOrEmpty(address)) continue;
|
||||||
final String uuid = e.getValue().get(PROP_UUID);
|
String uuid = e.getValue().get(PROP_UUID);
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
BluetoothSocket s = connect(address, uuid);
|
BluetoothSocket s = connect(address, uuid);
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
backoff.reset();
|
backoff.reset();
|
||||||
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,8 +351,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createConnection(ContactId c) {
|
public DuplexTransportConnection createConnection(ContactId c) {
|
||||||
if (!isRunning()) return null;
|
if (!isRunning()) return null;
|
||||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
TransportProperties p = callback.getRemoteProperties(c);
|
||||||
if (p == null) return null;
|
|
||||||
String address = p.get(PROP_ADDRESS);
|
String address = p.get(PROP_ADDRESS);
|
||||||
if (StringUtils.isNullOrEmpty(address)) return null;
|
if (StringUtils.isNullOrEmpty(address)) return null;
|
||||||
String uuid = p.get(PROP_UUID);
|
String uuid = p.get(PROP_UUID);
|
||||||
@@ -374,90 +361,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return new DroidtoothTransportConnection(this, s);
|
return new DroidtoothTransportConnection(this, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice) {
|
|
||||||
if (!isRunning()) return null;
|
|
||||||
// Use the invitation codes to generate the UUID
|
|
||||||
byte[] b = r.nextBytes(UUID_BYTES);
|
|
||||||
UUID uuid = UUID.nameUUIDFromBytes(b);
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Invitation UUID " + uuid);
|
|
||||||
// Bind a server socket for receiving invitation connections
|
|
||||||
BluetoothServerSocket ss;
|
|
||||||
try {
|
|
||||||
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
|
||||||
"RFCOMM", uuid);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Create the background tasks
|
|
||||||
CompletionService<BluetoothSocket> complete =
|
|
||||||
new ExecutorCompletionService<>(ioExecutor);
|
|
||||||
List<Future<BluetoothSocket>> futures = new ArrayList<>();
|
|
||||||
if (alice) {
|
|
||||||
// Return the first connected socket
|
|
||||||
futures.add(complete.submit(new ListeningTask(ss)));
|
|
||||||
futures.add(complete.submit(new DiscoveryTask(uuid.toString())));
|
|
||||||
} else {
|
|
||||||
// Return the first socket with readable data
|
|
||||||
futures.add(complete.submit(new ReadableTask(
|
|
||||||
new ListeningTask(ss))));
|
|
||||||
futures.add(complete.submit(new ReadableTask(
|
|
||||||
new DiscoveryTask(uuid.toString()))));
|
|
||||||
}
|
|
||||||
BluetoothSocket chosen = null;
|
|
||||||
try {
|
|
||||||
Future<BluetoothSocket> f = complete.poll(timeout, MILLISECONDS);
|
|
||||||
if (f == null) return null; // No task completed within the timeout
|
|
||||||
chosen = f.get();
|
|
||||||
return new DroidtoothTransportConnection(this, chosen);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while exchanging invitations");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return null;
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
// Closing the socket will terminate the listener task
|
|
||||||
tryToClose(ss);
|
|
||||||
closeSockets(futures, chosen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeSockets(final List<Future<BluetoothSocket>> futures,
|
|
||||||
@Nullable final BluetoothSocket chosen) {
|
|
||||||
ioExecutor.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
for (Future<BluetoothSocket> f : futures) {
|
|
||||||
try {
|
|
||||||
if (f.cancel(true)) {
|
|
||||||
LOG.info("Cancelled task");
|
|
||||||
} else {
|
|
||||||
BluetoothSocket s = f.get();
|
|
||||||
if (s != null && s != chosen) {
|
|
||||||
LOG.info("Closing unwanted socket");
|
|
||||||
s.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while closing sockets");
|
|
||||||
return;
|
|
||||||
} catch (ExecutionException | IOException e) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsKeyAgreement() {
|
public boolean supportsKeyAgreement() {
|
||||||
return true;
|
return true;
|
||||||
@@ -472,7 +375,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
// No truncation necessary because COMMIT_LENGTH = 16
|
// No truncation necessary because COMMIT_LENGTH = 16
|
||||||
UUID uuid = UUID.nameUUIDFromBytes(commitment);
|
UUID uuid = UUID.nameUUIDFromBytes(commitment);
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
||||||
// Bind a server socket for receiving invitation connections
|
// Bind a server socket for receiving key agreement connections
|
||||||
BluetoothServerSocket ss;
|
BluetoothServerSocket ss;
|
||||||
try {
|
try {
|
||||||
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
||||||
@@ -513,6 +416,23 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return StringUtils.macToString(mac);
|
return StringUtils.macToString(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof EnableBluetoothEvent) {
|
||||||
|
enableAdapterAsync();
|
||||||
|
} else if (e instanceof DisableBluetoothEvent) {
|
||||||
|
disableAdapterAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableAdapterAsync() {
|
||||||
|
ioExecutor.execute(this::enableAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableAdapterAsync() {
|
||||||
|
ioExecutor.execute(this::disableAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -536,115 +456,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DiscoveryTask implements Callable<BluetoothSocket> {
|
|
||||||
|
|
||||||
private final String uuid;
|
|
||||||
|
|
||||||
private DiscoveryTask(String uuid) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BluetoothSocket call() throws Exception {
|
|
||||||
// Repeat discovery until we connect or get interrupted
|
|
||||||
while (true) {
|
|
||||||
// Discover nearby devices
|
|
||||||
LOG.info("Discovering nearby devices");
|
|
||||||
List<String> addresses = discoverDevices();
|
|
||||||
if (addresses.isEmpty()) {
|
|
||||||
LOG.info("No devices discovered");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Connect to any device with the right UUID
|
|
||||||
for (String address : addresses) {
|
|
||||||
BluetoothSocket s = connect(address, uuid);
|
|
||||||
if (s != null) {
|
|
||||||
LOG.info("Outgoing connection");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> discoverDevices() throws InterruptedException {
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
filter.addAction(FOUND);
|
|
||||||
filter.addAction(DISCOVERY_FINISHED);
|
|
||||||
DiscoveryReceiver disco = new DiscoveryReceiver();
|
|
||||||
appContext.registerReceiver(disco, filter);
|
|
||||||
LOG.info("Starting discovery");
|
|
||||||
adapter.startDiscovery();
|
|
||||||
return disco.waitForAddresses();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DiscoveryReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private final CountDownLatch finished = new CountDownLatch(1);
|
|
||||||
private final List<String> addresses = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context ctx, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (action.equals(DISCOVERY_FINISHED)) {
|
|
||||||
LOG.info("Discovery finished");
|
|
||||||
ctx.unregisterReceiver(this);
|
|
||||||
finished.countDown();
|
|
||||||
} else if (action.equals(FOUND)) {
|
|
||||||
BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE);
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
LOG.info("Discovered device: " +
|
|
||||||
scrubMacAddress(d.getAddress()));
|
|
||||||
}
|
|
||||||
addresses.add(d.getAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> waitForAddresses() throws InterruptedException {
|
|
||||||
finished.await();
|
|
||||||
List<String> shuffled = new ArrayList<>(addresses);
|
|
||||||
Collections.shuffle(shuffled);
|
|
||||||
return shuffled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ListeningTask implements Callable<BluetoothSocket> {
|
|
||||||
|
|
||||||
private final BluetoothServerSocket serverSocket;
|
|
||||||
|
|
||||||
private ListeningTask(BluetoothServerSocket serverSocket) {
|
|
||||||
this.serverSocket = serverSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BluetoothSocket call() throws IOException {
|
|
||||||
BluetoothSocket s = serverSocket.accept();
|
|
||||||
LOG.info("Incoming connection");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ReadableTask implements Callable<BluetoothSocket> {
|
|
||||||
|
|
||||||
private final Callable<BluetoothSocket> connectionTask;
|
|
||||||
|
|
||||||
private ReadableTask(Callable<BluetoothSocket> connectionTask) {
|
|
||||||
this.connectionTask = connectionTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BluetoothSocket call() throws Exception {
|
|
||||||
BluetoothSocket s = connectionTask.call();
|
|
||||||
InputStream in = s.getInputStream();
|
|
||||||
while (in.available() == 0) {
|
|
||||||
LOG.info("Waiting for data");
|
|
||||||
Thread.sleep(1000);
|
|
||||||
}
|
|
||||||
LOG.info("Data available");
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
|
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
|
||||||
|
|
||||||
private final BluetoothServerSocket ss;
|
private final BluetoothServerSocket ss;
|
||||||
@@ -657,16 +468,13 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Callable<KeyAgreementConnection> listen() {
|
public Callable<KeyAgreementConnection> listen() {
|
||||||
return new Callable<KeyAgreementConnection>() {
|
return () -> {
|
||||||
@Override
|
|
||||||
public KeyAgreementConnection call() throws IOException {
|
|
||||||
BluetoothSocket s = ss.accept();
|
BluetoothSocket s = ss.accept();
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info(ID.getString() + ": Incoming connection");
|
LOG.info(ID.getString() + ": Incoming connection");
|
||||||
return new KeyAgreementConnection(
|
return new KeyAgreementConnection(
|
||||||
new DroidtoothTransportConnection(
|
new DroidtoothTransportConnection(
|
||||||
DroidtoothPlugin.this, s), ID);
|
DroidtoothPlugin.this, s), ID);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.droidtooth;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
@@ -31,15 +32,18 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
|
|||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
|
private final EventBus eventBus;
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
|
||||||
public DroidtoothPluginFactory(Executor ioExecutor,
|
public DroidtoothPluginFactory(Executor ioExecutor,
|
||||||
AndroidExecutor androidExecutor, Context appContext,
|
AndroidExecutor androidExecutor, Context appContext,
|
||||||
SecureRandom secureRandom, BackoffFactory backoffFactory) {
|
SecureRandom secureRandom, EventBus eventBus,
|
||||||
|
BackoffFactory backoffFactory) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.secureRandom = secureRandom;
|
this.secureRandom = secureRandom;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +61,10 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory {
|
|||||||
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
|
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
return new DroidtoothPlugin(ioExecutor, androidExecutor, appContext,
|
DroidtoothPlugin plugin = new DroidtoothPlugin(ioExecutor,
|
||||||
secureRandom, backoff, callback, MAX_LATENCY);
|
androidExecutor, appContext, secureRandom, backoff, callback,
|
||||||
|
MAX_LATENCY);
|
||||||
|
eventBus.addListener(plugin);
|
||||||
|
return plugin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import net.freehaven.tor.control.EventHandler;
|
|||||||
import net.freehaven.tor.control.TorControlConnection;
|
import net.freehaven.tor.control.TorControlConnection;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -56,6 +55,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -85,13 +85,13 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
|
|||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||||
|
|
||||||
private static final String PROP_ONION = "onion";
|
|
||||||
private static final String[] EVENTS = {
|
private static final String[] EVENTS = {
|
||||||
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
|
"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
|
||||||
};
|
};
|
||||||
@@ -149,7 +149,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||||
Object o = appContext.getSystemService(POWER_SERVICE);
|
Object o = appContext.getSystemService(POWER_SERVICE);
|
||||||
PowerManager pm = (PowerManager) o;
|
PowerManager pm = (PowerManager) o;
|
||||||
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "TorPlugin");
|
// This tag will prevent Huawei's powermanager from killing us.
|
||||||
|
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "LocationManagerService");
|
||||||
wakeLock.setReferenceCounted(false);
|
wakeLock.setReferenceCounted(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,20 +370,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendDevReports() {
|
private void sendDevReports() {
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// TODO: Trigger this with a TransportEnabledEvent
|
// TODO: Trigger this with a TransportEnabledEvent
|
||||||
File reportDir = AndroidUtils.getReportDir(appContext);
|
File reportDir = AndroidUtils.getReportDir(appContext);
|
||||||
reporter.sendReports(reportDir);
|
reporter.sendReports(reportDir);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
String portString = callback.getSettings().get(PREF_TOR_PORT);
|
String portString = callback.getSettings().get(PREF_TOR_PORT);
|
||||||
int port;
|
int port;
|
||||||
@@ -394,8 +390,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
ss = new ServerSocket();
|
ss = new ServerSocket();
|
||||||
ss.bind(new InetSocketAddress("127.0.0.1", port));
|
ss.bind(new InetSocketAddress("127.0.0.1", port));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
tryToClose(ss);
|
tryToClose(ss);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -405,21 +400,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
socket = ss;
|
socket = ss;
|
||||||
// Store the port number
|
// Store the port number
|
||||||
final String localPort = String.valueOf(ss.getLocalPort());
|
String localPort = String.valueOf(ss.getLocalPort());
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.put(PREF_TOR_PORT, localPort);
|
s.put(PREF_TOR_PORT, localPort);
|
||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
// Create a hidden service if necessary
|
// Create a hidden service if necessary
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> publishHiddenService(localPort));
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
publishHiddenService(localPort);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
backoff.reset();
|
backoff.reset();
|
||||||
// Accept incoming hidden service connections from Tor
|
// Accept incoming hidden service connections from Tor
|
||||||
acceptContactConnections(ss);
|
acceptContactConnections(ss);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,29 +528,33 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
public void poll(Collection<ContactId> connected) {
|
public void poll(Collection<ContactId> connected) {
|
||||||
if (!isRunning()) return;
|
if (!isRunning()) return;
|
||||||
backoff.increment();
|
backoff.increment();
|
||||||
// TODO: Pass properties to connectAndCallBack()
|
Map<ContactId, TransportProperties> remote =
|
||||||
for (ContactId c : callback.getRemoteProperties().keySet())
|
callback.getRemoteProperties();
|
||||||
if (!connected.contains(c)) connectAndCallBack(c);
|
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
|
ContactId c = e.getKey();
|
||||||
|
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectAndCallBack(final ContactId c) {
|
private void connectAndCallBack(ContactId c, TransportProperties p) {
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> {
|
||||||
@Override
|
if (!isRunning()) return;
|
||||||
public void run() {
|
DuplexTransportConnection d = createConnection(p);
|
||||||
DuplexTransportConnection d = createConnection(c);
|
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
backoff.reset();
|
backoff.reset();
|
||||||
callback.outgoingConnectionCreated(c, d);
|
callback.outgoingConnectionCreated(c, d);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createConnection(ContactId c) {
|
public DuplexTransportConnection createConnection(ContactId c) {
|
||||||
if (!isRunning()) return null;
|
if (!isRunning()) return null;
|
||||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
return createConnection(callback.getRemoteProperties(c));
|
||||||
if (p == null) return null;
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private DuplexTransportConnection createConnection(TransportProperties p) {
|
||||||
String onion = p.get(PROP_ONION);
|
String onion = p.get(PROP_ONION);
|
||||||
if (StringUtils.isNullOrEmpty(onion)) return null;
|
if (StringUtils.isNullOrEmpty(onion)) return null;
|
||||||
if (!ONION.matcher(onion).matches()) {
|
if (!ONION.matcher(onion).matches()) {
|
||||||
@@ -589,17 +582,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsKeyAgreement() {
|
public boolean supportsKeyAgreement() {
|
||||||
return false;
|
return false;
|
||||||
@@ -693,9 +675,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus() {
|
private void updateConnectionStatus() {
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
|
|
||||||
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
|
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
|
||||||
@@ -707,8 +687,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
|
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
|
||||||
country);
|
country);
|
||||||
Settings s = callback.getSettings();
|
Settings s = callback.getSettings();
|
||||||
int network = s.getInt(PREF_TOR_NETWORK,
|
int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_ALWAYS);
|
||||||
PREF_TOR_NETWORK_ALWAYS);
|
|
||||||
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Online: " + online + ", wifi: " + wifi);
|
LOG.info("Online: " + online + ", wifi: " + wifi);
|
||||||
@@ -732,9 +711,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
enableNetwork(true);
|
enableNetwork(true);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -21,12 +22,12 @@ class TorTransportConnection extends AbstractDuplexTransportConnection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getInputStream() throws IOException {
|
protected InputStream getInputStream() throws IOException {
|
||||||
return socket.getInputStream();
|
return IoUtils.getInputStream(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected OutputStream getOutputStream() throws IOException {
|
protected OutputStream getOutputStream() throws IOException {
|
||||||
return socket.getOutputStream();
|
return IoUtils.getOutputStream(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -27,14 +27,11 @@ class AndroidExecutorImpl implements AndroidExecutor {
|
|||||||
@Inject
|
@Inject
|
||||||
AndroidExecutorImpl(Application app) {
|
AndroidExecutorImpl(Application app) {
|
||||||
uiHandler = new Handler(app.getApplicationContext().getMainLooper());
|
uiHandler = new Handler(app.getApplicationContext().getMainLooper());
|
||||||
loop = new Runnable() {
|
loop = () -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Looper.prepare();
|
Looper.prepare();
|
||||||
backgroundHandler = new Handler();
|
backgroundHandler = new Handler();
|
||||||
startLatch.countDown();
|
startLatch.countDown();
|
||||||
Looper.loop();
|
Looper.loop();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
apply plugin: 'java'
|
apply plugin: 'java-library'
|
||||||
sourceCompatibility = 1.6
|
sourceCompatibility = 1.8
|
||||||
targetCompatibility = 1.6
|
targetCompatibility = 1.8
|
||||||
|
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "com.google.dagger:dagger:2.0.2"
|
implementation "com.google.dagger:dagger:2.0.2"
|
||||||
compile 'com.google.dagger:dagger-compiler:2.0.2'
|
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.1'
|
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testCompile "org.jmock:jmock:2.8.1"
|
testImplementation "org.jmock:jmock:2.8.2"
|
||||||
testCompile "org.jmock:jmock-junit4:2.8.1"
|
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||||
testCompile "org.jmock:jmock-legacy:2.8.1"
|
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||||
testCompile "org.hamcrest:hamcrest-library:1.3"
|
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||||
testCompile "org.hamcrest:hamcrest-core:1.3"
|
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||||
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
|
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
'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.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
|
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
|
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
|
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||||
|
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||||
|
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||||
|
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
|
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,3 +48,8 @@ task jarTest(type: Jar, dependsOn: testClasses) {
|
|||||||
artifacts {
|
artifacts {
|
||||||
testOutput jarTest
|
testOutput jarTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
useJava6StandardLibrary(it)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api;
|
package org.briarproject.bramble.api;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -53,6 +54,12 @@ public class Bytes implements Comparable<Bytes> {
|
|||||||
return aBytes.length - bBytes.length;
|
return aBytes.length - bBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() +
|
||||||
|
"(" + StringUtils.toHexString(getBytes()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
public static class BytesComparator implements Comparator<Bytes> {
|
public static class BytesComparator implements Comparator<Bytes> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class BdfMessageContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BdfMessageContext(BdfDictionary dictionary) {
|
public BdfMessageContext(BdfDictionary dictionary) {
|
||||||
this(dictionary, Collections.<MessageId>emptyList());
|
this(dictionary, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BdfDictionary getDictionary() {
|
public BdfDictionary getDictionary() {
|
||||||
|
|||||||
@@ -12,18 +12,19 @@ public interface ContactGroupFactory {
|
|||||||
/**
|
/**
|
||||||
* Creates a group that is not shared with any contacts.
|
* Creates a group that is not shared with any contacts.
|
||||||
*/
|
*/
|
||||||
Group createLocalGroup(ClientId clientId);
|
Group createLocalGroup(ClientId clientId, int clientVersion);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group for the given client to share with the given contact.
|
* Creates a group for the given client to share with the given contact.
|
||||||
*/
|
*/
|
||||||
Group createContactGroup(ClientId clientId, Contact contact);
|
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
|
Contact contact);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group for the given client to share between the given authors
|
* Creates a group for the given client to share between the given authors
|
||||||
* identified by their AuthorIds.
|
* identified by their AuthorIds.
|
||||||
*/
|
*/
|
||||||
Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
AuthorId authorId2);
|
AuthorId authorId1, AuthorId authorId2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,32 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ContactExchangeTask {
|
public interface ContactExchangeTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the contact exchange protocol
|
||||||
|
*/
|
||||||
|
int PROTOCOL_VERSION = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Alice's header key from the master secret.
|
||||||
|
*/
|
||||||
|
String ALICE_KEY_LABEL =
|
||||||
|
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Bob's header key from the master secret.
|
||||||
|
*/
|
||||||
|
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Alice's key binding nonce from the master secret.
|
||||||
|
*/
|
||||||
|
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Bob's key binding nonce from the master secret.
|
||||||
|
*/
|
||||||
|
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exchanges contact information with a remote peer.
|
* Exchanges contact information with a remote peer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
@@ -10,8 +7,6 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
SecretKey generateSecretKey();
|
SecretKey generateSecretKey();
|
||||||
|
|
||||||
PseudoRandom getPseudoRandom(int seed1, int seed2);
|
|
||||||
|
|
||||||
SecureRandom getSecureRandom();
|
SecureRandom getSecureRandom();
|
||||||
|
|
||||||
KeyPair generateAgreementKeyPair();
|
KeyPair generateAgreementKeyPair();
|
||||||
@@ -22,164 +17,98 @@ public interface CryptoComponent {
|
|||||||
|
|
||||||
KeyParser getSignatureKeyParser();
|
KeyParser getSignatureKeyParser();
|
||||||
|
|
||||||
|
KeyPair generateEdKeyPair();
|
||||||
|
|
||||||
|
KeyParser getEdKeyParser();
|
||||||
|
|
||||||
KeyParser getMessageKeyParser();
|
KeyParser getMessageKeyParser();
|
||||||
|
|
||||||
/** Generates a random invitation code. */
|
|
||||||
int generateBTInvitationCode();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a confirmation code from the given master secret.
|
* Derives another secret key from the given secret key.
|
||||||
* @param alice whether the code is for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
int deriveBTConfirmationCode(SecretKey master, boolean alice);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a stream header key from the given master secret.
|
|
||||||
* @param alice whether the key is for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
SecretKey deriveHeaderKey(SecretKey master, boolean alice);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a message authentication code key from the given master secret.
|
|
||||||
* @param alice whether the key is for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
SecretKey deriveMacKey(SecretKey master, boolean alice);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a nonce from the given master secret for one of the parties to
|
|
||||||
* sign.
|
|
||||||
* @param alice whether the nonce is for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a commitment to the provided public key.
|
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
*
|
||||||
* @param publicKey the public key
|
* @param label a namespaced label indicating the purpose of the derived
|
||||||
* @return the commitment to the provided public key.
|
* key, to prevent it from being repurposed or colliding with a key derived
|
||||||
|
* for another purpose
|
||||||
*/
|
*/
|
||||||
byte[] deriveKeyCommitment(byte[] publicKey);
|
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a common shared secret from two public keys and one of the
|
* Derives a common shared secret from two public keys and one of the
|
||||||
* corresponding private keys.
|
* corresponding private keys.
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
*
|
||||||
* @param theirPublicKey the ephemeral public key of the remote party
|
* @param label a namespaced label indicating the purpose of this shared
|
||||||
* @param ourKeyPair our ephemeral keypair
|
* secret, to prevent it from being repurposed or colliding with a shared
|
||||||
* @param alice true if ourKeyPair belongs to Alice
|
* secret derived for another purpose
|
||||||
|
* @param theirPublicKey the public key of the remote party
|
||||||
|
* @param ourKeyPair the key pair of the local party
|
||||||
* @return the shared secret
|
* @return the shared secret
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
*/
|
||||||
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
boolean alice) throws GeneralSecurityException;
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives the content of a confirmation record.
|
* Signs the given byte[] with the given ECDSA private key.
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
*
|
||||||
* @param sharedSecret the common shared secret
|
* @param label a namespaced label indicating the purpose of this
|
||||||
* @param theirPayload the commit payload from the remote party
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
* @param ourPayload the commit payload we sent
|
* signature created for another purpose
|
||||||
* @param theirPublicKey the ephemeral public key of the remote party
|
|
||||||
* @param ourKeyPair our ephemeral keypair
|
|
||||||
* @param alice true if ourKeyPair belongs to Alice
|
|
||||||
* @param aliceRecord true if the confirmation record is for use by Alice
|
|
||||||
* @return the confirmation record
|
|
||||||
*/
|
|
||||||
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
|
||||||
byte[] theirPayload, byte[] ourPayload,
|
|
||||||
byte[] theirPublicKey, KeyPair ourKeyPair,
|
|
||||||
boolean alice, boolean aliceRecord);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a master secret from the given shared secret.
|
|
||||||
* <p/>
|
|
||||||
* Part of BQP.
|
|
||||||
*
|
|
||||||
* @param sharedSecret the common shared secret
|
|
||||||
* @return the master secret
|
|
||||||
*/
|
|
||||||
SecretKey deriveMasterSecret(SecretKey sharedSecret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a master secret from two public keys and one of the corresponding
|
|
||||||
* private keys.
|
|
||||||
* <p/>
|
|
||||||
* This is a helper method that calls
|
|
||||||
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
|
|
||||||
*
|
|
||||||
* @param theirPublicKey the ephemeral public key of the remote party
|
|
||||||
* @param ourKeyPair our ephemeral keypair
|
|
||||||
* @param alice true if ourKeyPair belongs to Alice
|
|
||||||
* @return the shared secret
|
|
||||||
* @throws GeneralSecurityException
|
|
||||||
*/
|
|
||||||
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
|
|
||||||
boolean alice) throws GeneralSecurityException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives initial transport keys for the given transport in the given
|
|
||||||
* rotation period from the given master secret.
|
|
||||||
* @param alice whether the keys are for use by Alice or Bob.
|
|
||||||
*/
|
|
||||||
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
|
|
||||||
long rotationPeriod, boolean alice);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotates the given transport keys to the given rotation period. If the
|
|
||||||
* keys are for a future rotation period they are not rotated.
|
|
||||||
*/
|
|
||||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
|
||||||
|
|
||||||
/** Encodes the pseudo-random tag that is used to recognise a stream. */
|
|
||||||
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs the given byte[] with the given PrivateKey.
|
|
||||||
*
|
|
||||||
* @param label A label specific to this signature
|
|
||||||
* to ensure that the signature cannot be repurposed
|
|
||||||
*/
|
*/
|
||||||
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that the given signature is valid for the signedData
|
* Signs the given byte[] with the given Ed25519 private key.
|
||||||
* and the given publicKey.
|
|
||||||
*
|
*
|
||||||
* @param label A label that was specific to this signature
|
* @param label A label specific to this signature
|
||||||
* to ensure that the signature cannot be repurposed
|
* to ensure that the signature cannot be repurposed
|
||||||
|
*/
|
||||||
|
byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the given signature is valid for the signed data
|
||||||
|
* and the given ECDSA public key.
|
||||||
|
*
|
||||||
|
* @param label a namespaced label indicating the purpose of this
|
||||||
|
* signature, to prevent it from being repurposed or colliding with a
|
||||||
|
* signature created for another purpose
|
||||||
* @return true if the signature was valid, false otherwise.
|
* @return true if the signature was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
byte[] signature) throws GeneralSecurityException;
|
byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the given signature is valid for the signed data
|
||||||
|
* and the given Ed25519 public key.
|
||||||
|
*
|
||||||
|
* @param label A label that was specific to this signature
|
||||||
|
* to ensure that the signature cannot be repurposed
|
||||||
|
* @return true if the signature was valid, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash of the given inputs. The inputs are unambiguously
|
* Returns the hash of the given inputs. The inputs are unambiguously
|
||||||
* combined by prefixing each input with its length.
|
* combined by prefixing each input with its length.
|
||||||
*
|
*
|
||||||
* @param label A label specific to this hash to ensure that hashes
|
* @param label a namespaced label indicating the purpose of this hash, to
|
||||||
* calculated for distinct purposes don't collide.
|
* prevent it from being repurposed or colliding with a hash created for
|
||||||
|
* another purpose
|
||||||
*/
|
*/
|
||||||
byte[] hash(String label, byte[]... inputs);
|
byte[] hash(String label, byte[]... inputs);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length of hashes produced by
|
|
||||||
* the {@link CryptoComponent#hash(String, byte[]...)} method.
|
|
||||||
*/
|
|
||||||
int getHashLength();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a message authentication code with the given key over the
|
* Returns a message authentication code with the given key over the
|
||||||
* given inputs. The inputs are unambiguously combined by prefixing each
|
* given inputs. The inputs are unambiguously combined by prefixing each
|
||||||
* input with its length.
|
* input with its length.
|
||||||
|
*
|
||||||
|
* @param label a namespaced label indicating the purpose of this MAC, to
|
||||||
|
* prevent it from being repurposed or colliding with a MAC created for
|
||||||
|
* another purpose
|
||||||
*/
|
*/
|
||||||
byte[] mac(SecretKey macKey, byte[]... inputs);
|
byte[] mac(String label, SecretKey macKey, byte[]... inputs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts and authenticates the given plaintext so it can be written to
|
* Encrypts and authenticates the given plaintext so it can be written to
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypto operations for the key agreement protocol - see
|
||||||
|
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
|
||||||
|
*/
|
||||||
|
public interface KeyAgreementCrypto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash label for public key commitment.
|
||||||
|
*/
|
||||||
|
String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key derivation label for confirmation record.
|
||||||
|
*/
|
||||||
|
String CONFIRMATION_KEY_LABEL =
|
||||||
|
"org.briarproject.bramble.keyagreement/CONFIRMATION_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MAC label for confirmation record.
|
||||||
|
*/
|
||||||
|
String CONFIRMATION_MAC_LABEL =
|
||||||
|
"org.briarproject.bramble.keyagreement/CONFIRMATION_MAC";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives a commitment to the provided public key.
|
||||||
|
*
|
||||||
|
* @param publicKey the public key
|
||||||
|
* @return the commitment to the provided public key.
|
||||||
|
*/
|
||||||
|
byte[] deriveKeyCommitment(PublicKey publicKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives the content of a confirmation record.
|
||||||
|
*
|
||||||
|
* @param sharedSecret the common shared secret
|
||||||
|
* @param theirPayload the key exchange payload of the remote party
|
||||||
|
* @param ourPayload the key exchange payload of the local party
|
||||||
|
* @param theirPublicKey the ephemeral public key of the remote party
|
||||||
|
* @param ourKeyPair our ephemeral key pair of the local party
|
||||||
|
* @param alice true if the local party is Alice
|
||||||
|
* @param aliceRecord true if the confirmation record is for use by Alice
|
||||||
|
* @return the confirmation record
|
||||||
|
*/
|
||||||
|
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||||
|
byte[] theirPayload, byte[] ourPayload,
|
||||||
|
PublicKey theirPublicKey, KeyPair ourKeyPair,
|
||||||
|
boolean alice, boolean aliceRecord);
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A deterministic pseudo-random number generator.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface PseudoRandom {
|
|
||||||
|
|
||||||
byte[] nextBytes(int bytes);
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,9 @@ public interface StreamDecrypterFactory {
|
|||||||
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
|
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StreamDecrypter} for decrypting an invitation stream.
|
* Creates a {@link StreamDecrypter} for decrypting a contact exchange
|
||||||
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamDecrypter createInvitationStreamDecrypter(InputStream in,
|
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ public interface StreamEncrypterFactory {
|
|||||||
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
|
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link StreamEncrypter} for encrypting an invitation stream.
|
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
||||||
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypto operations for the transport security protocol - see
|
||||||
|
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
|
||||||
|
*/
|
||||||
|
public interface TransportCrypto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives initial transport keys for the given transport in the given
|
||||||
|
* rotation period from the given master secret.
|
||||||
|
*
|
||||||
|
* @param alice whether the keys are for use by Alice or Bob.
|
||||||
|
*/
|
||||||
|
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
|
||||||
|
long rotationPeriod, boolean alice);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates the given transport keys to the given rotation period. If the
|
||||||
|
* keys are for the given period or any later period they are not rotated.
|
||||||
|
*/
|
||||||
|
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes the pseudo-random tag that is used to recognise a stream.
|
||||||
|
*/
|
||||||
|
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
|
long streamNumber);
|
||||||
|
}
|
||||||
@@ -4,11 +4,14 @@ import org.briarproject.bramble.api.Bytes;
|
|||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
|
@NotThreadSafe
|
||||||
|
public class BdfDictionary extends TreeMap<String, Object> {
|
||||||
|
|
||||||
public static final Object NULL_VALUE = new Object();
|
public static final Object NULL_VALUE = new Object();
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ package org.briarproject.bramble.api.data;
|
|||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||||
|
|
||||||
public class BdfList extends Vector<Object> {
|
@NotThreadSafe
|
||||||
|
public class BdfList extends ArrayList<Object> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method for constructing lists inline.
|
* Factory method for constructing lists inline.
|
||||||
|
|||||||
@@ -122,8 +122,9 @@ public interface DatabaseComponent {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the message with the given ID. The message ID and any other
|
* Deletes the message with the given ID. Unlike
|
||||||
* associated data are not deleted.
|
* {@link #removeMessage(Transaction, MessageId)}, the message ID and any
|
||||||
|
* other associated data are not deleted.
|
||||||
*/
|
*/
|
||||||
void deleteMessage(Transaction txn, MessageId m) throws DbException;
|
void deleteMessage(Transaction txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
@@ -452,6 +453,11 @@ public interface DatabaseComponent {
|
|||||||
*/
|
*/
|
||||||
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
|
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a message (and all associated state) from the database.
|
||||||
|
*/
|
||||||
|
void removeMessage(Transaction txn, MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a transport (and all associated state) from the database.
|
* Removes a transport (and all associated state) from the database.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package org.briarproject.bramble.api.db;
|
package org.briarproject.bramble.api.db;
|
||||||
|
|
||||||
import java.util.Hashtable;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
@ThreadSafe
|
@NotThreadSafe
|
||||||
public class Metadata extends Hashtable<String, byte[]> {
|
public class Metadata extends TreeMap<String, byte[]> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special value to indicate that a key is being removed.
|
* Special value to indicate that a key is being removed.
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class Transaction {
|
|||||||
* committed.
|
* committed.
|
||||||
*/
|
*/
|
||||||
public void attach(Event e) {
|
public void attach(Event e) {
|
||||||
if (events == null) events = new ArrayList<Event>();
|
if (events == null) events = new ArrayList<>();
|
||||||
events.add(e);
|
events.add(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing authors to calculate their identities.
|
* Label for hashing authors to calculate their identities.
|
||||||
*/
|
*/
|
||||||
public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
|
public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID";
|
||||||
|
|
||||||
public AuthorId(byte[] id) {
|
public AuthorId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
public interface InvitationConstants {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection timeout in milliseconds.
|
|
||||||
*/
|
|
||||||
long CONNECTION_TIMEOUT = 60 * 1000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The confirmation timeout in milliseconds.
|
|
||||||
*/
|
|
||||||
long CONFIRMATION_TIMEOUT = 60 * 1000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of bits in an invitation or confirmation code. Codes must fit
|
|
||||||
* into six decimal digits.
|
|
||||||
*/
|
|
||||||
int CODE_BITS = 19;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface for receiving updates about the state of an
|
|
||||||
* {@link InvitationTask}.
|
|
||||||
*/
|
|
||||||
public interface InvitationListener {
|
|
||||||
|
|
||||||
/** Called if a connection to the remote peer is established. */
|
|
||||||
void connectionSucceeded();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if a connection to the remote peer cannot be established. This
|
|
||||||
* indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void connectionFailed();
|
|
||||||
|
|
||||||
/** Called if key agreement with the remote peer succeeds. */
|
|
||||||
void keyAgreementSucceeded(int localCode, int remoteCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if key agreement with the remote peer fails or the connection is
|
|
||||||
* lost. This indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void keyAgreementFailed();
|
|
||||||
|
|
||||||
/** Called if the remote peer's confirmation check succeeds. */
|
|
||||||
void remoteConfirmationSucceeded();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if remote peer's confirmation check fails or the connection is
|
|
||||||
* lost. This indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void remoteConfirmationFailed();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if the exchange of pseudonyms succeeds. This indicates that the
|
|
||||||
* protocol has ended successfully.
|
|
||||||
*/
|
|
||||||
void pseudonymExchangeSucceeded(String remoteName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if the exchange of pseudonyms fails or the connection is lost.
|
|
||||||
* This indicates that the protocol has ended unsuccessfully.
|
|
||||||
*/
|
|
||||||
void pseudonymExchangeFailed();
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A snapshot of the state of an {@link InvitationTask}.
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
public class InvitationState {
|
|
||||||
|
|
||||||
private final int localInvitationCode, remoteInvitationCode;
|
|
||||||
private final int localConfirmationCode, remoteConfirmationCode;
|
|
||||||
private final boolean connected, connectionFailed;
|
|
||||||
private final boolean localCompared, remoteCompared;
|
|
||||||
private final boolean localMatched, remoteMatched;
|
|
||||||
@Nullable
|
|
||||||
private final String contactName;
|
|
||||||
|
|
||||||
public InvitationState(int localInvitationCode, int remoteInvitationCode,
|
|
||||||
int localConfirmationCode, int remoteConfirmationCode,
|
|
||||||
boolean connected, boolean connectionFailed, boolean localCompared,
|
|
||||||
boolean remoteCompared, boolean localMatched,
|
|
||||||
boolean remoteMatched, @Nullable String contactName) {
|
|
||||||
this.localInvitationCode = localInvitationCode;
|
|
||||||
this.remoteInvitationCode = remoteInvitationCode;
|
|
||||||
this.localConfirmationCode = localConfirmationCode;
|
|
||||||
this.remoteConfirmationCode = remoteConfirmationCode;
|
|
||||||
this.connected = connected;
|
|
||||||
this.connectionFailed = connectionFailed;
|
|
||||||
this.localCompared = localCompared;
|
|
||||||
this.remoteCompared = remoteCompared;
|
|
||||||
this.localMatched = localMatched;
|
|
||||||
this.remoteMatched = remoteMatched;
|
|
||||||
this.contactName = contactName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalInvitationCode() {
|
|
||||||
return localInvitationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemoteInvitationCode() {
|
|
||||||
return remoteInvitationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLocalConfirmationCode() {
|
|
||||||
return localConfirmationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemoteConfirmationCode() {
|
|
||||||
return remoteConfirmationCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getConnected() {
|
|
||||||
return connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getConnectionFailed() {
|
|
||||||
return connectionFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getLocalCompared() {
|
|
||||||
return localCompared;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getRemoteCompared() {
|
|
||||||
return remoteCompared;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getLocalMatched() {
|
|
||||||
return localMatched;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getRemoteMatched() {
|
|
||||||
return remoteMatched;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String getContactName() {
|
|
||||||
return contactName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A task for exchanging invitations with a remote peer.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface InvitationTask {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener to be informed of state changes and returns the
|
|
||||||
* task's current state.
|
|
||||||
*/
|
|
||||||
InvitationState addListener(InvitationListener l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the given listener.
|
|
||||||
*/
|
|
||||||
void removeListener(InvitationListener l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously starts the connection process.
|
|
||||||
*/
|
|
||||||
void connect();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously informs the remote peer that the local peer's
|
|
||||||
* confirmation codes matched.
|
|
||||||
*/
|
|
||||||
void localConfirmationSucceeded();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously informs the remote peer that the local peer's
|
|
||||||
* confirmation codes did not match.
|
|
||||||
*/
|
|
||||||
void localConfirmationFailed();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates tasks for exchanging invitations with remote peers.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface InvitationTaskFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a task using the given local and remote invitation codes.
|
|
||||||
*/
|
|
||||||
InvitationTask createTask(int localCode, int remoteCode);
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
|
|||||||
/**
|
/**
|
||||||
* The current version of the BQP protocol.
|
* The current version of the BQP protocol.
|
||||||
*/
|
*/
|
||||||
byte PROTOCOL_VERSION = 2;
|
byte PROTOCOL_VERSION = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the record header in bytes.
|
* The length of the record header in bytes.
|
||||||
@@ -22,7 +22,10 @@ public interface KeyAgreementConstants {
|
|||||||
*/
|
*/
|
||||||
int COMMIT_LENGTH = 16;
|
int COMMIT_LENGTH = 16;
|
||||||
|
|
||||||
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
|
/**
|
||||||
|
* The connection timeout in milliseconds.
|
||||||
|
*/
|
||||||
|
long CONNECTION_TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The transport identifier for Bluetooth.
|
* The transport identifier for Bluetooth.
|
||||||
@@ -33,4 +36,16 @@ public interface KeyAgreementConstants {
|
|||||||
* The transport identifier for LAN.
|
* The transport identifier for LAN.
|
||||||
*/
|
*/
|
||||||
int TRANSPORT_ID_LAN = 1;
|
int TRANSPORT_ID_LAN = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving the shared secret.
|
||||||
|
*/
|
||||||
|
String SHARED_SECRET_LABEL =
|
||||||
|
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving the master secret.
|
||||||
|
*/
|
||||||
|
String MASTER_SECRET_LABEL =
|
||||||
|
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.keyagreement;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages tasks for conducting key agreements with remote peers.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface KeyAgreementTaskFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current key agreement task.
|
|
||||||
*/
|
|
||||||
KeyAgreementTask createTask();
|
|
||||||
}
|
|
||||||
@@ -4,5 +4,10 @@ public interface LanTcpConstants {
|
|||||||
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.lan");
|
TransportId ID = new TransportId("org.briarproject.bramble.lan");
|
||||||
|
|
||||||
|
// a transport property (shared with contacts)
|
||||||
|
String PROP_IP_PORTS = "ipPorts";
|
||||||
|
|
||||||
|
// a local setting
|
||||||
String PREF_LAN_IP_PORTS = "ipPorts";
|
String PREF_LAN_IP_PORTS = "ipPorts";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ public interface PluginCallback {
|
|||||||
*/
|
*/
|
||||||
Map<ContactId, TransportProperties> getRemoteProperties();
|
Map<ContactId, TransportProperties> getRemoteProperties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the plugin's remote transport properties for the given contact.
|
||||||
|
*/
|
||||||
|
TransportProperties getRemoteProperties(ContactId c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given settings with the namespaced settings
|
* Merges the given settings with the namespaced settings
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ public interface PluginManager {
|
|||||||
*/
|
*/
|
||||||
Collection<DuplexPlugin> getDuplexPlugins();
|
Collection<DuplexPlugin> getDuplexPlugins();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns any duplex plugins that support invitations.
|
|
||||||
*/
|
|
||||||
Collection<DuplexPlugin> getInvitationPlugins();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns any duplex plugins that support key agreement.
|
* Returns any duplex plugins that support key agreement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ public interface TorConstants {
|
|||||||
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
||||||
|
|
||||||
|
String PROP_ONION = "onion";
|
||||||
|
|
||||||
int SOCKS_PORT = 59050;
|
int SOCKS_PORT = 59050;
|
||||||
int CONTROL_PORT = 59051;
|
int CONTROL_PORT = 59051;
|
||||||
|
|
||||||
@@ -16,4 +18,5 @@ public interface TorConstants {
|
|||||||
int PREF_TOR_NETWORK_NEVER = 0;
|
int PREF_TOR_NETWORK_NEVER = 0;
|
||||||
int PREF_TOR_NETWORK_WIFI = 1;
|
int PREF_TOR_NETWORK_WIFI = 1;
|
||||||
int PREF_TOR_NETWORK_ALWAYS = 2;
|
int PREF_TOR_NETWORK_ALWAYS = 2;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.plugin.duplex;
|
package org.briarproject.bramble.api.plugin.duplex;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
@@ -23,20 +22,6 @@ public interface DuplexPlugin extends Plugin {
|
|||||||
@Nullable
|
@Nullable
|
||||||
DuplexTransportConnection createConnection(ContactId c);
|
DuplexTransportConnection createConnection(ContactId c);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the plugin supports exchanging invitations.
|
|
||||||
*/
|
|
||||||
boolean supportsInvitations();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to create and return an invitation connection to the remote
|
|
||||||
* peer. Returns null if no connection can be established within the given
|
|
||||||
* time.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
|
||||||
long timeout, boolean alice);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the plugin supports short-range key agreement.
|
* Returns true if the plugin supports short-range key agreement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin.event;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that asks the Bluetooth plugin to disable the Bluetooth adapter if
|
||||||
|
* we previously enabled it.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class DisableBluetoothEvent extends Event {
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin.event;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event asks the Bluetooth plugin to enable the Bluetooth adapter.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class EnableBluetoothEvent extends Event {
|
||||||
|
}
|
||||||
@@ -17,6 +17,11 @@ public interface TransportPropertyManager {
|
|||||||
*/
|
*/
|
||||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
|
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the transport property client.
|
||||||
|
*/
|
||||||
|
int CLIENT_VERSION = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given properties received while adding a contact - they will
|
* Stores the given properties received while adding a contact - they will
|
||||||
* be superseded by any properties synced from the contact.
|
* be superseded by any properties synced from the contact.
|
||||||
@@ -33,7 +38,7 @@ public interface TransportPropertyManager {
|
|||||||
/**
|
/**
|
||||||
* Returns the local transport properties for all transports.
|
* Returns the local transport properties for all transports.
|
||||||
* <br/>
|
* <br/>
|
||||||
* Read-Only
|
* TODO: Transaction can be read-only when code is simplified
|
||||||
*/
|
*/
|
||||||
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
|
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
@@ -49,6 +54,13 @@ public interface TransportPropertyManager {
|
|||||||
Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
|
Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remote transport properties for the given contact and
|
||||||
|
* transport.
|
||||||
|
*/
|
||||||
|
TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given properties with the existing local properties for the
|
* Merges the given properties with the existing local properties for the
|
||||||
* given transport.
|
* given transport.
|
||||||
|
|||||||
@@ -36,4 +36,8 @@ public class ClientId implements Comparable<ClientId> {
|
|||||||
return id.hashCode();
|
return id.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
public interface GroupFactory {
|
public interface GroupFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group with the given client ID and descriptor.
|
* Creates a group with the given client ID, client version and descriptor.
|
||||||
*/
|
*/
|
||||||
Group createGroup(ClientId c, byte[] descriptor);
|
Group createGroup(ClientId c, int clientVersion, byte[] descriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing groups to calculate their identifiers.
|
* Label for hashing groups to calculate their identifiers.
|
||||||
*/
|
*/
|
||||||
public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
|
public static final String LABEL = "org.briarproject.bramble/GROUP_ID";
|
||||||
|
|
||||||
public GroupId(byte[] id) {
|
public GroupId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class MessageContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MessageContext(Metadata metadata) {
|
public MessageContext(Metadata metadata) {
|
||||||
this(metadata, Collections.<MessageId>emptyList());
|
this(metadata, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Metadata getMetadata() {
|
public Metadata getMetadata() {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
|
|||||||
/**
|
/**
|
||||||
* Label for hashing messages to calculate their identifiers.
|
* Label for hashing messages to calculate their identifiers.
|
||||||
*/
|
*/
|
||||||
public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
|
public static final String LABEL = "org.briarproject.bramble/MESSAGE_ID";
|
||||||
|
|
||||||
public MessageId(byte[] id) {
|
public MessageId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public interface StreamReaderFactory {
|
|||||||
InputStream createStreamReader(InputStream in, StreamContext ctx);
|
InputStream createStreamReader(InputStream in, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link InputStream InputStream} for reading from an
|
* Creates an {@link InputStream InputStream} for reading from a contact
|
||||||
* invitation stream.
|
* exchangestream.
|
||||||
*/
|
*/
|
||||||
InputStream createInvitationStreamReader(InputStream in,
|
InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public interface StreamWriterFactory {
|
|||||||
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
|
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link OutputStream OutputStream} for writing to an
|
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
||||||
* invitation stream.
|
* exchange stream.
|
||||||
*/
|
*/
|
||||||
OutputStream createInvitationStreamWriter(OutputStream out,
|
OutputStream createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import org.briarproject.bramble.api.crypto.SecretKey;
|
|||||||
|
|
||||||
public interface TransportConstants {
|
public interface TransportConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the transport protocol.
|
||||||
|
*/
|
||||||
|
int PROTOCOL_VERSION = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the pseudo-random tag in bytes.
|
* The length of the pseudo-random tag in bytes.
|
||||||
*/
|
*/
|
||||||
@@ -14,21 +19,22 @@ public interface TransportConstants {
|
|||||||
*/
|
*/
|
||||||
int STREAM_HEADER_NONCE_LENGTH = 24;
|
int STREAM_HEADER_NONCE_LENGTH = 24;
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the stream header initialisation vector (IV) in bytes.
|
|
||||||
*/
|
|
||||||
int STREAM_HEADER_IV_LENGTH = STREAM_HEADER_NONCE_LENGTH - 8;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the message authentication code (MAC) in bytes.
|
* The length of the message authentication code (MAC) in bytes.
|
||||||
*/
|
*/
|
||||||
int MAC_LENGTH = 16;
|
int MAC_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the stream header plaintext in bytes. The stream header
|
||||||
|
* contains the protocol version, stream number and frame key.
|
||||||
|
*/
|
||||||
|
int STREAM_HEADER_PLAINTEXT_LENGTH = 2 + 8 + SecretKey.LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the stream header in bytes.
|
* The length of the stream header in bytes.
|
||||||
*/
|
*/
|
||||||
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
|
int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
|
||||||
+ MAC_LENGTH;
|
+ STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the frame nonce in bytes.
|
* The length of the frame nonce in bytes.
|
||||||
@@ -74,4 +80,32 @@ public interface TransportConstants {
|
|||||||
* The size of the reordering window.
|
* The size of the reordering window.
|
||||||
*/
|
*/
|
||||||
int REORDERING_WINDOW_SIZE = 32;
|
int REORDERING_WINDOW_SIZE = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Alice's initial tag key from the master secret.
|
||||||
|
*/
|
||||||
|
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Bob's initial tag key from the master secret.
|
||||||
|
*/
|
||||||
|
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Alice's initial header key from the master secret.
|
||||||
|
*/
|
||||||
|
String ALICE_HEADER_LABEL =
|
||||||
|
"org.briarproject.bramble.transport/ALICE_HEADER_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving Bob's initial header key from the master secret.
|
||||||
|
*/
|
||||||
|
String BOB_HEADER_LABEL =
|
||||||
|
"org.briarproject.bramble.transport/BOB_HEADER_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving the next period's key in key rotation.
|
||||||
|
*/
|
||||||
|
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -59,4 +60,24 @@ public class IoUtils {
|
|||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for a bug in Android 7, see
|
||||||
|
// https://android-review.googlesource.com/#/c/271775/
|
||||||
|
public static InputStream getInputStream(Socket s) throws IOException {
|
||||||
|
try {
|
||||||
|
return s.getInputStream();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for a bug in Android 7, see
|
||||||
|
// https://android-review.googlesource.com/#/c/271775/
|
||||||
|
public static OutputStream getOutputStream(Socket s) throws IOException {
|
||||||
|
try {
|
||||||
|
return s.getOutputStream();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class PrivacyUtils {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String scrubMacAddress(@Nullable String address) {
|
public static String scrubMacAddress(@Nullable String address) {
|
||||||
if (address == null) return null;
|
if (address == null || address.length() == 0) return null;
|
||||||
// this is a fake address we need to know about
|
// this is a fake address we need to know about
|
||||||
if (address.equals("02:00:00:00:00:00")) return address;
|
if (address.equals("02:00:00:00:00:00")) return address;
|
||||||
// keep first and last octet of MAC address
|
// keep first and last octet of MAC address
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import java.nio.charset.CharacterCodingException;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.CharsetDecoder;
|
import java.nio.charset.CharsetDecoder;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -27,6 +28,7 @@ public class StringUtils {
|
|||||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
};
|
};
|
||||||
|
private static final Random random = new Random();
|
||||||
|
|
||||||
public static boolean isNullOrEmpty(@Nullable String s) {
|
public static boolean isNullOrEmpty(@Nullable String s) {
|
||||||
return s == null || s.length() == 0;
|
return s == null || s.length() == 0;
|
||||||
@@ -139,4 +141,12 @@ public class StringUtils {
|
|||||||
}
|
}
|
||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRandomString(int length) {
|
||||||
|
char[] c = new char[length];
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
c[i] = (char) ('a' + random.nextInt(26));
|
||||||
|
return new String(c);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package org.briarproject.bramble.test;
|
|||||||
import org.jmock.Mockery;
|
import org.jmock.Mockery;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
public abstract class BrambleMockTestCase extends
|
public abstract class BrambleMockTestCase extends BrambleTestCase {
|
||||||
BrambleTestCase {
|
|
||||||
|
|
||||||
protected final Mockery context = new Mockery();
|
protected final Mockery context = new Mockery();
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,9 @@ public abstract class BrambleTestCase {
|
|||||||
|
|
||||||
public BrambleTestCase() {
|
public BrambleTestCase() {
|
||||||
// Ensure exceptions thrown on worker threads cause tests to fail
|
// Ensure exceptions thrown on worker threads cause tests to fail
|
||||||
UncaughtExceptionHandler fail = new UncaughtExceptionHandler() {
|
UncaughtExceptionHandler fail = (thread, throwable) -> {
|
||||||
@Override
|
|
||||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
throwable.printStackTrace();
|
||||||
fail();
|
fail();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Thread.setDefaultUncaughtExceptionHandler(fail);
|
Thread.setDefaultUncaughtExceptionHandler(fail);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,13 +34,6 @@ public class TestUtils {
|
|||||||
return getRandomBytes(UniqueId.LENGTH);
|
return getRandomBytes(UniqueId.LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getRandomString(int length) {
|
|
||||||
char[] c = new char[length];
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
c[i] = (char) ('a' + random.nextInt(26));
|
|
||||||
return new String(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SecretKey getSecretKey() {
|
public static SecretKey getSecretKey() {
|
||||||
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
|
return new SecretKey(getRandomBytes(SecretKey.LENGTH));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,60 @@
|
|||||||
plugins {
|
apply plugin: 'java-library'
|
||||||
id "java"
|
sourceCompatibility = 1.8
|
||||||
id "net.ltgt.apt" version "0.9"
|
targetCompatibility = 1.8
|
||||||
id "idea"
|
|
||||||
}
|
|
||||||
sourceCompatibility = 1.6
|
|
||||||
targetCompatibility = 1.6
|
|
||||||
|
|
||||||
|
apply plugin: 'net.ltgt.apt'
|
||||||
|
apply plugin: 'idea'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-api')
|
implementation project(path: ':bramble-api', configuration: 'default')
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
implementation 'com.madgag.spongycastle:core:1.58.0.0'
|
||||||
compile 'com.madgag.spongycastle:core:1.54.0.0'
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
compile 'com.h2database:h2:1.4.190'
|
implementation 'org.bitlet:weupnp:0.1.4'
|
||||||
|
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
|
|
||||||
testCompile project(path: ':bramble-api', configuration: 'testOutput')
|
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
|
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
testImplementation "org.jmock:jmock:2.8.2"
|
||||||
|
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||||
|
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||||
|
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||||
|
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||||
|
testImplementation "org.whispersystems:curve25519-java:0.4.1"
|
||||||
|
|
||||||
|
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
|
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||||
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c'
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
|
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||||
|
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||||
|
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
|
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
|
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||||
|
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
|
||||||
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
|
'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.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
|
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
|
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
|
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
|
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
||||||
|
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||||
|
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||||
|
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||||
|
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
|
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
|
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,3 +69,8 @@ task jarTest(type: Jar, dependsOn: testClasses) {
|
|||||||
artifacts {
|
artifacts {
|
||||||
testOutput jarTest
|
testOutput jarTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
useJava6StandardLibrary(it)
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.invitation.InvitationModule;
|
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
@@ -32,7 +31,6 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
InvitationModule.class,
|
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
@@ -54,6 +52,7 @@ public class BrambleCoreModule {
|
|||||||
c.inject(new IdentityModule.EagerSingletons());
|
c.inject(new IdentityModule.EagerSingletons());
|
||||||
c.inject(new LifecycleModule.EagerSingletons());
|
c.inject(new LifecycleModule.EagerSingletons());
|
||||||
c.inject(new PluginModule.EagerSingletons());
|
c.inject(new PluginModule.EagerSingletons());
|
||||||
|
c.inject(new PropertiesModule.EagerSingletons());
|
||||||
c.inject(new SyncModule.EagerSingletons());
|
c.inject(new SyncModule.EagerSingletons());
|
||||||
c.inject(new SystemModule.EagerSingletons());
|
c.inject(new SystemModule.EagerSingletons());
|
||||||
c.inject(new TransportModule.EagerSingletons());
|
c.inject(new TransportModule.EagerSingletons());
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class PoliteExecutor implements Executor {
|
|||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private final Queue<Runnable> queue = new LinkedList<Runnable>();
|
private final Queue<Runnable> queue = new LinkedList<>();
|
||||||
private final Executor delegate;
|
private final Executor delegate;
|
||||||
private final int maxConcurrentTasks;
|
private final int maxConcurrentTasks;
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
@@ -48,11 +48,9 @@ public class PoliteExecutor implements Executor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(final Runnable r) {
|
public void execute(Runnable r) {
|
||||||
final long submitted = System.currentTimeMillis();
|
long submitted = System.currentTimeMillis();
|
||||||
Runnable wrapped = new Runnable() {
|
Runnable wrapped = () -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (log.isLoggable(LOG_LEVEL)) {
|
if (log.isLoggable(LOG_LEVEL)) {
|
||||||
long queued = System.currentTimeMillis() - submitted;
|
long queued = System.currentTimeMillis() - submitted;
|
||||||
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
||||||
@@ -62,7 +60,6 @@ public class PoliteExecutor implements Executor {
|
|||||||
} finally {
|
} finally {
|
||||||
scheduleNext();
|
scheduleNext();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (concurrentTasks < maxConcurrentTasks) {
|
if (concurrentTasks < maxConcurrentTasks) {
|
||||||
|
|||||||
@@ -28,19 +28,16 @@ public class TimeLoggingExecutor extends ThreadPoolExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(final Runnable r) {
|
public void execute(Runnable r) {
|
||||||
if (log.isLoggable(LOG_LEVEL)) {
|
if (log.isLoggable(LOG_LEVEL)) {
|
||||||
final long submitted = System.currentTimeMillis();
|
long submitted = System.currentTimeMillis();
|
||||||
super.execute(new Runnable() {
|
super.execute(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
long started = System.currentTimeMillis();
|
long started = System.currentTimeMillis();
|
||||||
long queued = started - submitted;
|
long queued = started - submitted;
|
||||||
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
||||||
r.run();
|
r.run();
|
||||||
long executing = System.currentTimeMillis() - started;
|
long executing = System.currentTimeMillis() - started;
|
||||||
log.log(LOG_LEVEL, "Execution time " + executing + " ms");
|
log.log(LOG_LEVEL, "Execution time " + executing + " ms");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
super.execute(r);
|
super.execute(r);
|
||||||
|
|||||||
@@ -201,8 +201,7 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
|
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
|
||||||
Transaction txn, GroupId g) throws DbException, FormatException {
|
Transaction txn, GroupId g) throws DbException, FormatException {
|
||||||
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g);
|
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g);
|
||||||
Map<MessageId, BdfDictionary> parsed =
|
Map<MessageId, BdfDictionary> parsed = new HashMap<>(raw.size());
|
||||||
new HashMap<MessageId, BdfDictionary>(raw.size());
|
|
||||||
for (Entry<MessageId, Metadata> e : raw.entrySet())
|
for (Entry<MessageId, Metadata> e : raw.entrySet())
|
||||||
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
|
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
|
||||||
return parsed;
|
return parsed;
|
||||||
@@ -229,8 +228,7 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
FormatException {
|
FormatException {
|
||||||
Metadata metadata = metadataEncoder.encode(query);
|
Metadata metadata = metadataEncoder.encode(query);
|
||||||
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g, metadata);
|
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g, metadata);
|
||||||
Map<MessageId, BdfDictionary> parsed =
|
Map<MessageId, BdfDictionary> parsed = new HashMap<>(raw.size());
|
||||||
new HashMap<MessageId, BdfDictionary>(raw.size());
|
|
||||||
for (Entry<MessageId, Metadata> e : raw.entrySet())
|
for (Entry<MessageId, Metadata> e : raw.entrySet())
|
||||||
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
|
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|||||||
@@ -32,23 +32,25 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createLocalGroup(ClientId clientId) {
|
public Group createLocalGroup(ClientId clientId, int clientVersion) {
|
||||||
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
|
return groupFactory.createGroup(clientId, clientVersion,
|
||||||
|
LOCAL_GROUP_DESCRIPTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, Contact contact) {
|
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
|
Contact contact) {
|
||||||
AuthorId local = contact.getLocalAuthorId();
|
AuthorId local = contact.getLocalAuthorId();
|
||||||
AuthorId remote = contact.getAuthor().getId();
|
AuthorId remote = contact.getAuthor().getId();
|
||||||
byte[] descriptor = createGroupDescriptor(local, remote);
|
byte[] descriptor = createGroupDescriptor(local, remote);
|
||||||
return groupFactory.createGroup(clientId, descriptor);
|
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, AuthorId authorId1,
|
public Group createContactGroup(ClientId clientId, int clientVersion,
|
||||||
AuthorId authorId2) {
|
AuthorId authorId1, AuthorId authorId2) {
|
||||||
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
|
||||||
return groupFactory.createGroup(clientId, descriptor);
|
return groupFactory.createGroup(clientId, clientVersion, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
private volatile boolean alice;
|
private volatile boolean alice;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ContactExchangeTaskImpl(DatabaseComponent db,
|
ContactExchangeTaskImpl(DatabaseComponent db,
|
||||||
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
|
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
|
||||||
BdfWriterFactory bdfWriterFactory, Clock clock,
|
BdfWriterFactory bdfWriterFactory, Clock clock,
|
||||||
ConnectionManager connectionManager, ContactManager contactManager,
|
ConnectionManager connectionManager, ContactManager contactManager,
|
||||||
@@ -141,23 +141,27 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Derive the header keys for the transport streams
|
// Derive the header keys for the transport streams
|
||||||
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
|
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
|
||||||
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
|
masterSecret, new byte[] {PROTOCOL_VERSION});
|
||||||
|
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
|
||||||
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
|
||||||
// Create the readers
|
// Create the readers
|
||||||
InputStream streamReader =
|
InputStream streamReader =
|
||||||
streamReaderFactory.createInvitationStreamReader(in,
|
streamReaderFactory.createContactExchangeStreamReader(in,
|
||||||
alice ? bobHeaderKey : aliceHeaderKey);
|
alice ? bobHeaderKey : aliceHeaderKey);
|
||||||
BdfReader r = bdfReaderFactory.createReader(streamReader);
|
BdfReader r = bdfReaderFactory.createReader(streamReader);
|
||||||
// Create the writers
|
// Create the writers
|
||||||
OutputStream streamWriter =
|
OutputStream streamWriter =
|
||||||
streamWriterFactory.createInvitationStreamWriter(out,
|
streamWriterFactory.createContactExchangeStreamWriter(out,
|
||||||
alice ? aliceHeaderKey : bobHeaderKey);
|
alice ? aliceHeaderKey : bobHeaderKey);
|
||||||
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
|
||||||
|
|
||||||
// Derive the nonces to be signed
|
// Derive the nonces to be signed
|
||||||
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
|
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
|
||||||
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
||||||
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
|
||||||
// Exchange pseudonyms, signed nonces, and timestamps
|
// Exchange pseudonyms, signed nonces, and timestamps
|
||||||
long localTimestamp = clock.currentTimeMillis();
|
long localTimestamp = clock.currentTimeMillis();
|
||||||
@@ -184,12 +188,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
// Close the outgoing stream and expect EOF on the incoming stream
|
// Close the outgoing stream and expect EOF on the incoming stream
|
||||||
w.close();
|
w.close();
|
||||||
if (!r.eof()) LOG.warning("Unexpected data at end of connection");
|
if (!r.eof()) LOG.warning("Unexpected data at end of connection");
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException | IOException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
listener.contactExchangeFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
listener.contactExchangeFailed();
|
listener.contactExchangeFailed();
|
||||||
tryToClose(conn, true);
|
tryToClose(conn, true);
|
||||||
@@ -201,8 +200,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Add the contact
|
// Add the contact
|
||||||
ContactId contactId = addContact(remoteAuthor, masterSecret,
|
ContactId contactId = addContact(remoteAuthor, timestamp,
|
||||||
timestamp, alice, remoteProperties);
|
remoteProperties);
|
||||||
// Reuse the connection as a transport connection
|
// Reuse the connection as a transport connection
|
||||||
connectionManager.manageOutgoingConnection(contactId, transportId,
|
connectionManager.manageOutgoingConnection(contactId, transportId,
|
||||||
conn);
|
conn);
|
||||||
@@ -276,8 +275,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
|
|
||||||
private Map<TransportId, TransportProperties> receiveTransportProperties(
|
private Map<TransportId, TransportProperties> receiveTransportProperties(
|
||||||
BdfReader r) throws IOException {
|
BdfReader r) throws IOException {
|
||||||
Map<TransportId, TransportProperties> remote =
|
Map<TransportId, TransportProperties> remote = new HashMap<>();
|
||||||
new HashMap<TransportId, TransportProperties>();
|
|
||||||
r.readListStart();
|
r.readListStart();
|
||||||
while (!r.hasListEnd()) {
|
while (!r.hasListEnd()) {
|
||||||
r.readListStart();
|
r.readListStart();
|
||||||
@@ -300,15 +298,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
return remote;
|
return remote;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContactId addContact(Author remoteAuthor, SecretKey master,
|
private ContactId addContact(Author remoteAuthor, long timestamp,
|
||||||
long timestamp, boolean alice,
|
|
||||||
Map<TransportId, TransportProperties> remoteProperties)
|
Map<TransportId, TransportProperties> remoteProperties)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
ContactId contactId;
|
ContactId contactId;
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
contactId = contactManager.addContact(txn, remoteAuthor,
|
contactId = contactManager.addContact(txn, remoteAuthor,
|
||||||
localAuthor.getId(), master, timestamp, alice, true, true);
|
localAuthor.getId(), masterSecret, timestamp, alice,
|
||||||
|
true, true);
|
||||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||||
remoteProperties);
|
remoteProperties);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -318,8 +316,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(DuplexTransportConnection conn,
|
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
|
||||||
boolean exception) {
|
|
||||||
try {
|
try {
|
||||||
LOG.info("Closing connection");
|
LOG.info("Closing connection");
|
||||||
conn.getReader().dispose(exception, true);
|
conn.getReader().dispose(exception, true);
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
|
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.keyManager = keyManager;
|
this.keyManager = keyManager;
|
||||||
addHooks = new CopyOnWriteArrayList<AddContactHook>();
|
addHooks = new CopyOnWriteArrayList<>();
|
||||||
removeHooks = new CopyOnWriteArrayList<RemoveContactHook>();
|
removeHooks = new CopyOnWriteArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -125,7 +125,7 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
List<Contact> active = new ArrayList<Contact>(contacts.size());
|
List<Contact> active = new ArrayList<>(contacts.size());
|
||||||
for (Contact c : contacts) if (c.isActive()) active.add(c);
|
for (Contact c : contacts) if (c.isActive()) active.add(c);
|
||||||
return active;
|
return active;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
|
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
@@ -27,7 +26,6 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
|||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
@@ -41,13 +39,8 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_BITS;
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
|
||||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
|
|
||||||
@@ -56,52 +49,19 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
private static final int AGREEMENT_KEY_PAIR_BITS = 256;
|
||||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||||
|
private static final int ED_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||||
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
private static final int PBKDF_SALT_BYTES = 32; // 256 bits
|
||||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||||
private static final int PBKDF_SAMPLES = 30;
|
private static final int PBKDF_SAMPLES = 30;
|
||||||
private static final int HASH_SIZE = 256 / 8;
|
|
||||||
|
|
||||||
private static byte[] ascii(String s) {
|
|
||||||
return s.getBytes(Charset.forName("US-ASCII"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// KDF labels for bluetooth confirmation code derivation
|
|
||||||
private static final byte[] BT_A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
|
|
||||||
private static final byte[] BT_B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
|
|
||||||
// KDF labels for contact exchange stream header key derivation
|
|
||||||
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
|
|
||||||
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
|
|
||||||
// KDF labels for contact exchange signature nonce derivation
|
|
||||||
private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE");
|
|
||||||
private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE");
|
|
||||||
// Hash label for BQP public key commitment derivation
|
|
||||||
private static final String COMMIT =
|
|
||||||
"org.briarproject.bramble.COMMIT";
|
|
||||||
// Hash label for shared secret derivation
|
|
||||||
private static final String SHARED_SECRET =
|
|
||||||
"org.briarproject.bramble.SHARED_SECRET";
|
|
||||||
// KDF label for BQP confirmation key derivation
|
|
||||||
private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
|
|
||||||
// KDF label for master key derivation
|
|
||||||
private static final byte[] MASTER_KEY = ascii("MASTER_KEY");
|
|
||||||
// KDF labels for tag key derivation
|
|
||||||
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
|
|
||||||
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
|
|
||||||
// KDF labels for header key derivation
|
|
||||||
private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY");
|
|
||||||
private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY");
|
|
||||||
// KDF labels for MAC key derivation
|
|
||||||
private static final byte[] A_MAC = ascii("ALICE_MAC_KEY");
|
|
||||||
private static final byte[] B_MAC = ascii("BOB_MAC_KEY");
|
|
||||||
// KDF label for key rotation
|
|
||||||
private static final byte[] ROTATE = ascii("ROTATE");
|
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
private final ECKeyPairGenerator signatureKeyPairGenerator;
|
||||||
private final KeyParser agreementKeyParser, signatureKeyParser;
|
private final KeyParser agreementKeyParser, signatureKeyParser;
|
||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
|
private final KeyPairGenerator edKeyPairGenerator;
|
||||||
|
private final KeyParser edKeyParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||||
@@ -135,6 +95,9 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
|
||||||
SIGNATURE_KEY_PAIR_BITS);
|
SIGNATURE_KEY_PAIR_BITS);
|
||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
|
edKeyPairGenerator = new KeyPairGenerator();
|
||||||
|
edKeyPairGenerator.initialize(ED_KEY_PAIR_BITS, secureRandom);
|
||||||
|
edKeyParser = new EdKeyParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
@@ -169,14 +132,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new SecretKey(b);
|
return new SecretKey(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
|
||||||
byte[] seed = new byte[INT_32_BYTES * 2];
|
|
||||||
ByteUtils.writeUint32(seed1, seed, 0);
|
|
||||||
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
|
|
||||||
return new PseudoRandomImpl(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecureRandom getSecureRandom() {
|
public SecureRandom getSecureRandom() {
|
||||||
return secureRandom;
|
return secureRandom;
|
||||||
@@ -201,6 +156,21 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair generateEdKeyPair() {
|
||||||
|
java.security.KeyPair keyPair = edKeyPairGenerator.generateKeyPair();
|
||||||
|
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
|
||||||
|
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
|
||||||
|
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
|
||||||
|
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
|
||||||
|
return new KeyPair(publicKey, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyParser getEdKeyParser() {
|
||||||
|
return edKeyParser;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyPair generateAgreementKeyPair() {
|
public KeyPair generateAgreementKeyPair() {
|
||||||
AsymmetricCipherKeyPair keyPair =
|
AsymmetricCipherKeyPair keyPair =
|
||||||
@@ -249,204 +219,63 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int generateBTInvitationCode() {
|
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
|
||||||
int codeBytes = (CODE_BITS + 7) / 8;
|
byte[] mac = mac(label, k, inputs);
|
||||||
byte[] random = new byte[codeBytes];
|
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
secureRandom.nextBytes(random);
|
return new SecretKey(mac);
|
||||||
return ByteUtils.readUint(random, CODE_BITS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
|
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
|
||||||
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
return ByteUtils.readUint(b, CODE_BITS);
|
throws GeneralSecurityException {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveHeaderKey(SecretKey master,
|
|
||||||
boolean alice) {
|
|
||||||
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
|
|
||||||
return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] deriveSignatureNonce(SecretKey master,
|
|
||||||
boolean alice) {
|
|
||||||
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] deriveKeyCommitment(byte[] publicKey) {
|
|
||||||
byte[] hash = hash(COMMIT, publicKey);
|
|
||||||
// The output is the first COMMIT_LENGTH bytes of the hash
|
|
||||||
byte[] commitment = new byte[COMMIT_LENGTH];
|
|
||||||
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
|
|
||||||
return commitment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
|
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
|
||||||
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
PrivateKey ourPriv = ourKeyPair.getPrivate();
|
||||||
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
|
byte[][] hashInputs = new byte[inputs.length + 1][];
|
||||||
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
|
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey);
|
||||||
byte[] alicePub, bobPub;
|
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length);
|
||||||
if (alice) {
|
byte[] hash = hash(label, hashInputs);
|
||||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
bobPub = theirPublicKey;
|
return new SecretKey(hash);
|
||||||
} else {
|
|
||||||
alicePub = theirPublicKey;
|
|
||||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
|
||||||
}
|
|
||||||
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
|
||||||
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
|
|
||||||
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
|
||||||
SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY));
|
|
||||||
byte[] alicePayload, alicePub, bobPayload, bobPub;
|
|
||||||
if (alice) {
|
|
||||||
alicePayload = ourPayload;
|
|
||||||
alicePub = ourKeyPair.getPublic().getEncoded();
|
|
||||||
bobPayload = theirPayload;
|
|
||||||
bobPub = theirPublicKey;
|
|
||||||
} else {
|
|
||||||
alicePayload = theirPayload;
|
|
||||||
alicePub = theirPublicKey;
|
|
||||||
bobPayload = ourPayload;
|
|
||||||
bobPub = ourKeyPair.getPublic().getEncoded();
|
|
||||||
}
|
|
||||||
if (aliceRecord)
|
|
||||||
return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub);
|
|
||||||
else
|
|
||||||
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
|
|
||||||
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
|
||||||
return deriveMasterSecret(deriveSharedSecret(
|
|
||||||
theirPublicKey, ourKeyPair, alice));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransportKeys deriveTransportKeys(TransportId t,
|
|
||||||
SecretKey master, long rotationPeriod, boolean alice) {
|
|
||||||
// Keys for the previous period are derived from the master secret
|
|
||||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
|
||||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
|
||||||
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
|
||||||
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
|
||||||
// Derive the keys for the current and next periods
|
|
||||||
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
|
||||||
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
|
||||||
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
|
||||||
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
|
||||||
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
|
||||||
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
|
||||||
// Initialise the reordering windows and stream counters
|
|
||||||
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
|
|
||||||
rotationPeriod - 1);
|
|
||||||
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
|
||||||
rotationPeriod);
|
|
||||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
|
||||||
rotationPeriod + 1);
|
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
|
||||||
rotationPeriod);
|
|
||||||
// Collect and return the keys
|
|
||||||
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransportKeys rotateTransportKeys(TransportKeys k,
|
|
||||||
long rotationPeriod) {
|
|
||||||
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
|
||||||
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
|
||||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
|
||||||
IncomingKeys inNext = k.getNextIncomingKeys();
|
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
|
||||||
long startPeriod = outCurr.getRotationPeriod();
|
|
||||||
// Rotate the keys
|
|
||||||
for (long p = startPeriod + 1; p <= rotationPeriod; 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);
|
|
||||||
}
|
|
||||||
// Collect and return the keys
|
|
||||||
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
|
||||||
outCurr);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
|
||||||
byte[] period = new byte[INT_64_BYTES];
|
|
||||||
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
|
||||||
return new SecretKey(macKdf(k, ROTATE, period));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
|
||||||
boolean alice) {
|
|
||||||
byte[] id = StringUtils.toUtf8(t.getString());
|
|
||||||
return new SecretKey(macKdf(master, alice ? A_TAG : B_TAG, id));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
|
||||||
boolean alice) {
|
|
||||||
byte[] id = StringUtils.toUtf8(t.getString());
|
|
||||||
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
|
|
||||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
|
||||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
// Initialise the PRF
|
|
||||||
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
|
||||||
// The output of the PRF must be long enough to use as a tag
|
|
||||||
int macLength = prf.getDigestSize();
|
|
||||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
|
||||||
// The input is the stream number as a 64-bit integer
|
|
||||||
byte[] input = new byte[INT_64_BYTES];
|
|
||||||
ByteUtils.writeUint64(streamNumber, input, 0);
|
|
||||||
prf.update(input, 0, input.length);
|
|
||||||
byte[] mac = new byte[macLength];
|
|
||||||
prf.doFinal(mac, 0);
|
|
||||||
// The output is the first TAG_LENGTH bytes of the MAC
|
|
||||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
Signature signature = new SignatureImpl(secureRandom);
|
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label,
|
||||||
KeyParser keyParser = getSignatureKeyParser();
|
toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] signEd(String label, byte[] toSign, byte[] privateKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
return sign(new EdSignature(), edKeyParser, label, toSign, privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] sign(Signature sig, KeyParser keyParser, String label,
|
||||||
|
byte[] toSign, byte[] privateKey) throws GeneralSecurityException {
|
||||||
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
PrivateKey key = keyParser.parsePrivateKey(privateKey);
|
||||||
signature.initSign(key);
|
sig.initSign(key);
|
||||||
updateSignature(signature, label, toSign);
|
updateSignature(sig, label, toSign);
|
||||||
return signature.sign();
|
return sig.sign();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
public boolean verify(String label, byte[] signedData, byte[] publicKey,
|
||||||
byte[] signature) throws GeneralSecurityException {
|
byte[] signature) throws GeneralSecurityException {
|
||||||
Signature sig = new SignatureImpl(secureRandom);
|
return verify(new SignatureImpl(secureRandom), signatureKeyParser,
|
||||||
KeyParser keyParser = getSignatureKeyParser();
|
label, signedData, publicKey, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
|
||||||
|
byte[] signature) throws GeneralSecurityException {
|
||||||
|
return verify(new EdSignature(), edKeyParser, label, signedData,
|
||||||
|
publicKey, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean verify(Signature sig, KeyParser keyParser, String label,
|
||||||
|
byte[] signedData, byte[] publicKey, byte[] signature)
|
||||||
|
throws GeneralSecurityException {
|
||||||
PublicKey key = keyParser.parsePublicKey(publicKey);
|
PublicKey key = keyParser.parsePublicKey(publicKey);
|
||||||
sig.initVerify(key);
|
sig.initVerify(key);
|
||||||
updateSignature(sig, label, signedData);
|
updateSignature(sig, label, signedData);
|
||||||
@@ -454,7 +283,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateSignature(Signature signature, String label,
|
private void updateSignature(Signature signature, String label,
|
||||||
byte[] toSign) {
|
byte[] toSign) throws GeneralSecurityException {
|
||||||
byte[] labelBytes = StringUtils.toUtf8(label);
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
byte[] length = new byte[INT_32_BYTES];
|
||||||
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||||
@@ -484,14 +313,13 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getHashLength() {
|
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
|
||||||
return HASH_SIZE;
|
byte[] labelBytes = StringUtils.toUtf8(label);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] mac(SecretKey macKey, byte[]... inputs) {
|
|
||||||
Digest mac = new Blake2sDigest(macKey.getBytes());
|
Digest mac = new Blake2sDigest(macKey.getBytes());
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
byte[] length = new byte[INT_32_BYTES];
|
||||||
|
ByteUtils.writeUint32(labelBytes.length, length, 0);
|
||||||
|
mac.update(length, 0, length.length);
|
||||||
|
mac.update(labelBytes, 0, labelBytes.length);
|
||||||
for (byte[] input : inputs) {
|
for (byte[] input : inputs) {
|
||||||
ByteUtils.writeUint32(input.length, length, 0);
|
ByteUtils.writeUint32(input.length, length, 0);
|
||||||
mac.update(length, 0, length.length);
|
mac.update(length, 0, length.length);
|
||||||
@@ -583,30 +411,6 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return AsciiArmour.wrap(b, lineLength);
|
return AsciiArmour.wrap(b, lineLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key derivation function based on a pseudo-random function - see
|
|
||||||
// NIST SP 800-108, section 5.1
|
|
||||||
private byte[] macKdf(SecretKey key, byte[]... inputs) {
|
|
||||||
// Initialise the PRF
|
|
||||||
Digest prf = new Blake2sDigest(key.getBytes());
|
|
||||||
// The output of the PRF must be long enough to use as a key
|
|
||||||
int macLength = prf.getDigestSize();
|
|
||||||
if (macLength < SecretKey.LENGTH) throw new IllegalStateException();
|
|
||||||
// Calculate the PRF over the concatenated length-prefixed inputs
|
|
||||||
byte[] length = new byte[INT_32_BYTES];
|
|
||||||
for (byte[] input : inputs) {
|
|
||||||
ByteUtils.writeUint32(input.length, length, 0);
|
|
||||||
prf.update(length, 0, length.length);
|
|
||||||
prf.update(input, 0, input.length);
|
|
||||||
}
|
|
||||||
byte[] mac = new byte[macLength];
|
|
||||||
prf.doFinal(mac, 0);
|
|
||||||
// The output is the first SecretKey.LENGTH bytes of the MAC
|
|
||||||
if (mac.length == SecretKey.LENGTH) return mac;
|
|
||||||
byte[] truncated = new byte[SecretKey.LENGTH];
|
|
||||||
System.arraycopy(mac, 0, truncated, 0, truncated.length);
|
|
||||||
return truncated;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
|
||||||
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
|
||||||
byte[] utf8 = StringUtils.toUtf8(password);
|
byte[] utf8 = StringUtils.toUtf8(password);
|
||||||
@@ -620,8 +424,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
int chooseIterationCount(int targetMillis) {
|
int chooseIterationCount(int targetMillis) {
|
||||||
List<Long> quickSamples = new ArrayList<Long>(PBKDF_SAMPLES);
|
List<Long> quickSamples = new ArrayList<>(PBKDF_SAMPLES);
|
||||||
List<Long> slowSamples = new ArrayList<Long>(PBKDF_SAMPLES);
|
List<Long> slowSamples = new ArrayList<>(PBKDF_SAMPLES);
|
||||||
long iterationNanos = 0, initNanos = 0;
|
long iterationNanos = 0, initNanos = 0;
|
||||||
while (iterationNanos <= 0 || initNanos <= 0) {
|
while (iterationNanos <= 0 || initNanos <= 0) {
|
||||||
// Sample the running time with one iteration and two iterations
|
// Sample the running time with one iteration and two iterations
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.TimeLoggingExecutor;
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ public class CryptoModule {
|
|||||||
|
|
||||||
public CryptoModule() {
|
public CryptoModule() {
|
||||||
// Use an unbounded queue
|
// Use an unbounded queue
|
||||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
|
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||||
// Discard tasks that are submitted during shutdown
|
// Discard tasks that are submitted during shutdown
|
||||||
RejectedExecutionHandler policy =
|
RejectedExecutionHandler policy =
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
@@ -74,6 +76,12 @@ public class CryptoModule {
|
|||||||
return new PasswordStrengthEstimatorImpl();
|
return new PasswordStrengthEstimatorImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
TransportCrypto provideTransportCrypto(
|
||||||
|
TransportCryptoImpl transportCrypto) {
|
||||||
|
return transportCrypto;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StreamDecrypterFactory provideStreamDecrypterFactory(
|
StreamDecrypterFactory provideStreamDecrypterFactory(
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
@@ -81,9 +89,17 @@ public class CryptoModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
|
StreamEncrypterFactory provideStreamEncrypterFactory(
|
||||||
|
CryptoComponent crypto, TransportCrypto transportCrypto,
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
|
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
|
||||||
|
cipherProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
KeyAgreementCrypto provideKeyAgreementCrypto(
|
||||||
|
KeyAgreementCryptoImpl keyAgreementCrypto) {
|
||||||
|
return keyAgreementCrypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class EdKeyParser implements KeyParser {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicKey parsePublicKey(byte[] encodedKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
|
return new EdPublicKey(encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrivateKey parsePrivateKey(byte[] encodedKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
if (encodedKey.length != 32) throw new GeneralSecurityException();
|
||||||
|
return new EdPrivateKey(encodedKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||||
|
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||||
|
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||||
|
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
|
|
||||||
|
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class EdSignature implements Signature {
|
||||||
|
|
||||||
|
private static final Provider PROVIDER = new EdDSASecurityProvider();
|
||||||
|
|
||||||
|
private static final EdDSANamedCurveSpec CURVE_SPEC =
|
||||||
|
EdDSANamedCurveTable.getByName("Ed25519");
|
||||||
|
|
||||||
|
private final java.security.Signature signature;
|
||||||
|
|
||||||
|
EdSignature() {
|
||||||
|
try {
|
||||||
|
signature = java.security.Signature
|
||||||
|
.getInstance(SIGNATURE_ALGORITHM, PROVIDER);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initSign(PrivateKey k) throws GeneralSecurityException {
|
||||||
|
if (!(k instanceof EdPrivateKey))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
|
||||||
|
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initVerify(PublicKey k) throws GeneralSecurityException {
|
||||||
|
if (!(k instanceof EdPublicKey))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
EdDSAPublicKey publicKey = new EdDSAPublicKey(
|
||||||
|
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
|
||||||
|
signature.initVerify(publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte b) throws GeneralSecurityException {
|
||||||
|
signature.update(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] b) throws GeneralSecurityException {
|
||||||
|
signature.update(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] b, int off, int len)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
signature.update(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] sign() throws GeneralSecurityException {
|
||||||
|
return signature.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(byte[] sig) throws GeneralSecurityException {
|
||||||
|
return signature.verify(sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
|
|
||||||
|
class KeyAgreementCryptoImpl implements KeyAgreementCrypto {
|
||||||
|
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KeyAgreementCryptoImpl(CryptoComponent crypto) {
|
||||||
|
this.crypto = crypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] deriveKeyCommitment(PublicKey publicKey) {
|
||||||
|
byte[] hash = crypto.hash(COMMIT_LABEL, publicKey.getEncoded());
|
||||||
|
// The output is the first COMMIT_LENGTH bytes of the hash
|
||||||
|
byte[] commitment = new byte[COMMIT_LENGTH];
|
||||||
|
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
|
||||||
|
return commitment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
|
||||||
|
byte[] theirPayload, byte[] ourPayload, PublicKey theirPublicKey,
|
||||||
|
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
|
||||||
|
SecretKey ck = crypto.deriveKey(CONFIRMATION_KEY_LABEL, sharedSecret);
|
||||||
|
byte[] alicePayload, alicePub, bobPayload, bobPub;
|
||||||
|
if (alice) {
|
||||||
|
alicePayload = ourPayload;
|
||||||
|
alicePub = ourKeyPair.getPublic().getEncoded();
|
||||||
|
bobPayload = theirPayload;
|
||||||
|
bobPub = theirPublicKey.getEncoded();
|
||||||
|
} else {
|
||||||
|
alicePayload = theirPayload;
|
||||||
|
alicePub = theirPublicKey.getEncoded();
|
||||||
|
bobPayload = ourPayload;
|
||||||
|
bobPub = ourKeyPair.getPublic().getEncoded();
|
||||||
|
}
|
||||||
|
if (aliceRecord) {
|
||||||
|
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, alicePayload,
|
||||||
|
alicePub, bobPayload, bobPub);
|
||||||
|
} else {
|
||||||
|
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, bobPayload, bobPub,
|
||||||
|
alicePayload, alicePub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float estimateStrength(String password) {
|
public float estimateStrength(String password) {
|
||||||
HashSet<Character> unique = new HashSet<Character>();
|
HashSet<Character> unique = new HashSet<>();
|
||||||
int length = password.length();
|
int length = password.length();
|
||||||
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
|
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
|
||||||
return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS);
|
return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS);
|
||||||
|
|||||||
@@ -22,25 +22,25 @@ interface Signature {
|
|||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#update(byte)}
|
* @see {@link java.security.Signature#update(byte)}
|
||||||
*/
|
*/
|
||||||
void update(byte b);
|
void update(byte b) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#update(byte[])}
|
* @see {@link java.security.Signature#update(byte[])}
|
||||||
*/
|
*/
|
||||||
void update(byte[] b);
|
void update(byte[] b) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#update(byte[], int, int)}
|
* @see {@link java.security.Signature#update(byte[], int, int)}
|
||||||
*/
|
*/
|
||||||
void update(byte[] b, int off, int len);
|
void update(byte[] b, int off, int len) throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#sign()}
|
* @see {@link java.security.Signature#sign()}
|
||||||
*/
|
*/
|
||||||
byte[] sign();
|
byte[] sign() throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link java.security.Signature#verify(byte[])}
|
* @see {@link java.security.Signature#verify(byte[])}
|
||||||
*/
|
*/
|
||||||
boolean verify(byte[] signature);
|
boolean verify(byte[] signature) throws GeneralSecurityException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamDecrypter createInvitationStreamDecrypter(InputStream in,
|
public StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -117,7 +119,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
|
|
||||||
private void readStreamHeader() throws IOException {
|
private void readStreamHeader() throws IOException {
|
||||||
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
||||||
byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH];
|
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
|
||||||
// Read the stream header
|
// Read the stream header
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (offset < STREAM_HEADER_LENGTH) {
|
while (offset < STREAM_HEADER_LENGTH) {
|
||||||
@@ -126,21 +128,35 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
if (read == -1) throw new EOFException();
|
if (read == -1) throw new EOFException();
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
// The nonce consists of the stream number followed by the IV
|
// Extract the nonce
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
|
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
|
||||||
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce,
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
|
|
||||||
// Decrypt and authenticate the stream header
|
// Decrypt and authenticate the stream header
|
||||||
try {
|
try {
|
||||||
cipher.init(false, streamHeaderKey, streamHeaderNonce);
|
cipher.init(false, streamHeaderKey, streamHeaderNonce);
|
||||||
int decrypted = cipher.process(streamHeaderCiphertext,
|
int decrypted = cipher.process(streamHeaderCiphertext,
|
||||||
STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH,
|
STREAM_HEADER_NONCE_LENGTH,
|
||||||
|
STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH,
|
||||||
streamHeaderPlaintext, 0);
|
streamHeaderPlaintext, 0);
|
||||||
if (decrypted != SecretKey.LENGTH) throw new RuntimeException();
|
if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
|
||||||
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
frameKey = new SecretKey(streamHeaderPlaintext);
|
// Check the protocol version
|
||||||
|
int receivedProtocolVersion =
|
||||||
|
ByteUtils.readUint16(streamHeaderPlaintext, 0);
|
||||||
|
if (receivedProtocolVersion != PROTOCOL_VERSION)
|
||||||
|
throw new FormatException();
|
||||||
|
// Check the stream number
|
||||||
|
long receivedStreamNumber = ByteUtils.readUint64(streamHeaderPlaintext,
|
||||||
|
INT_16_BYTES);
|
||||||
|
if (receivedStreamNumber != streamNumber) throw new FormatException();
|
||||||
|
// Extract the frame key
|
||||||
|
byte[] frameKeyBytes = new byte[SecretKey.LENGTH];
|
||||||
|
System.arraycopy(streamHeaderPlaintext, INT_16_BYTES + INT_64_BYTES,
|
||||||
|
frameKeyBytes, 0, SecretKey.LENGTH);
|
||||||
|
frameKey = new SecretKey(frameKeyBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.transport.StreamContext;
|
import org.briarproject.bramble.api.transport.StreamContext;
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -21,12 +23,15 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
|
|||||||
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final TransportCrypto transportCrypto;
|
||||||
private final Provider<AuthenticatedCipher> cipherProvider;
|
private final Provider<AuthenticatedCipher> cipherProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
StreamEncrypterFactoryImpl(CryptoComponent crypto,
|
||||||
|
TransportCrypto transportCrypto,
|
||||||
Provider<AuthenticatedCipher> cipherProvider) {
|
Provider<AuthenticatedCipher> cipherProvider) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.transportCrypto = transportCrypto;
|
||||||
this.cipherProvider = cipherProvider;
|
this.cipherProvider = cipherProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,22 +41,23 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber);
|
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION,
|
||||||
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
streamNumber);
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
|
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
|
||||||
streamHeaderIv, ctx.getHeaderKey(), frameKey);
|
streamHeaderNonce, ctx.getHeaderKey(), frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
public StreamEncrypter createContactExchangeStreamDecrypter(
|
||||||
SecretKey headerKey) {
|
OutputStream out, SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv,
|
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||||
headerKey, frameKey);
|
headerKey, frameKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -33,7 +35,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private final long streamNumber;
|
private final long streamNumber;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] tag;
|
private final byte[] tag;
|
||||||
private final byte[] streamHeaderIv;
|
private final byte[] streamHeaderNonce;
|
||||||
private final byte[] frameNonce, frameHeader;
|
private final byte[] frameNonce, frameHeader;
|
||||||
private final byte[] framePlaintext, frameCiphertext;
|
private final byte[] framePlaintext, frameCiphertext;
|
||||||
|
|
||||||
@@ -41,13 +43,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private boolean writeTag, writeStreamHeader;
|
private boolean writeTag, writeStreamHeader;
|
||||||
|
|
||||||
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
||||||
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv,
|
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
|
||||||
SecretKey streamHeaderKey, SecretKey frameKey) {
|
SecretKey streamHeaderKey, SecretKey frameKey) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.cipher = cipher;
|
this.cipher = cipher;
|
||||||
this.streamNumber = streamNumber;
|
this.streamNumber = streamNumber;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.streamHeaderIv = streamHeaderIv;
|
this.streamHeaderNonce = streamHeaderNonce;
|
||||||
this.streamHeaderKey = streamHeaderKey;
|
this.streamHeaderKey = streamHeaderKey;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
frameNonce = new byte[FRAME_NONCE_LENGTH];
|
frameNonce = new byte[FRAME_NONCE_LENGTH];
|
||||||
@@ -114,22 +116,23 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeStreamHeader() throws IOException {
|
private void writeStreamHeader() throws IOException {
|
||||||
// The nonce consists of the stream number followed by the IV
|
// The header contains the protocol version, stream number and frame key
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
|
||||||
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
|
ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
|
||||||
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES,
|
ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
INT_16_BYTES);
|
||||||
byte[] streamHeaderPlaintext = frameKey.getBytes();
|
System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
|
||||||
|
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
|
||||||
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
||||||
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0,
|
System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
// Encrypt and authenticate the frame key
|
// Encrypt and authenticate the stream header key
|
||||||
try {
|
try {
|
||||||
cipher.init(true, streamHeaderKey, streamHeaderNonce);
|
cipher.init(true, streamHeaderKey, streamHeaderNonce);
|
||||||
int encrypted = cipher.process(streamHeaderPlaintext, 0,
|
int encrypted = cipher.process(streamHeaderPlaintext, 0,
|
||||||
SecretKey.LENGTH, streamHeaderCiphertext,
|
STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
if (encrypted != SecretKey.LENGTH + MAC_LENGTH)
|
if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException badCipher) {
|
} catch (GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
import org.spongycastle.crypto.Digest;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
|
class TransportCryptoImpl implements TransportCrypto {
|
||||||
|
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TransportCryptoImpl(CryptoComponent crypto) {
|
||||||
|
this.crypto = crypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportKeys deriveTransportKeys(TransportId t,
|
||||||
|
SecretKey master, long rotationPeriod, boolean alice) {
|
||||||
|
// Keys for the previous period are derived from the master secret
|
||||||
|
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
||||||
|
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
||||||
|
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
||||||
|
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
||||||
|
// Derive the keys for the current and next periods
|
||||||
|
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
||||||
|
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
||||||
|
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
||||||
|
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
||||||
|
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
||||||
|
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
||||||
|
// Initialise the reordering windows and stream counters
|
||||||
|
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
|
||||||
|
rotationPeriod - 1);
|
||||||
|
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
||||||
|
rotationPeriod);
|
||||||
|
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||||
|
rotationPeriod + 1);
|
||||||
|
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||||
|
rotationPeriod);
|
||||||
|
// Collect and return the keys
|
||||||
|
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportKeys rotateTransportKeys(TransportKeys k,
|
||||||
|
long rotationPeriod) {
|
||||||
|
if (k.getRotationPeriod() >= rotationPeriod) return k;
|
||||||
|
IncomingKeys inPrev = k.getPreviousIncomingKeys();
|
||||||
|
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||||
|
IncomingKeys inNext = k.getNextIncomingKeys();
|
||||||
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
|
long startPeriod = outCurr.getRotationPeriod();
|
||||||
|
// Rotate the keys
|
||||||
|
for (long p = startPeriod + 1; p <= rotationPeriod; 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);
|
||||||
|
}
|
||||||
|
// Collect and return the keys
|
||||||
|
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
|
||||||
|
outCurr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
||||||
|
byte[] period = new byte[INT_64_BYTES];
|
||||||
|
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
||||||
|
return crypto.deriveKey(ROTATE_LABEL, k, period);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
||||||
|
boolean alice) {
|
||||||
|
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
|
||||||
|
byte[] id = StringUtils.toUtf8(t.getString());
|
||||||
|
return crypto.deriveKey(label, master, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
||||||
|
boolean alice) {
|
||||||
|
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
|
||||||
|
byte[] id = StringUtils.toUtf8(t.getString());
|
||||||
|
return crypto.deriveKey(label, master, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
|
long streamNumber) {
|
||||||
|
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
|
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
// Initialise the PRF
|
||||||
|
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||||
|
// The output of the PRF must be long enough to use as a tag
|
||||||
|
int macLength = prf.getDigestSize();
|
||||||
|
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||||
|
// The input is the protocol version as a 16-bit integer, followed by
|
||||||
|
// the stream number as a 64-bit integer
|
||||||
|
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||||
|
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||||
|
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||||
|
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||||
|
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||||
|
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||||
|
byte[] mac = new byte[macLength];
|
||||||
|
prf.doFinal(mac, 0);
|
||||||
|
// The output is the first TAG_LENGTH bytes of the MAC
|
||||||
|
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,25 +70,7 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
|
|||||||
byte[] subKey = new byte[SUBKEY_LENGTH];
|
byte[] subKey = new byte[SUBKEY_LENGTH];
|
||||||
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
|
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
|
||||||
|
|
||||||
// Reverse the order of the Poly130 subkey
|
// Clamp the subkey
|
||||||
//
|
|
||||||
// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
|
|
||||||
// subkey for crypto_onetimeauth_poly1305, which interprets it
|
|
||||||
// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
|
|
||||||
// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
|
|
||||||
// where the XSalsa20 output is defined as (r, s, t, ...).
|
|
||||||
//
|
|
||||||
// BC's Poly1305 implementation interprets the subkey as
|
|
||||||
// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
|
|
||||||
// the reference implementation).
|
|
||||||
//
|
|
||||||
// To be NaCl-compatible, we reverse the subkey.
|
|
||||||
System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
|
|
||||||
System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0,
|
|
||||||
SUBKEY_LENGTH / 2);
|
|
||||||
System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2,
|
|
||||||
SUBKEY_LENGTH / 2);
|
|
||||||
// Now we can clamp the correct part of the subkey
|
|
||||||
Poly1305KeyGenerator.clamp(subKey);
|
Poly1305KeyGenerator.clamp(subKey);
|
||||||
|
|
||||||
// Initialize Poly1305 with the subkey
|
// Initialize Poly1305 with the subkey
|
||||||
|
|||||||
@@ -90,8 +90,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
private final ReentrantReadWriteLock lock =
|
private final ReentrantReadWriteLock lock =
|
||||||
new ReentrantReadWriteLock(true);
|
new ReentrantReadWriteLock(true);
|
||||||
|
|
||||||
private volatile int shutdownHandle = -1;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
|
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
|
||||||
ShutdownManager shutdown) {
|
ShutdownManager shutdown) {
|
||||||
@@ -103,26 +101,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open() throws DbException {
|
public boolean open() throws DbException {
|
||||||
Runnable shutdownHook = new Runnable() {
|
boolean reopened = db.open();
|
||||||
@Override
|
shutdown.addShutdownHook(() -> {
|
||||||
public void run() {
|
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
|
||||||
boolean reopened = db.open();
|
|
||||||
shutdownHandle = shutdown.addShutdownHook(shutdownHook);
|
|
||||||
return reopened;
|
return reopened;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
if (closed.getAndSet(true)) return;
|
if (closed.getAndSet(true)) return;
|
||||||
shutdown.removeShutdownHook(shutdownHandle);
|
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,11 +133,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return new Transaction(db.startTransaction(), readOnly);
|
return new Transaction(db.startTransaction(), readOnly);
|
||||||
} catch (DbException e) {
|
} catch (DbException | RuntimeException e) {
|
||||||
if (readOnly) lock.readLock().unlock();
|
|
||||||
else lock.writeLock().unlock();
|
|
||||||
throw e;
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
if (readOnly) lock.readLock().unlock();
|
if (readOnly) lock.readLock().unlock();
|
||||||
else lock.writeLock().unlock();
|
else lock.writeLock().unlock();
|
||||||
throw e;
|
throw e;
|
||||||
@@ -331,7 +319,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
Collection<MessageId> ids = db.getMessagesToSend(txn, c, maxLength);
|
Collection<MessageId> ids = db.getMessagesToSend(txn, c, maxLength);
|
||||||
List<byte[]> messages = new ArrayList<byte[]>(ids.size());
|
List<byte[]> messages = new ArrayList<>(ids.size());
|
||||||
for (MessageId m : ids) {
|
for (MessageId m : ids) {
|
||||||
messages.add(db.getRawMessage(txn, m));
|
messages.add(db.getRawMessage(txn, m));
|
||||||
db.updateExpiryTime(txn, c, m, maxLatency);
|
db.updateExpiryTime(txn, c, m, maxLatency);
|
||||||
@@ -381,7 +369,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
Collection<MessageId> ids = db.getRequestedMessagesToSend(txn, c,
|
Collection<MessageId> ids = db.getRequestedMessagesToSend(txn, c,
|
||||||
maxLength);
|
maxLength);
|
||||||
List<byte[]> messages = new ArrayList<byte[]>(ids.size());
|
List<byte[]> messages = new ArrayList<>(ids.size());
|
||||||
for (MessageId m : ids) {
|
for (MessageId m : ids) {
|
||||||
messages.add(db.getRawMessage(txn, m));
|
messages.add(db.getRawMessage(txn, m));
|
||||||
db.updateExpiryTime(txn, c, m, maxLatency);
|
db.updateExpiryTime(txn, c, m, maxLatency);
|
||||||
@@ -661,7 +649,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsContact(txn, c))
|
if (!db.containsContact(txn, c))
|
||||||
throw new NoSuchContactException();
|
throw new NoSuchContactException();
|
||||||
Collection<MessageId> acked = new ArrayList<MessageId>();
|
Collection<MessageId> acked = new ArrayList<>();
|
||||||
for (MessageId m : a.getMessageIds()) {
|
for (MessageId m : a.getMessageIds()) {
|
||||||
if (db.containsVisibleMessage(txn, c, m)) {
|
if (db.containsVisibleMessage(txn, c, m)) {
|
||||||
db.raiseSeenFlag(txn, c, m);
|
db.raiseSeenFlag(txn, c, m);
|
||||||
@@ -770,6 +758,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
transaction.attach(new LocalAuthorRemovedEvent(a));
|
transaction.attach(new LocalAuthorRemovedEvent(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeMessage(Transaction transaction, MessageId m)
|
||||||
|
throws DbException {
|
||||||
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsMessage(txn, m))
|
||||||
|
throw new NoSuchMessageException();
|
||||||
|
db.removeMessage(txn, m);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeTransport(Transaction transaction, TransportId t)
|
public void removeTransport(Transaction transaction, TransportId t)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -886,8 +884,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
Map<ContactId, TransportKeys> keys) throws DbException {
|
Map<ContactId, TransportKeys> keys) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
Map<ContactId, TransportKeys> filtered =
|
Map<ContactId, TransportKeys> filtered = new HashMap<>();
|
||||||
new HashMap<ContactId, TransportKeys>();
|
|
||||||
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
|
||||||
ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
TransportKeys k = e.getValue();
|
TransportKeys k = e.getValue();
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class DatabaseExecutorModule {
|
|||||||
|
|
||||||
public DatabaseExecutorModule() {
|
public DatabaseExecutorModule() {
|
||||||
// Use an unbounded queue
|
// Use an unbounded queue
|
||||||
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
|
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||||
// Discard tasks that are submitted during shutdown
|
// Discard tasks that are submitted during shutdown
|
||||||
RejectedExecutionHandler policy =
|
RejectedExecutionHandler policy =
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class DatabaseModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
DatabaseComponent provideDatabaseComponent(Database<Connection> db,
|
DatabaseComponent provideDatabaseComponent(Database<Connection> db,
|
||||||
EventBus eventBus, ShutdownManager shutdown) {
|
EventBus eventBus, ShutdownManager shutdown) {
|
||||||
return new DatabaseComponentImpl<Connection>(db, Connection.class,
|
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
||||||
eventBus, shutdown);
|
shutdown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,21 +22,23 @@ import javax.inject.Inject;
|
|||||||
class H2Database extends JdbcDatabase {
|
class H2Database extends JdbcDatabase {
|
||||||
|
|
||||||
private static final String HASH_TYPE = "BINARY(32)";
|
private static final String HASH_TYPE = "BINARY(32)";
|
||||||
|
private static final String SECRET_TYPE = "BINARY(32)";
|
||||||
private static final String BINARY_TYPE = "BINARY";
|
private static final String BINARY_TYPE = "BINARY";
|
||||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||||
private static final String SECRET_TYPE = "BINARY(32)";
|
private static final String STRING_TYPE = "VARCHAR";
|
||||||
|
|
||||||
private final DatabaseConfig config;
|
private final DatabaseConfig config;
|
||||||
private final String url;
|
private final String url;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
H2Database(DatabaseConfig config, Clock clock) {
|
H2Database(DatabaseConfig config, Clock clock) {
|
||||||
super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock);
|
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||||
|
clock);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
File dir = config.getDatabaseDirectory();
|
File dir = config.getDatabaseDirectory();
|
||||||
String path = new File(dir, "db").getAbsolutePath();
|
String path = new File(dir, "db").getAbsolutePath();
|
||||||
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
|
||||||
+ ";WRITE_DELAY=0;DB_CLOSE_ON_EXIT=false";
|
+ ";WRITE_DELAY=0";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains all the HSQLDB-specific code for the database.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
class HyperSqlDatabase extends JdbcDatabase {
|
||||||
|
|
||||||
|
private static final String HASH_TYPE = "BINARY(32)";
|
||||||
|
private static final String SECRET_TYPE = "BINARY(32)";
|
||||||
|
private static final String BINARY_TYPE = "BINARY";
|
||||||
|
private static final String COUNTER_TYPE =
|
||||||
|
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||||
|
private static final String STRING_TYPE = "VARCHAR";
|
||||||
|
|
||||||
|
private final DatabaseConfig config;
|
||||||
|
private final String url;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
|
||||||
|
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||||
|
clock);
|
||||||
|
this.config = config;
|
||||||
|
File dir = config.getDatabaseDirectory();
|
||||||
|
String path = new File(dir, "db").getAbsolutePath();
|
||||||
|
url = "jdbc:hsqldb:file:" + path
|
||||||
|
+ ";sql.enforce_size=false;allow_empty_batch=true"
|
||||||
|
+ ";encrypt_lobs=true;crypt_type=AES";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() throws DbException {
|
||||||
|
boolean reopen = config.databaseExists();
|
||||||
|
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||||
|
super.open("org.hsqldb.jdbc.JDBCDriver", reopen);
|
||||||
|
return reopen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws DbException {
|
||||||
|
try {
|
||||||
|
super.closeAllConnections();
|
||||||
|
Connection c = createConnection();
|
||||||
|
Statement s = c.createStatement();
|
||||||
|
s.executeQuery("SHUTDOWN");
|
||||||
|
s.close();
|
||||||
|
c.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getFreeSpace() throws DbException {
|
||||||
|
File dir = config.getDatabaseDirectory();
|
||||||
|
long maxSize = config.getMaxSize();
|
||||||
|
long free = dir.getFreeSpace();
|
||||||
|
long used = getDiskSpace(dir);
|
||||||
|
long quota = maxSize - used;
|
||||||
|
return Math.min(free, quota);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getDiskSpace(File f) {
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
long total = 0;
|
||||||
|
File[] children = f.listFiles();
|
||||||
|
if (children != null)
|
||||||
|
for (File child : children) total += getDiskSpace(child);
|
||||||
|
return total;
|
||||||
|
} else if (f.isFile()) {
|
||||||
|
return f.length();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Connection createConnection() throws SQLException {
|
||||||
|
SecretKey key = config.getEncryptionKey();
|
||||||
|
if (key == null) throw new IllegalStateException();
|
||||||
|
String hex = StringUtils.toHexString(key.getBytes());
|
||||||
|
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,32 +68,32 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
private static final int SCHEMA_VERSION = 30;
|
private static final int SCHEMA_VERSION = 31;
|
||||||
private static final int MIN_SCHEMA_VERSION = 30;
|
private static final int MIN_SCHEMA_VERSION = 31;
|
||||||
|
|
||||||
private static final String CREATE_SETTINGS =
|
private static final String CREATE_SETTINGS =
|
||||||
"CREATE TABLE settings"
|
"CREATE TABLE settings"
|
||||||
+ " (namespace VARCHAR NOT NULL,"
|
+ " (namespace _STRING NOT NULL,"
|
||||||
+ " key VARCHAR NOT NULL,"
|
+ " settingKey _STRING NOT NULL,"
|
||||||
+ " value VARCHAR NOT NULL,"
|
+ " value _STRING NOT NULL,"
|
||||||
+ " PRIMARY KEY (namespace, key))";
|
+ " PRIMARY KEY (namespace, settingKey))";
|
||||||
|
|
||||||
private static final String CREATE_LOCAL_AUTHORS =
|
private static final String CREATE_LOCAL_AUTHORS =
|
||||||
"CREATE TABLE localAuthors"
|
"CREATE TABLE localAuthors"
|
||||||
+ " (authorId HASH NOT NULL,"
|
+ " (authorId _HASH NOT NULL,"
|
||||||
+ " name VARCHAR NOT NULL,"
|
+ " name _STRING NOT NULL,"
|
||||||
+ " publicKey BINARY NOT NULL,"
|
+ " publicKey _BINARY NOT NULL,"
|
||||||
+ " privateKey BINARY NOT NULL,"
|
+ " privateKey _BINARY NOT NULL,"
|
||||||
+ " created BIGINT NOT NULL,"
|
+ " created BIGINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (authorId))";
|
+ " PRIMARY KEY (authorId))";
|
||||||
|
|
||||||
private static final String CREATE_CONTACTS =
|
private static final String CREATE_CONTACTS =
|
||||||
"CREATE TABLE contacts"
|
"CREATE TABLE contacts"
|
||||||
+ " (contactId COUNTER,"
|
+ " (contactId _COUNTER,"
|
||||||
+ " authorId HASH NOT NULL,"
|
+ " authorId _HASH NOT NULL,"
|
||||||
+ " name VARCHAR NOT NULL,"
|
+ " name _STRING NOT NULL,"
|
||||||
+ " publicKey BINARY NOT NULL,"
|
+ " publicKey _BINARY NOT NULL,"
|
||||||
+ " localAuthorId HASH NOT NULL,"
|
+ " localAuthorId _HASH NOT NULL,"
|
||||||
+ " verified BOOLEAN NOT NULL,"
|
+ " verified BOOLEAN NOT NULL,"
|
||||||
+ " active BOOLEAN NOT NULL,"
|
+ " active BOOLEAN NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId),"
|
+ " PRIMARY KEY (contactId),"
|
||||||
@@ -103,17 +103,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_GROUPS =
|
private static final String CREATE_GROUPS =
|
||||||
"CREATE TABLE groups"
|
"CREATE TABLE groups"
|
||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId _HASH NOT NULL,"
|
||||||
+ " clientId VARCHAR NOT NULL,"
|
+ " clientId _STRING NOT NULL,"
|
||||||
+ " descriptor BINARY NOT NULL,"
|
+ " descriptor _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId))";
|
+ " PRIMARY KEY (groupId))";
|
||||||
|
|
||||||
private static final String CREATE_GROUP_METADATA =
|
private static final String CREATE_GROUP_METADATA =
|
||||||
"CREATE TABLE groupMetadata"
|
"CREATE TABLE groupMetadata"
|
||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId _HASH NOT NULL,"
|
||||||
+ " key VARCHAR NOT NULL,"
|
+ " metaKey _STRING NOT NULL,"
|
||||||
+ " value BINARY NOT NULL,"
|
+ " value _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId, key),"
|
+ " PRIMARY KEY (groupId, metaKey),"
|
||||||
+ " FOREIGN KEY (groupId)"
|
+ " FOREIGN KEY (groupId)"
|
||||||
+ " REFERENCES groups (groupId)"
|
+ " REFERENCES groups (groupId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
@@ -121,7 +121,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private static final String CREATE_GROUP_VISIBILITIES =
|
private static final String CREATE_GROUP_VISIBILITIES =
|
||||||
"CREATE TABLE groupVisibilities"
|
"CREATE TABLE groupVisibilities"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " groupId HASH NOT NULL,"
|
+ " groupId _HASH NOT NULL,"
|
||||||
+ " shared BOOLEAN NOT NULL,"
|
+ " shared BOOLEAN NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, groupId),"
|
+ " PRIMARY KEY (contactId, groupId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
@@ -133,8 +133,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_MESSAGES =
|
private static final String CREATE_MESSAGES =
|
||||||
"CREATE TABLE messages"
|
"CREATE TABLE messages"
|
||||||
+ " (messageId HASH NOT NULL,"
|
+ " (messageId _HASH NOT NULL,"
|
||||||
+ " groupId HASH NOT NULL,"
|
+ " groupId _HASH NOT NULL,"
|
||||||
+ " timestamp BIGINT NOT NULL,"
|
+ " timestamp BIGINT NOT NULL,"
|
||||||
+ " state INT NOT NULL,"
|
+ " state INT NOT NULL,"
|
||||||
+ " shared BOOLEAN NOT NULL,"
|
+ " shared BOOLEAN NOT NULL,"
|
||||||
@@ -147,19 +147,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_MESSAGE_METADATA =
|
private static final String CREATE_MESSAGE_METADATA =
|
||||||
"CREATE TABLE messageMetadata"
|
"CREATE TABLE messageMetadata"
|
||||||
+ " (messageId HASH NOT NULL,"
|
+ " (messageId _HASH NOT NULL,"
|
||||||
+ " key VARCHAR NOT NULL,"
|
+ " metaKey _STRING NOT NULL,"
|
||||||
+ " value BINARY NOT NULL,"
|
+ " value _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId, key),"
|
+ " PRIMARY KEY (messageId, metaKey),"
|
||||||
+ " FOREIGN KEY (messageId)"
|
+ " FOREIGN KEY (messageId)"
|
||||||
+ " REFERENCES messages (messageId)"
|
+ " REFERENCES messages (messageId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_MESSAGE_DEPENDENCIES =
|
private static final String CREATE_MESSAGE_DEPENDENCIES =
|
||||||
"CREATE TABLE messageDependencies"
|
"CREATE TABLE messageDependencies"
|
||||||
+ " (groupId HASH NOT NULL,"
|
+ " (groupId _HASH NOT NULL,"
|
||||||
+ " messageId HASH NOT NULL,"
|
+ " messageId _HASH NOT NULL,"
|
||||||
+ " dependencyId HASH NOT NULL," // Not a foreign key
|
+ " dependencyId _HASH NOT NULL," // Not a foreign key
|
||||||
+ " FOREIGN KEY (groupId)"
|
+ " FOREIGN KEY (groupId)"
|
||||||
+ " REFERENCES groups (groupId)"
|
+ " REFERENCES groups (groupId)"
|
||||||
+ " ON DELETE CASCADE,"
|
+ " ON DELETE CASCADE,"
|
||||||
@@ -169,7 +169,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_OFFERS =
|
private static final String CREATE_OFFERS =
|
||||||
"CREATE TABLE offers"
|
"CREATE TABLE offers"
|
||||||
+ " (messageId HASH NOT NULL," // Not a foreign key
|
+ " (messageId _HASH NOT NULL," // Not a foreign key
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId, contactId),"
|
+ " PRIMARY KEY (messageId, contactId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
@@ -178,7 +178,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_STATUSES =
|
private static final String CREATE_STATUSES =
|
||||||
"CREATE TABLE statuses"
|
"CREATE TABLE statuses"
|
||||||
+ " (messageId HASH NOT NULL,"
|
+ " (messageId _HASH NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " ack BOOLEAN NOT NULL,"
|
+ " ack BOOLEAN NOT NULL,"
|
||||||
+ " seen BOOLEAN NOT NULL,"
|
+ " seen BOOLEAN NOT NULL,"
|
||||||
@@ -195,20 +195,20 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_TRANSPORTS =
|
private static final String CREATE_TRANSPORTS =
|
||||||
"CREATE TABLE transports"
|
"CREATE TABLE transports"
|
||||||
+ " (transportId VARCHAR NOT NULL,"
|
+ " (transportId _STRING NOT NULL,"
|
||||||
+ " maxLatency INT NOT NULL,"
|
+ " maxLatency INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (transportId))";
|
+ " PRIMARY KEY (transportId))";
|
||||||
|
|
||||||
private static final String CREATE_INCOMING_KEYS =
|
private static final String CREATE_INCOMING_KEYS =
|
||||||
"CREATE TABLE incomingKeys"
|
"CREATE TABLE incomingKeys"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " transportId VARCHAR NOT NULL,"
|
+ " transportId _STRING NOT NULL,"
|
||||||
+ " period BIGINT NOT NULL,"
|
+ " rotationPeriod BIGINT NOT NULL,"
|
||||||
+ " tagKey SECRET NOT NULL,"
|
+ " tagKey _SECRET NOT NULL,"
|
||||||
+ " headerKey SECRET NOT NULL,"
|
+ " headerKey _SECRET NOT NULL,"
|
||||||
+ " base BIGINT NOT NULL,"
|
+ " base BIGINT NOT NULL,"
|
||||||
+ " bitmap BINARY NOT NULL,"
|
+ " bitmap _BINARY NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, transportId, period),"
|
+ " PRIMARY KEY (contactId, transportId, rotationPeriod),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
+ " REFERENCES contacts (contactId)"
|
+ " REFERENCES contacts (contactId)"
|
||||||
+ " ON DELETE CASCADE,"
|
+ " ON DELETE CASCADE,"
|
||||||
@@ -219,10 +219,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private static final String CREATE_OUTGOING_KEYS =
|
private static final String CREATE_OUTGOING_KEYS =
|
||||||
"CREATE TABLE outgoingKeys"
|
"CREATE TABLE outgoingKeys"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " transportId VARCHAR NOT NULL,"
|
+ " transportId _STRING NOT NULL,"
|
||||||
+ " period BIGINT NOT NULL,"
|
+ " rotationPeriod BIGINT NOT NULL,"
|
||||||
+ " tagKey SECRET NOT NULL,"
|
+ " tagKey _SECRET NOT NULL,"
|
||||||
+ " headerKey SECRET NOT NULL,"
|
+ " headerKey _SECRET NOT NULL,"
|
||||||
+ " stream BIGINT NOT NULL,"
|
+ " stream BIGINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, transportId),"
|
+ " PRIMARY KEY (contactId, transportId),"
|
||||||
+ " FOREIGN KEY (contactId)"
|
+ " FOREIGN KEY (contactId)"
|
||||||
@@ -232,15 +232,40 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " REFERENCES transports (transportId)"
|
+ " REFERENCES transports (transportId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
|
private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
|
||||||
|
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
|
||||||
|
+ " ON contacts (authorId)";
|
||||||
|
|
||||||
|
private static final String INDEX_MESSAGES_BY_GROUP_ID =
|
||||||
|
"CREATE INDEX IF NOT EXISTS messagesByGroupId"
|
||||||
|
+ " ON messages (groupId)";
|
||||||
|
|
||||||
|
private static final String INDEX_OFFERS_BY_CONTACT_ID =
|
||||||
|
"CREATE INDEX IF NOT EXISTS offersByContactId"
|
||||||
|
+ " ON offers (contactId)";
|
||||||
|
|
||||||
|
private static final String INDEX_GROUPS_BY_CLIENT_ID =
|
||||||
|
"CREATE INDEX IF NOT EXISTS groupsByClientId"
|
||||||
|
+ " ON groups (clientId)";
|
||||||
|
|
||||||
|
private static final String INDEX_MESSAGE_METADATA_BY_MESSAGE_ID =
|
||||||
|
"CREATE INDEX IF NOT EXISTS messageMetadataByMessageId"
|
||||||
|
+ " ON messageMetadata (messageId)";
|
||||||
|
|
||||||
|
private static final String INDEX_GROUP_METADATA_BY_GROUP_ID =
|
||||||
|
"CREATE INDEX IF NOT EXISTS groupMetadataByGroupId"
|
||||||
|
+ " ON groupMetadata (groupId)";
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(JdbcDatabase.class.getName());
|
Logger.getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
// Different database libraries use different names for certain types
|
// Different database libraries use different names for certain types
|
||||||
private final String hashType, binaryType, counterType, secretType;
|
private final String hashType, secretType, binaryType;
|
||||||
|
private final String counterType, stringType;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
|
||||||
private final LinkedList<Connection> connections =
|
// Locking: connectionsLock
|
||||||
new LinkedList<Connection>(); // Locking: connectionsLock
|
private final LinkedList<Connection> connections = new LinkedList<>();
|
||||||
|
|
||||||
private int openConnections = 0; // Locking: connectionsLock
|
private int openConnections = 0; // Locking: connectionsLock
|
||||||
private boolean closed = false; // Locking: connectionsLock
|
private boolean closed = false; // Locking: connectionsLock
|
||||||
@@ -251,12 +276,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private final Lock connectionsLock = new ReentrantLock();
|
private final Lock connectionsLock = new ReentrantLock();
|
||||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||||
|
|
||||||
JdbcDatabase(String hashType, String binaryType, String counterType,
|
JdbcDatabase(String hashType, String secretType, String binaryType,
|
||||||
String secretType, Clock clock) {
|
String counterType, String stringType, Clock clock) {
|
||||||
this.hashType = hashType;
|
this.hashType = hashType;
|
||||||
|
this.secretType = secretType;
|
||||||
this.binaryType = binaryType;
|
this.binaryType = binaryType;
|
||||||
this.counterType = counterType;
|
this.counterType = counterType;
|
||||||
this.secretType = secretType;
|
this.stringType = stringType;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +293,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
// Open the database and create the tables if necessary
|
// Open the database and create the tables and indexes if necessary
|
||||||
Connection txn = startTransaction();
|
Connection txn = startTransaction();
|
||||||
try {
|
try {
|
||||||
if (reopen) {
|
if (reopen) {
|
||||||
@@ -276,6 +302,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
createTables(txn);
|
createTables(txn);
|
||||||
storeSchemaVersion(txn);
|
storeSchemaVersion(txn);
|
||||||
}
|
}
|
||||||
|
createIndexes(txn);
|
||||||
commitTransaction(txn);
|
commitTransaction(txn);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
@@ -340,17 +367,35 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createIndexes(Connection txn) throws DbException {
|
||||||
|
Statement s = null;
|
||||||
|
try {
|
||||||
|
s = txn.createStatement();
|
||||||
|
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR_ID);
|
||||||
|
s.executeUpdate(INDEX_MESSAGES_BY_GROUP_ID);
|
||||||
|
s.executeUpdate(INDEX_OFFERS_BY_CONTACT_ID);
|
||||||
|
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID);
|
||||||
|
s.executeUpdate(INDEX_MESSAGE_METADATA_BY_MESSAGE_ID);
|
||||||
|
s.executeUpdate(INDEX_GROUP_METADATA_BY_GROUP_ID);
|
||||||
|
s.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
tryToClose(s);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String insertTypeNames(String s) {
|
private String insertTypeNames(String s) {
|
||||||
s = s.replaceAll("HASH", hashType);
|
s = s.replaceAll("_HASH", hashType);
|
||||||
s = s.replaceAll("BINARY", binaryType);
|
s = s.replaceAll("_SECRET", secretType);
|
||||||
s = s.replaceAll("COUNTER", counterType);
|
s = s.replaceAll("_BINARY", binaryType);
|
||||||
s = s.replaceAll("SECRET", secretType);
|
s = s.replaceAll("_COUNTER", counterType);
|
||||||
|
s = s.replaceAll("_STRING", stringType);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection startTransaction() throws DbException {
|
public Connection startTransaction() throws DbException {
|
||||||
Connection txn = null;
|
Connection txn;
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
if (closed) throw new DbClosedException();
|
if (closed) throw new DbClosedException();
|
||||||
@@ -458,7 +503,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Create a contact row
|
// Create a contact row
|
||||||
String sql = "INSERT INTO contacts"
|
String sql = "INSERT INTO contacts"
|
||||||
+ " (authorId, name, publicKey, localAuthorId, verified, active)"
|
+ " (authorId, name, publicKey, localAuthorId,"
|
||||||
|
+ " verified, active)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, remote.getId().getBytes());
|
ps.setBytes(1, remote.getId().getBytes());
|
||||||
@@ -677,7 +723,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Store the incoming keys
|
// Store the incoming keys
|
||||||
String sql = "INSERT INTO incomingKeys (contactId, transportId,"
|
String sql = "INSERT INTO incomingKeys (contactId, transportId,"
|
||||||
+ " period, tagKey, headerKey, base, bitmap)"
|
+ " rotationPeriod, tagKey, headerKey, base, bitmap)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
@@ -712,8 +758,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows != 1) throw new DbStateException();
|
if (rows != 1) throw new DbStateException();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Store the outgoing keys
|
// Store the outgoing keys
|
||||||
sql = "INSERT INTO outgoingKeys (contactId, transportId, period,"
|
sql = "INSERT INTO outgoingKeys (contactId, transportId,"
|
||||||
+ " tagKey, headerKey, stream)"
|
+ " rotationPeriod, tagKey, headerKey, stream)"
|
||||||
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
+ " VALUES (?, ?, ?, ?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
@@ -993,7 +1039,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " FROM contacts";
|
+ " FROM contacts";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<Contact> contacts = new ArrayList<Contact>();
|
List<Contact> contacts = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
ContactId contactId = new ContactId(rs.getInt(1));
|
ContactId contactId = new ContactId(rs.getInt(1));
|
||||||
AuthorId authorId = new AuthorId(rs.getBytes(2));
|
AuthorId authorId = new AuthorId(rs.getBytes(2));
|
||||||
@@ -1027,7 +1073,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, local.getBytes());
|
ps.setBytes(1, local.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<ContactId> ids = new ArrayList<ContactId>();
|
List<ContactId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new ContactId(rs.getInt(1)));
|
while (rs.next()) ids.add(new ContactId(rs.getInt(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1052,7 +1098,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, remote.getBytes());
|
ps.setBytes(1, remote.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<Contact> contacts = new ArrayList<Contact>();
|
List<Contact> contacts = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
ContactId c = new ContactId(rs.getInt(1));
|
ContactId c = new ContactId(rs.getInt(1));
|
||||||
String name = rs.getString(2);
|
String name = rs.getString(2);
|
||||||
@@ -1108,7 +1154,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, c.getString());
|
ps.setString(1, c.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<Group> groups = new ArrayList<Group>();
|
List<Group> groups = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
GroupId id = new GroupId(rs.getBytes(1));
|
GroupId id = new GroupId(rs.getBytes(1));
|
||||||
byte[] descriptor = rs.getBytes(2);
|
byte[] descriptor = rs.getBytes(2);
|
||||||
@@ -1161,7 +1207,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, g.getBytes());
|
ps.setBytes(1, g.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<ContactId> visible = new ArrayList<ContactId>();
|
List<ContactId> visible = new ArrayList<>();
|
||||||
while (rs.next()) visible.add(new ContactId(rs.getInt(1)));
|
while (rs.next()) visible.add(new ContactId(rs.getInt(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1213,7 +1259,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " FROM localAuthors";
|
+ " FROM localAuthors";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<LocalAuthor> authors = new ArrayList<LocalAuthor>();
|
List<LocalAuthor> authors = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
||||||
String name = rs.getString(2);
|
String name = rs.getString(2);
|
||||||
@@ -1243,7 +1289,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, g.getBytes());
|
ps.setBytes(1, g.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1266,7 +1312,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, state.getValue());
|
ps.setInt(1, state.getValue());
|
||||||
ps.setBytes(2, g.getBytes());
|
ps.setBytes(2, g.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1293,7 +1339,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " JOIN messageMetadata AS md"
|
+ " JOIN messageMetadata AS md"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
+ " WHERE state = ? AND groupId = ?"
|
+ " WHERE state = ? AND groupId = ?"
|
||||||
+ " AND key = ? AND value = ?";
|
+ " AND metaKey = ? AND value = ?";
|
||||||
for (Entry<String, byte[]> e : query.entrySet()) {
|
for (Entry<String, byte[]> e : query.entrySet()) {
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, DELIVERED.getValue());
|
ps.setInt(1, DELIVERED.getValue());
|
||||||
@@ -1301,7 +1347,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setString(3, e.getKey());
|
ps.setString(3, e.getKey());
|
||||||
ps.setBytes(4, e.getValue());
|
ps.setBytes(4, e.getValue());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Set<MessageId> ids = new HashSet<MessageId>();
|
Set<MessageId> ids = new HashSet<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1325,7 +1371,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT m.messageId, key, value"
|
String sql = "SELECT m.messageId, metaKey, value"
|
||||||
+ " FROM messages AS m"
|
+ " FROM messages AS m"
|
||||||
+ " JOIN messageMetadata AS md"
|
+ " JOIN messageMetadata AS md"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
@@ -1335,7 +1381,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, DELIVERED.getValue());
|
ps.setInt(1, DELIVERED.getValue());
|
||||||
ps.setBytes(2, g.getBytes());
|
ps.setBytes(2, g.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Map<MessageId, Metadata> all = new HashMap<MessageId, Metadata>();
|
Map<MessageId, Metadata> all = new HashMap<>();
|
||||||
Metadata metadata = null;
|
Metadata metadata = null;
|
||||||
MessageId lastMessageId = null;
|
MessageId lastMessageId = null;
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
@@ -1364,8 +1410,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
Collection<MessageId> matches = getMessageIds(txn, g, query);
|
Collection<MessageId> matches = getMessageIds(txn, g, query);
|
||||||
if (matches.isEmpty()) return Collections.emptyMap();
|
if (matches.isEmpty()) return Collections.emptyMap();
|
||||||
// Retrieve the metadata for each match
|
// Retrieve the metadata for each match
|
||||||
Map<MessageId, Metadata> all = new HashMap<MessageId, Metadata>(
|
Map<MessageId, Metadata> all = new HashMap<>(matches.size());
|
||||||
matches.size());
|
|
||||||
for (MessageId m : matches) all.put(m, getMessageMetadata(txn, m));
|
for (MessageId m : matches) all.put(m, getMessageMetadata(txn, m));
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
@@ -1376,7 +1421,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM groupMetadata"
|
String sql = "SELECT metaKey, value FROM groupMetadata"
|
||||||
+ " WHERE groupId = ?";
|
+ " WHERE groupId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, g.getBytes());
|
ps.setBytes(1, g.getBytes());
|
||||||
@@ -1399,7 +1444,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||||
+ " JOIN messages AS m"
|
+ " JOIN messages AS m"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
+ " WHERE m.state = ? AND md.messageId = ?";
|
+ " WHERE m.state = ? AND md.messageId = ?";
|
||||||
@@ -1425,7 +1470,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM messageMetadata AS md"
|
String sql = "SELECT metaKey, value FROM messageMetadata AS md"
|
||||||
+ " JOIN messages AS m"
|
+ " JOIN messages AS m"
|
||||||
+ " ON m.messageId = md.messageId"
|
+ " ON m.messageId = md.messageId"
|
||||||
+ " WHERE (m.state = ? OR m.state = ?)"
|
+ " WHERE (m.state = ? OR m.state = ?)"
|
||||||
@@ -1463,7 +1508,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setBytes(1, g.getBytes());
|
ps.setBytes(1, g.getBytes());
|
||||||
ps.setInt(2, c.getInt());
|
ps.setInt(2, c.getInt());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageStatus> statuses = new ArrayList<MessageStatus>();
|
List<MessageStatus> statuses = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
MessageId messageId = new MessageId(rs.getBytes(1));
|
MessageId messageId = new MessageId(rs.getBytes(1));
|
||||||
boolean sent = rs.getBoolean(2);
|
boolean sent = rs.getBoolean(2);
|
||||||
@@ -1522,7 +1567,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Map<MessageId, State> dependencies = new HashMap<MessageId, State>();
|
Map<MessageId, State> dependencies = new HashMap<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
MessageId dependency = new MessageId(rs.getBytes(1));
|
MessageId dependency = new MessageId(rs.getBytes(1));
|
||||||
State state = State.fromValue(rs.getInt(2));
|
State state = State.fromValue(rs.getInt(2));
|
||||||
@@ -1560,7 +1605,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, m.getBytes());
|
ps.setBytes(1, m.getBytes());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Map<MessageId, State> dependents = new HashMap<MessageId, State>();
|
Map<MessageId, State> dependents = new HashMap<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
MessageId dependent = new MessageId(rs.getBytes(1));
|
MessageId dependent = new MessageId(rs.getBytes(1));
|
||||||
State state = State.fromValue(rs.getInt(2));
|
State state = State.fromValue(rs.getInt(2));
|
||||||
@@ -1612,7 +1657,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, maxMessages);
|
ps.setInt(2, maxMessages);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1648,7 +1693,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setLong(3, now);
|
ps.setLong(3, now);
|
||||||
ps.setInt(4, maxMessages);
|
ps.setInt(4, maxMessages);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1673,7 +1718,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setInt(2, maxMessages);
|
ps.setInt(2, maxMessages);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1708,7 +1753,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(2, DELIVERED.getValue());
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
ps.setLong(3, now);
|
ps.setLong(3, now);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
int total = 0;
|
int total = 0;
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
int length = rs.getInt(1);
|
int length = rs.getInt(1);
|
||||||
@@ -1750,7 +1795,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(1, state.getValue());
|
ps.setInt(1, state.getValue());
|
||||||
ps.setString(2, c.getString());
|
ps.setString(2, c.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1780,7 +1825,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, c.getString());
|
ps.setString(1, c.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1839,7 +1884,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.setInt(2, DELIVERED.getValue());
|
ps.setInt(2, DELIVERED.getValue());
|
||||||
ps.setLong(3, now);
|
ps.setLong(3, now);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<MessageId> ids = new ArrayList<MessageId>();
|
List<MessageId> ids = new ArrayList<>();
|
||||||
int total = 0;
|
int total = 0;
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
int length = rs.getInt(1);
|
int length = rs.getInt(1);
|
||||||
@@ -1863,7 +1908,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT key, value FROM settings WHERE namespace = ?";
|
String sql = "SELECT settingKey, value FROM settings"
|
||||||
|
+ " WHERE namespace = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, namespace);
|
ps.setString(1, namespace);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
@@ -1886,14 +1932,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
// Retrieve the incoming keys
|
// Retrieve the incoming keys
|
||||||
String sql = "SELECT period, tagKey, headerKey, base, bitmap"
|
String sql = "SELECT rotationPeriod, tagKey, headerKey,"
|
||||||
|
+ " base, bitmap"
|
||||||
+ " FROM incomingKeys"
|
+ " FROM incomingKeys"
|
||||||
+ " WHERE transportId = ?"
|
+ " WHERE transportId = ?"
|
||||||
+ " ORDER BY contactId, period";
|
+ " ORDER BY contactId, rotationPeriod";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, t.getString());
|
ps.setString(1, t.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
List<IncomingKeys> inKeys = new ArrayList<IncomingKeys>();
|
List<IncomingKeys> inKeys = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
long rotationPeriod = rs.getLong(1);
|
long rotationPeriod = rs.getLong(1);
|
||||||
SecretKey tagKey = new SecretKey(rs.getBytes(2));
|
SecretKey tagKey = new SecretKey(rs.getBytes(2));
|
||||||
@@ -1906,15 +1953,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Retrieve the outgoing keys in the same order
|
// Retrieve the outgoing keys in the same order
|
||||||
sql = "SELECT contactId, period, tagKey, headerKey, stream"
|
sql = "SELECT contactId, rotationPeriod, tagKey, headerKey, stream"
|
||||||
+ " FROM outgoingKeys"
|
+ " FROM outgoingKeys"
|
||||||
+ " WHERE transportId = ?"
|
+ " WHERE transportId = ?"
|
||||||
+ " ORDER BY contactId, period";
|
+ " ORDER BY contactId, rotationPeriod";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setString(1, t.getString());
|
ps.setString(1, t.getString());
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
Map<ContactId, TransportKeys> keys =
|
Map<ContactId, TransportKeys> keys = new HashMap<>();
|
||||||
new HashMap<ContactId, TransportKeys>();
|
|
||||||
for (int i = 0; rs.next(); i++) {
|
for (int i = 0; rs.next(); i++) {
|
||||||
// There should be three times as many incoming keys
|
// There should be three times as many incoming keys
|
||||||
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
|
||||||
@@ -1947,7 +1993,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
String sql = "UPDATE outgoingKeys SET stream = stream + 1"
|
||||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
+ " WHERE contactId = ? AND transportId = ?"
|
||||||
|
+ " AND rotationPeriod = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.setString(2, t.getString());
|
ps.setString(2, t.getString());
|
||||||
@@ -2032,8 +2079,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
// Determine which keys are being removed
|
// Determine which keys are being removed
|
||||||
List<String> removed = new ArrayList<String>();
|
List<String> removed = new ArrayList<>();
|
||||||
Map<String, byte[]> retained = new HashMap<String, byte[]>();
|
Map<String, byte[]> retained = new HashMap<>();
|
||||||
for (Entry<String, byte[]> e : meta.entrySet()) {
|
for (Entry<String, byte[]> e : meta.entrySet()) {
|
||||||
if (e.getValue() == REMOVE) removed.add(e.getKey());
|
if (e.getValue() == REMOVE) removed.add(e.getKey());
|
||||||
else retained.put(e.getKey(), e.getValue());
|
else retained.put(e.getKey(), e.getValue());
|
||||||
@@ -2041,7 +2088,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
// Delete any keys that are being removed
|
// Delete any keys that are being removed
|
||||||
if (!removed.isEmpty()) {
|
if (!removed.isEmpty()) {
|
||||||
String sql = "DELETE FROM " + tableName
|
String sql = "DELETE FROM " + tableName
|
||||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, id);
|
ps.setBytes(1, id);
|
||||||
for (String key : removed) {
|
for (String key : removed) {
|
||||||
@@ -2060,7 +2107,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (retained.isEmpty()) return;
|
if (retained.isEmpty()) return;
|
||||||
// Update any keys that already exist
|
// Update any keys that already exist
|
||||||
String sql = "UPDATE " + tableName + " SET value = ?"
|
String sql = "UPDATE " + tableName + " SET value = ?"
|
||||||
+ " WHERE " + columnName + " = ? AND key = ?";
|
+ " WHERE " + columnName + " = ? AND metaKey = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(2, id);
|
ps.setBytes(2, id);
|
||||||
for (Entry<String, byte[]> e : retained.entrySet()) {
|
for (Entry<String, byte[]> e : retained.entrySet()) {
|
||||||
@@ -2077,7 +2124,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
// Insert any keys that don't already exist
|
// Insert any keys that don't already exist
|
||||||
sql = "INSERT INTO " + tableName
|
sql = "INSERT INTO " + tableName
|
||||||
+ " (" + columnName + ", key, value)"
|
+ " (" + columnName + ", metaKey, value)"
|
||||||
+ " VALUES (?, ?, ?)";
|
+ " VALUES (?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setBytes(1, id);
|
ps.setBytes(1, id);
|
||||||
@@ -2109,7 +2156,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
try {
|
try {
|
||||||
// Update any settings that already exist
|
// Update any settings that already exist
|
||||||
String sql = "UPDATE settings SET value = ?"
|
String sql = "UPDATE settings SET value = ?"
|
||||||
+ " WHERE namespace = ? AND key = ?";
|
+ " WHERE namespace = ? AND settingKey = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
for (Entry<String, String> e : s.entrySet()) {
|
for (Entry<String, String> e : s.entrySet()) {
|
||||||
ps.setString(1, e.getValue());
|
ps.setString(1, e.getValue());
|
||||||
@@ -2124,7 +2171,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (rows > 1) throw new DbStateException();
|
if (rows > 1) throw new DbStateException();
|
||||||
}
|
}
|
||||||
// Insert any settings that don't already exist
|
// Insert any settings that don't already exist
|
||||||
sql = "INSERT INTO settings (namespace, key, value)"
|
sql = "INSERT INTO settings (namespace, settingKey, value)"
|
||||||
+ " VALUES (?, ?, ?)";
|
+ " VALUES (?, ?, ?)";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
int updateIndex = 0, inserted = 0;
|
int updateIndex = 0, inserted = 0;
|
||||||
@@ -2488,7 +2535,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
try {
|
try {
|
||||||
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
|
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
|
||||||
+ " WHERE contactId = ? AND transportId = ? AND period = ?";
|
+ " WHERE contactId = ? AND transportId = ?"
|
||||||
|
+ " AND rotationPeriod = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setLong(1, base);
|
ps.setLong(1, base);
|
||||||
ps.setBytes(2, bitmap);
|
ps.setBytes(2, bitmap);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
class EventBusImpl implements EventBus {
|
class EventBusImpl implements EventBus {
|
||||||
|
|
||||||
private final Collection<EventListener> listeners =
|
private final Collection<EventListener> listeners =
|
||||||
new CopyOnWriteArrayList<EventListener>();
|
new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(EventListener l) {
|
public void addListener(EventListener l) {
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriter;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A connection thread for the peer being Alice in the invitation protocol.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
class AliceConnector extends Connector {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(AliceConnector.class.getName());
|
|
||||||
|
|
||||||
AliceConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
|
|
||||||
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
|
|
||||||
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
|
|
||||||
group, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Create an incoming or outgoing connection
|
|
||||||
DuplexTransportConnection conn = createInvitationConnection(true);
|
|
||||||
if (conn == null) return;
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
|
|
||||||
// Don't proceed with more than one connection
|
|
||||||
if (group.getAndSetConnected()) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Carry out the key agreement protocol
|
|
||||||
InputStream in;
|
|
||||||
OutputStream out;
|
|
||||||
BdfReader r;
|
|
||||||
BdfWriter w;
|
|
||||||
SecretKey master;
|
|
||||||
try {
|
|
||||||
in = conn.getReader().getInputStream();
|
|
||||||
out = conn.getWriter().getOutputStream();
|
|
||||||
r = bdfReaderFactory.createReader(in);
|
|
||||||
w = bdfWriterFactory.createWriter(out);
|
|
||||||
// Alice goes first
|
|
||||||
sendPublicKeyHash(w);
|
|
||||||
byte[] hash = receivePublicKeyHash(r);
|
|
||||||
sendPublicKey(w);
|
|
||||||
byte[] key = receivePublicKey(r);
|
|
||||||
master = deriveMasterSecret(hash, key, true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The key agreement succeeded - derive the confirmation codes
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
|
|
||||||
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
|
|
||||||
int bobCode = crypto.deriveBTConfirmationCode(master, false);
|
|
||||||
group.keyAgreementSucceeded(aliceCode, bobCode);
|
|
||||||
// Exchange confirmation results
|
|
||||||
boolean localMatched, remoteMatched;
|
|
||||||
try {
|
|
||||||
localMatched = group.waitForLocalConfirmationResult();
|
|
||||||
sendConfirmation(w, localMatched);
|
|
||||||
remoteMatched = receiveConfirmation(r);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for confirmation");
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (remoteMatched) group.remoteConfirmationSucceeded();
|
|
||||||
else group.remoteConfirmationFailed();
|
|
||||||
if (!(localMatched && remoteMatched)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation failed");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Confirmation succeeded - upgrade to a secure connection
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation succeeded");
|
|
||||||
contactExchangeTask.startExchange(group, localAuthor, master, conn,
|
|
||||||
plugin.getId(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriter;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A connection thread for the peer being Bob in the invitation protocol.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
class BobConnector extends Connector {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(BobConnector.class.getName());
|
|
||||||
|
|
||||||
BobConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
|
|
||||||
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
|
|
||||||
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
|
|
||||||
group, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
// Create an incoming or outgoing connection
|
|
||||||
DuplexTransportConnection conn = createInvitationConnection(false);
|
|
||||||
if (conn == null) return;
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
|
|
||||||
// Carry out the key agreement protocol
|
|
||||||
InputStream in;
|
|
||||||
OutputStream out;
|
|
||||||
BdfReader r;
|
|
||||||
BdfWriter w;
|
|
||||||
SecretKey master;
|
|
||||||
try {
|
|
||||||
in = conn.getReader().getInputStream();
|
|
||||||
out = conn.getWriter().getOutputStream();
|
|
||||||
r = bdfReaderFactory.createReader(in);
|
|
||||||
w = bdfWriterFactory.createWriter(out);
|
|
||||||
// Alice goes first
|
|
||||||
byte[] hash = receivePublicKeyHash(r);
|
|
||||||
// Don't proceed with more than one connection
|
|
||||||
if (group.getAndSetConnected()) {
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendPublicKeyHash(w);
|
|
||||||
byte[] key = receivePublicKey(r);
|
|
||||||
sendPublicKey(w);
|
|
||||||
master = deriveMasterSecret(hash, key, false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.keyAgreementFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The key agreement succeeded - derive the confirmation codes
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
|
|
||||||
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
|
|
||||||
int bobCode = crypto.deriveBTConfirmationCode(master, false);
|
|
||||||
group.keyAgreementSucceeded(bobCode, aliceCode);
|
|
||||||
// Exchange confirmation results
|
|
||||||
boolean localMatched, remoteMatched;
|
|
||||||
try {
|
|
||||||
remoteMatched = receiveConfirmation(r);
|
|
||||||
localMatched = group.waitForLocalConfirmationResult();
|
|
||||||
sendConfirmation(w, localMatched);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
return;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for confirmation");
|
|
||||||
group.remoteConfirmationFailed();
|
|
||||||
tryToClose(conn, true);
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (remoteMatched) group.remoteConfirmationSucceeded();
|
|
||||||
else group.remoteConfirmationFailed();
|
|
||||||
if (!(localMatched && remoteMatched)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation failed");
|
|
||||||
tryToClose(conn, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Confirmation succeeded - upgrade to a secure connection
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " confirmation succeeded");
|
|
||||||
contactExchangeTask.startExchange(group, localAuthor, master, conn,
|
|
||||||
plugin.getId(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriter;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
|
|
||||||
|
|
||||||
// FIXME: This class has way too many dependencies
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class Connector extends Thread {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(Connector.class.getName());
|
|
||||||
private static final String LABEL_PUBLIC_KEY =
|
|
||||||
"org.briarproject.bramble.invitation.PUBLIC_KEY";
|
|
||||||
|
|
||||||
protected final CryptoComponent crypto;
|
|
||||||
protected final BdfReaderFactory bdfReaderFactory;
|
|
||||||
protected final BdfWriterFactory bdfWriterFactory;
|
|
||||||
protected final ContactExchangeTask contactExchangeTask;
|
|
||||||
protected final ConnectorGroup group;
|
|
||||||
protected final DuplexPlugin plugin;
|
|
||||||
protected final LocalAuthor localAuthor;
|
|
||||||
protected final PseudoRandom random;
|
|
||||||
protected final String pluginName;
|
|
||||||
|
|
||||||
private final KeyPair keyPair;
|
|
||||||
private final KeyParser keyParser;
|
|
||||||
|
|
||||||
Connector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
|
|
||||||
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
|
|
||||||
super("Connector");
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
|
||||||
this.bdfWriterFactory = bdfWriterFactory;
|
|
||||||
this.contactExchangeTask = contactExchangeTask;
|
|
||||||
this.group = group;
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.localAuthor = localAuthor;
|
|
||||||
this.random = random;
|
|
||||||
pluginName = plugin.getClass().getName();
|
|
||||||
keyPair = crypto.generateAgreementKeyPair();
|
|
||||||
keyParser = crypto.getAgreementKeyParser();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
DuplexTransportConnection createInvitationConnection(boolean alice) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " creating invitation connection");
|
|
||||||
return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT,
|
|
||||||
alice);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPublicKeyHash(BdfWriter w) throws IOException {
|
|
||||||
byte[] hash =
|
|
||||||
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
|
|
||||||
w.writeRaw(hash);
|
|
||||||
w.flush();
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent hash");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] receivePublicKeyHash(BdfReader r) throws IOException {
|
|
||||||
int hashLength = crypto.getHashLength();
|
|
||||||
byte[] b = r.readRaw(hashLength);
|
|
||||||
if (b.length < hashLength) throw new FormatException();
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash");
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPublicKey(BdfWriter w) throws IOException {
|
|
||||||
byte[] key = keyPair.getPublic().getEncoded();
|
|
||||||
w.writeRaw(key);
|
|
||||||
w.flush();
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent key");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] receivePublicKey(BdfReader r)
|
|
||||||
throws GeneralSecurityException, IOException {
|
|
||||||
byte[] b = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
|
|
||||||
keyParser.parsePublicKey(b);
|
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received key");
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
SecretKey deriveMasterSecret(byte[] hash, byte[] key, boolean alice)
|
|
||||||
throws GeneralSecurityException {
|
|
||||||
// Check that the hash matches the key
|
|
||||||
byte[] keyHash =
|
|
||||||
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
|
|
||||||
if (!Arrays.equals(hash, keyHash)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " hash does not match key");
|
|
||||||
throw new GeneralSecurityException();
|
|
||||||
}
|
|
||||||
// Derive the master secret
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " deriving master secret");
|
|
||||||
return crypto.deriveMasterSecret(key, keyPair, alice);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendConfirmation(BdfWriter w, boolean confirmed) throws IOException {
|
|
||||||
w.writeBoolean(confirmed);
|
|
||||||
w.flush();
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " sent confirmation: " + confirmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean receiveConfirmation(BdfReader r) throws IOException {
|
|
||||||
boolean confirmed = r.readBoolean();
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(pluginName + " received confirmation: " + confirmed);
|
|
||||||
return confirmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void tryToClose(DuplexTransportConnection conn,
|
|
||||||
boolean exception) {
|
|
||||||
try {
|
|
||||||
LOG.info("Closing connection");
|
|
||||||
conn.getReader().dispose(exception, true);
|
|
||||||
conn.getWriter().dispose(exception);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeListener;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationListener;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationState;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTask;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONFIRMATION_TIMEOUT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A task consisting of one or more parallel connection attempts.
|
|
||||||
*/
|
|
||||||
@MethodsNotNullByDefault
|
|
||||||
@ParametersNotNullByDefault
|
|
||||||
class ConnectorGroup extends Thread implements InvitationTask,
|
|
||||||
ContactExchangeListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ConnectorGroup.class.getName());
|
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final BdfReaderFactory bdfReaderFactory;
|
|
||||||
private final BdfWriterFactory bdfWriterFactory;
|
|
||||||
private final ContactExchangeTask contactExchangeTask;
|
|
||||||
private final IdentityManager identityManager;
|
|
||||||
private final PluginManager pluginManager;
|
|
||||||
private final int localInvitationCode, remoteInvitationCode;
|
|
||||||
private final Collection<InvitationListener> listeners;
|
|
||||||
private final AtomicBoolean connected;
|
|
||||||
private final CountDownLatch localConfirmationLatch;
|
|
||||||
private final Lock lock = new ReentrantLock();
|
|
||||||
|
|
||||||
// The following are locking: lock
|
|
||||||
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
|
|
||||||
private boolean connectionFailed = false;
|
|
||||||
private boolean localCompared = false, remoteCompared = false;
|
|
||||||
private boolean localMatched = false, remoteMatched = false;
|
|
||||||
private String remoteName = null;
|
|
||||||
|
|
||||||
ConnectorGroup(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask,
|
|
||||||
IdentityManager identityManager, PluginManager pluginManager,
|
|
||||||
int localInvitationCode, int remoteInvitationCode) {
|
|
||||||
super("ConnectorGroup");
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
|
||||||
this.bdfWriterFactory = bdfWriterFactory;
|
|
||||||
this.contactExchangeTask = contactExchangeTask;
|
|
||||||
this.identityManager = identityManager;
|
|
||||||
this.pluginManager = pluginManager;
|
|
||||||
this.localInvitationCode = localInvitationCode;
|
|
||||||
this.remoteInvitationCode = remoteInvitationCode;
|
|
||||||
listeners = new CopyOnWriteArrayList<InvitationListener>();
|
|
||||||
connected = new AtomicBoolean(false);
|
|
||||||
localConfirmationLatch = new CountDownLatch(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InvitationState addListener(InvitationListener l) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
listeners.add(l);
|
|
||||||
return new InvitationState(localInvitationCode,
|
|
||||||
remoteInvitationCode, localConfirmationCode,
|
|
||||||
remoteConfirmationCode, connected.get(), connectionFailed,
|
|
||||||
localCompared, remoteCompared, localMatched, remoteMatched,
|
|
||||||
remoteName);
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListener(InvitationListener l) {
|
|
||||||
listeners.remove(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connect() {
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
LocalAuthor localAuthor;
|
|
||||||
// Load the local pseudonym
|
|
||||||
try {
|
|
||||||
localAuthor = identityManager.getLocalAuthor();
|
|
||||||
} catch (DbException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
connectionFailed = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.connectionFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Start the connection threads
|
|
||||||
Collection<Connector> connectors = new ArrayList<Connector>();
|
|
||||||
// Alice is the party with the smaller invitation code
|
|
||||||
if (localInvitationCode < remoteInvitationCode) {
|
|
||||||
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
|
|
||||||
Connector c = createAliceConnector(plugin, localAuthor);
|
|
||||||
connectors.add(c);
|
|
||||||
c.start();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
|
|
||||||
Connector c = createBobConnector(plugin, localAuthor);
|
|
||||||
connectors.add(c);
|
|
||||||
c.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Wait for the connection threads to finish
|
|
||||||
try {
|
|
||||||
for (Connector c : connectors) c.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.warning("Interrupted while waiting for connectors");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
// If none of the threads connected, inform the listeners
|
|
||||||
if (!connected.get()) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
connectionFailed = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.connectionFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Connector createAliceConnector(DuplexPlugin plugin,
|
|
||||||
LocalAuthor localAuthor) {
|
|
||||||
PseudoRandom random = crypto.getPseudoRandom(localInvitationCode,
|
|
||||||
remoteInvitationCode);
|
|
||||||
return new AliceConnector(crypto, bdfReaderFactory, bdfWriterFactory,
|
|
||||||
contactExchangeTask, this, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Connector createBobConnector(DuplexPlugin plugin,
|
|
||||||
LocalAuthor localAuthor) {
|
|
||||||
PseudoRandom random = crypto.getPseudoRandom(remoteInvitationCode,
|
|
||||||
localInvitationCode);
|
|
||||||
return new BobConnector(crypto, bdfReaderFactory, bdfWriterFactory,
|
|
||||||
contactExchangeTask, this, plugin, localAuthor, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void localConfirmationSucceeded() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
localCompared = true;
|
|
||||||
localMatched = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
localConfirmationLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void localConfirmationFailed() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
localCompared = true;
|
|
||||||
localMatched = false;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
localConfirmationLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean getAndSetConnected() {
|
|
||||||
boolean redundant = connected.getAndSet(true);
|
|
||||||
if (!redundant)
|
|
||||||
for (InvitationListener l : listeners) l.connectionSucceeded();
|
|
||||||
return redundant;
|
|
||||||
}
|
|
||||||
|
|
||||||
void keyAgreementSucceeded(int localCode, int remoteCode) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
localConfirmationCode = localCode;
|
|
||||||
remoteConfirmationCode = remoteCode;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners)
|
|
||||||
l.keyAgreementSucceeded(localCode, remoteCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void keyAgreementFailed() {
|
|
||||||
for (InvitationListener l : listeners) l.keyAgreementFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean waitForLocalConfirmationResult() throws InterruptedException {
|
|
||||||
localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return localMatched;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remoteConfirmationSucceeded() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
remoteCompared = true;
|
|
||||||
remoteMatched = true;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.remoteConfirmationSucceeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void remoteConfirmationFailed() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
remoteCompared = true;
|
|
||||||
remoteMatched = false;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners) l.remoteConfirmationFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contactExchangeSucceeded(Author remoteAuthor) {
|
|
||||||
String name = remoteAuthor.getName();
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
remoteName = name;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
for (InvitationListener l : listeners)
|
|
||||||
l.pseudonymExchangeSucceeded(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void duplicateContact(Author remoteAuthor) {
|
|
||||||
// TODO differentiate
|
|
||||||
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contactExchangeFailed() {
|
|
||||||
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class InvitationModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
InvitationTaskFactory provideInvitationTaskFactory(
|
|
||||||
InvitationTaskFactoryImpl invitationTaskFactory) {
|
|
||||||
return invitationTaskFactory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package org.briarproject.bramble.invitation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTask;
|
|
||||||
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class InvitationTaskFactoryImpl implements InvitationTaskFactory {
|
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final BdfReaderFactory bdfReaderFactory;
|
|
||||||
private final BdfWriterFactory bdfWriterFactory;
|
|
||||||
private final ContactExchangeTask contactExchangeTask;
|
|
||||||
private final IdentityManager identityManager;
|
|
||||||
private final PluginManager pluginManager;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
InvitationTaskFactoryImpl(CryptoComponent crypto,
|
|
||||||
BdfReaderFactory bdfReaderFactory,
|
|
||||||
BdfWriterFactory bdfWriterFactory,
|
|
||||||
ContactExchangeTask contactExchangeTask,
|
|
||||||
IdentityManager identityManager, PluginManager pluginManager) {
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
|
||||||
this.bdfWriterFactory = bdfWriterFactory;
|
|
||||||
this.contactExchangeTask = contactExchangeTask;
|
|
||||||
this.identityManager = identityManager;
|
|
||||||
this.pluginManager = pluginManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InvitationTask createTask(int localCode, int remoteCode) {
|
|
||||||
return new ConnectorGroup(crypto, bdfReaderFactory, bdfWriterFactory,
|
|
||||||
contactExchangeTask, identityManager, pluginManager,
|
|
||||||
localCode, remoteCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
@@ -46,37 +46,34 @@ class KeyAgreementConnector {
|
|||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final CryptoComponent crypto;
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
private final CompletionService<KeyAgreementConnection> connect;
|
private final CompletionService<KeyAgreementConnection> connect;
|
||||||
|
|
||||||
private final List<KeyAgreementListener> listeners =
|
private final List<KeyAgreementListener> listeners = new ArrayList<>();
|
||||||
new ArrayList<KeyAgreementListener>();
|
|
||||||
private final List<Future<KeyAgreementConnection>> pending =
|
private final List<Future<KeyAgreementConnection>> pending =
|
||||||
new ArrayList<Future<KeyAgreementConnection>>();
|
new ArrayList<>();
|
||||||
|
|
||||||
private volatile boolean connecting = false;
|
private volatile boolean connecting = false;
|
||||||
private volatile boolean alice = false;
|
private volatile boolean alice = false;
|
||||||
|
|
||||||
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
KeyAgreementConnector(Callbacks callbacks, Clock clock,
|
||||||
CryptoComponent crypto, PluginManager pluginManager,
|
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
|
||||||
Executor ioExecutor) {
|
Executor ioExecutor) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.crypto = crypto;
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
connect = new ExecutorCompletionService<KeyAgreementConnection>(
|
connect = new ExecutorCompletionService<>(ioExecutor);
|
||||||
ioExecutor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Payload listen(KeyPair localKeyPair) {
|
public Payload listen(KeyPair localKeyPair) {
|
||||||
LOG.info("Starting BQP listeners");
|
LOG.info("Starting BQP listeners");
|
||||||
// Derive commitment
|
// Derive commitment
|
||||||
byte[] commitment = crypto.deriveKeyCommitment(
|
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment(
|
||||||
localKeyPair.getPublic().getEncoded());
|
localKeyPair.getPublic());
|
||||||
// Start all listeners and collect their descriptors
|
// Start all listeners and collect their descriptors
|
||||||
List<TransportDescriptor> descriptors =
|
List<TransportDescriptor> descriptors = new ArrayList<>();
|
||||||
new ArrayList<TransportDescriptor>();
|
|
||||||
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
|
||||||
KeyAgreementListener l =
|
KeyAgreementListener l =
|
||||||
plugin.createKeyAgreementListener(commitment);
|
plugin.createKeyAgreementListener(commitment);
|
||||||
@@ -132,10 +129,7 @@ class KeyAgreementConnector {
|
|||||||
LOG.info("Interrupted while waiting for connection");
|
LOG.info("Interrupted while waiting for connection");
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
return null;
|
return null;
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException | IOException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
return null;
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
@@ -22,13 +13,9 @@ import dagger.Provides;
|
|||||||
public class KeyAgreementModule {
|
public class KeyAgreementModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
KeyAgreementTask provideKeyAgreementTask(
|
||||||
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
|
KeyAgreementTaskImpl keyAgreementTask) {
|
||||||
CryptoComponent crypto, EventBus eventBus,
|
return keyAgreementTask;
|
||||||
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
|
|
||||||
PluginManager pluginManager) {
|
|
||||||
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
|
|
||||||
ioExecutor, payloadEncoder, pluginManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
@@ -11,6 +14,10 @@ import java.io.IOException;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the BQP protocol.
|
* Implementation of the BQP protocol.
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -57,6 +64,7 @@ class KeyAgreementProtocol {
|
|||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
private final KeyAgreementTransport transport;
|
private final KeyAgreementTransport transport;
|
||||||
private final Payload theirPayload, ourPayload;
|
private final Payload theirPayload, ourPayload;
|
||||||
@@ -64,11 +72,13 @@ class KeyAgreementProtocol {
|
|||||||
private final boolean alice;
|
private final boolean alice;
|
||||||
|
|
||||||
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
|
||||||
|
KeyAgreementCrypto keyAgreementCrypto,
|
||||||
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
|
||||||
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
|
||||||
boolean alice) {
|
boolean alice) {
|
||||||
this.callbacks = callbacks;
|
this.callbacks = callbacks;
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.theirPayload = theirPayload;
|
this.theirPayload = theirPayload;
|
||||||
@@ -86,7 +96,7 @@ class KeyAgreementProtocol {
|
|||||||
*/
|
*/
|
||||||
SecretKey perform() throws AbortException, IOException {
|
SecretKey perform() throws AbortException, IOException {
|
||||||
try {
|
try {
|
||||||
byte[] theirPublicKey;
|
PublicKey theirPublicKey;
|
||||||
if (alice) {
|
if (alice) {
|
||||||
sendKey();
|
sendKey();
|
||||||
// Alice waits here until Bob obtains her payload.
|
// Alice waits here until Bob obtains her payload.
|
||||||
@@ -104,7 +114,7 @@ class KeyAgreementProtocol {
|
|||||||
receiveConfirm(s, theirPublicKey);
|
receiveConfirm(s, theirPublicKey);
|
||||||
sendConfirm(s, theirPublicKey);
|
sendConfirm(s, theirPublicKey);
|
||||||
}
|
}
|
||||||
return crypto.deriveMasterSecret(s);
|
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
|
||||||
} catch (AbortException e) {
|
} catch (AbortException e) {
|
||||||
sendAbort(e.getCause() != null);
|
sendAbort(e.getCause() != null);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -115,27 +125,41 @@ class KeyAgreementProtocol {
|
|||||||
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
transport.sendKey(ourKeyPair.getPublic().getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] receiveKey() throws AbortException {
|
private PublicKey receiveKey() throws AbortException {
|
||||||
byte[] publicKey = transport.receiveKey();
|
byte[] publicKeyBytes = transport.receiveKey();
|
||||||
callbacks.initialRecordReceived();
|
callbacks.initialRecordReceived();
|
||||||
byte[] expected = crypto.deriveKeyCommitment(publicKey);
|
KeyParser keyParser = crypto.getAgreementKeyParser();
|
||||||
|
try {
|
||||||
|
PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||||
|
byte[] expected = keyAgreementCrypto.deriveKeyCommitment(publicKey);
|
||||||
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
if (!Arrays.equals(expected, theirPayload.getCommitment()))
|
||||||
throw new AbortException();
|
throw new AbortException();
|
||||||
return publicKey;
|
return publicKey;
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new AbortException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
|
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
|
||||||
throws AbortException {
|
throws AbortException {
|
||||||
try {
|
try {
|
||||||
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
|
byte[] ourPublicKeyBytes = ourKeyPair.getPublic().getEncoded();
|
||||||
|
byte[] theirPublicKeyBytes = theirPublicKey.getEncoded();
|
||||||
|
byte[][] inputs = {
|
||||||
|
new byte[] {PROTOCOL_VERSION},
|
||||||
|
alice ? ourPublicKeyBytes : theirPublicKeyBytes,
|
||||||
|
alice ? theirPublicKeyBytes : ourPublicKeyBytes
|
||||||
|
};
|
||||||
|
return crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
|
||||||
|
theirPublicKey, ourKeyPair, inputs);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new AbortException(e);
|
throw new AbortException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
|
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
byte[] confirm = crypto.deriveConfirmationRecord(s,
|
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||||
payloadEncoder.encode(theirPayload),
|
payloadEncoder.encode(theirPayload),
|
||||||
payloadEncoder.encode(ourPayload),
|
payloadEncoder.encode(ourPayload),
|
||||||
theirPublicKey, ourKeyPair,
|
theirPublicKey, ourKeyPair,
|
||||||
@@ -143,10 +167,10 @@ class KeyAgreementProtocol {
|
|||||||
transport.sendConfirm(confirm);
|
transport.sendConfirm(confirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
|
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
|
||||||
throws AbortException {
|
throws AbortException {
|
||||||
byte[] confirm = transport.receiveConfirm();
|
byte[] confirm = transport.receiveConfirm();
|
||||||
byte[] expected = crypto.deriveConfirmationRecord(s,
|
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
|
||||||
payloadEncoder.encode(theirPayload),
|
payloadEncoder.encode(theirPayload),
|
||||||
payloadEncoder.encode(ourPayload),
|
payloadEncoder.encode(ourPayload),
|
||||||
theirPublicKey, ourKeyPair,
|
theirPublicKey, ourKeyPair,
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class KeyAgreementTaskFactoryImpl implements KeyAgreementTaskFactory {
|
|
||||||
|
|
||||||
private final Clock clock;
|
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final EventBus eventBus;
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final PayloadEncoder payloadEncoder;
|
|
||||||
private final PluginManager pluginManager;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
KeyAgreementTaskFactoryImpl(Clock clock, CryptoComponent crypto,
|
|
||||||
EventBus eventBus, @IoExecutor Executor ioExecutor,
|
|
||||||
PayloadEncoder payloadEncoder, PluginManager pluginManager) {
|
|
||||||
this.clock = clock;
|
|
||||||
this.crypto = crypto;
|
|
||||||
this.eventBus = eventBus;
|
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
this.payloadEncoder = payloadEncoder;
|
|
||||||
this.pluginManager = pluginManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyAgreementTask createTask() {
|
|
||||||
return new KeyAgreementTaskImpl(clock, crypto, eventBus, payloadEncoder,
|
|
||||||
pluginManager, ioExecutor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -14,6 +15,7 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent
|
|||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
|
||||||
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
@@ -23,6 +25,8 @@ import java.io.IOException;
|
|||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -35,6 +39,7 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final CryptoComponent crypto;
|
||||||
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
private final KeyPair localKeyPair;
|
private final KeyPair localKeyPair;
|
||||||
@@ -43,14 +48,17 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
private Payload localPayload;
|
private Payload localPayload;
|
||||||
private Payload remotePayload;
|
private Payload remotePayload;
|
||||||
|
|
||||||
|
@Inject
|
||||||
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
|
||||||
EventBus eventBus, PayloadEncoder payloadEncoder,
|
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
|
||||||
PluginManager pluginManager, Executor ioExecutor) {
|
PayloadEncoder payloadEncoder, PluginManager pluginManager,
|
||||||
|
@IoExecutor Executor ioExecutor) {
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
|
this.keyAgreementCrypto = keyAgreementCrypto;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
localKeyPair = crypto.generateAgreementKeyPair();
|
localKeyPair = crypto.generateAgreementKeyPair();
|
||||||
connector = new KeyAgreementConnector(this, clock, crypto,
|
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
|
||||||
pluginManager, ioExecutor);
|
pluginManager, ioExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +108,8 @@ class KeyAgreementTaskImpl extends Thread implements
|
|||||||
// Run BQP protocol over the connection
|
// Run BQP protocol over the connection
|
||||||
LOG.info("Starting BQP protocol");
|
LOG.info("Starting BQP protocol");
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
|
||||||
payloadEncoder, transport, remotePayload, localPayload,
|
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
|
||||||
localKeyPair, alice);
|
localPayload, localKeyPair, alice);
|
||||||
try {
|
try {
|
||||||
SecretKey master = protocol.perform();
|
SecretKey master = protocol.perform();
|
||||||
KeyAgreementResult result =
|
KeyAgreementResult result =
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ class PayloadParserImpl implements PayloadParser {
|
|||||||
byte[] commitment = payload.getRaw(1);
|
byte[] commitment = payload.getRaw(1);
|
||||||
if (commitment.length != COMMIT_LENGTH) throw new FormatException();
|
if (commitment.length != COMMIT_LENGTH) throw new FormatException();
|
||||||
// Remaining elements: transport descriptors
|
// Remaining elements: transport descriptors
|
||||||
List<TransportDescriptor> recognised =
|
List<TransportDescriptor> recognised = new ArrayList<>();
|
||||||
new ArrayList<TransportDescriptor>();
|
|
||||||
for (int i = 2; i < payload.size(); i++) {
|
for (int i = 2; i < payload.size(); i++) {
|
||||||
BdfList descriptor = payload.getList(i);
|
BdfList descriptor = payload.getList(i);
|
||||||
long transportId = descriptor.getLong(0);
|
long transportId = descriptor.getLong(0);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user