Add temp repository encryption (#131)
This commit is contained in:
@@ -88,4 +88,5 @@ dependencies {
|
||||
implementation 'com.github.apl-devs:appintro:v4.2.3'
|
||||
|
||||
implementation project(':syncthing-repository-android')
|
||||
implementation project(':syncthing-temp-repository-encryption')
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import kotlinx.coroutines.launch
|
||||
import net.syncthing.java.client.SyncthingClient
|
||||
import net.syncthing.java.core.configuration.Configuration
|
||||
import net.syncthing.java.core.exception.ExceptionReport
|
||||
import net.syncthing.java.repository.EncryptedTempRepository
|
||||
import net.syncthing.repository.android.SqliteIndexRepository
|
||||
import net.syncthing.repository.android.TempDirectoryLocalRepository
|
||||
import net.syncthing.repository.android.database.RepositoryDatabase
|
||||
@@ -49,7 +50,11 @@ class LibraryInstance (
|
||||
}
|
||||
}
|
||||
|
||||
private val tempRepository = TempDirectoryLocalRepository(File(context.filesDir, "temp_repository"))
|
||||
private val tempRepository = EncryptedTempRepository(
|
||||
TempDirectoryLocalRepository(
|
||||
File(context.filesDir, "temp_repository")
|
||||
)
|
||||
)
|
||||
|
||||
val isListeningPortTaken = checkIsListeningPortTaken() // this must come first to work correctly
|
||||
val configuration = Configuration(configFolder = context.filesDir)
|
||||
@@ -58,7 +63,7 @@ class LibraryInstance (
|
||||
repository = SqliteIndexRepository(
|
||||
database = RepositoryDatabase.with(context),
|
||||
closeDatabaseOnClose = false,
|
||||
clearTempStorageHook = { tempRepository.deleteAllData() }
|
||||
clearTempStorageHook = { tempRepository.deleteAllTempData() }
|
||||
),
|
||||
tempRepository = tempRepository,
|
||||
exceptionReportHandler = { ex ->
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
include ':app', ':syncthing-repository-android', ':syncthing-repository-default', ':syncthing-relay-client', ':syncthing-bep', ':syncthing-core', ':syncthing-client', ':syncthing-discovery', ':syncthing-client-cli'
|
||||
include ':app', ':syncthing-repository-android', ':syncthing-repository-default', ':syncthing-relay-client', ':syncthing-bep', ':syncthing-core', ':syncthing-client', ':syncthing-discovery', ':syncthing-client-cli', ':syncthing-temp-repository-encryption'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Davide Imbriaco
|
||||
* Copyright (C) 2018 Jonas Lochmann
|
||||
*
|
||||
* This Java file is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -22,4 +23,6 @@ interface TempRepository: Closeable {
|
||||
fun popTempData(key: String): ByteArray
|
||||
|
||||
fun deleteTempData(keys: List<String>)
|
||||
|
||||
fun deleteAllTempData()
|
||||
}
|
||||
|
||||
+16
-3
@@ -1,3 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jonas Lochmann
|
||||
*
|
||||
* This Java file is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.syncthing.repository.android
|
||||
|
||||
import net.syncthing.java.core.interfaces.TempRepository
|
||||
@@ -14,7 +27,7 @@ class TempDirectoryLocalRepository(private val directory: File): TempRepository
|
||||
directory.mkdirs()
|
||||
|
||||
// there could be garbage from the previous session which we don't need anymore
|
||||
deleteAllData()
|
||||
deleteAllTempData()
|
||||
}
|
||||
|
||||
override fun pushTempData(data: ByteArray): String {
|
||||
@@ -59,10 +72,10 @@ class TempDirectoryLocalRepository(private val directory: File): TempRepository
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
deleteAllData()
|
||||
deleteAllTempData()
|
||||
}
|
||||
|
||||
fun deleteAllData() {
|
||||
override fun deleteAllTempData() {
|
||||
directory.listFiles().forEach { file ->
|
||||
if (file.isFile) {
|
||||
file.delete()
|
||||
|
||||
+9
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Davide Imbriaco
|
||||
* Copyright (C) 2018 Jonas Lochmann
|
||||
*
|
||||
* This Java file is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -227,6 +228,14 @@ class SqlRepository(databaseFolder: File) : Closeable, IndexRepository, TempRepo
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteAllTempData() {
|
||||
getConnection().use { connection ->
|
||||
connection.prepareStatement("DELETE FROM temporary_data").use { statement ->
|
||||
statement.executeUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val VERSION = 13
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
implementation (project(':syncthing-core')) {
|
||||
exclude group: 'commons-logging', module:'commons-logging'
|
||||
exclude group: 'org.slf4j'
|
||||
exclude group: 'ch.qos.logback'
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jonas Lochmann
|
||||
*
|
||||
* This Java file is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.syncthing.java.repository
|
||||
|
||||
import net.syncthing.java.core.interfaces.TempRepository
|
||||
import java.io.IOException
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class EncryptedTempRepository(private val otherRepository: TempRepository): TempRepository {
|
||||
companion object {
|
||||
private val secureRandom = SecureRandom()
|
||||
}
|
||||
|
||||
private val keyStorage = Collections.synchronizedMap(mutableMapOf<String, EncryptedTempRepositoryItem>())
|
||||
|
||||
override fun pushTempData(data: ByteArray): String {
|
||||
val (encrypted, config) = encrypt(data)
|
||||
val key = otherRepository.pushTempData(encrypted)
|
||||
|
||||
keyStorage[key] = config
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
override fun popTempData(key: String) = decrypt(otherRepository.popTempData(key), keyStorage.remove(key)!!)
|
||||
|
||||
override fun deleteTempData(keys: List<String>) {
|
||||
keys.forEach { keyStorage.remove(it) }
|
||||
otherRepository.deleteTempData(keys)
|
||||
}
|
||||
|
||||
override fun deleteAllTempData() {
|
||||
keyStorage.clear()
|
||||
otherRepository.deleteAllTempData()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
keyStorage.clear()
|
||||
otherRepository.close()
|
||||
}
|
||||
|
||||
private fun encrypt(input: ByteArray): Pair<ByteArray, EncryptedTempRepositoryItem> {
|
||||
val cryptoSpec = EncryptedTempRepositoryItem(
|
||||
key = ByteArray(16).apply { secureRandom.nextBytes(this) },
|
||||
iv = ByteArray(16).apply { secureRandom.nextBytes(this) },
|
||||
sha512 = sha512(input)
|
||||
)
|
||||
|
||||
val output = Cipher.getInstance("AES/GCM/NoPadding").apply {
|
||||
init(
|
||||
Cipher.ENCRYPT_MODE,
|
||||
SecretKeySpec(cryptoSpec.key, "AES"),
|
||||
GCMParameterSpec(128, cryptoSpec.iv)
|
||||
)
|
||||
}.doFinal(input)
|
||||
|
||||
return output to cryptoSpec
|
||||
}
|
||||
|
||||
private fun decrypt(input: ByteArray, config: EncryptedTempRepositoryItem): ByteArray {
|
||||
val output = Cipher.getInstance("AES/GCM/NoPadding").apply {
|
||||
init(
|
||||
Cipher.DECRYPT_MODE,
|
||||
SecretKeySpec(config.key, "AES"),
|
||||
GCMParameterSpec(128, config.iv)
|
||||
)
|
||||
}.doFinal(input)
|
||||
|
||||
if (!sha512(output).contentEquals(config.sha512)) {
|
||||
throw IOException("temporarily file was modified")
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
private fun sha512(data: ByteArray): ByteArray = MessageDigest.getInstance("SHA-512").digest(data)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jonas Lochmann
|
||||
*
|
||||
* This Java file is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package net.syncthing.java.repository
|
||||
|
||||
internal class EncryptedTempRepositoryItem(
|
||||
val iv: ByteArray,
|
||||
val key: ByteArray,
|
||||
val sha512: ByteArray
|
||||
)
|
||||
Reference in New Issue
Block a user