Класс шифрования готов
This commit is contained in:
@@ -3,9 +3,9 @@ package com.github.nullptroma.wallenc.data.vaults.local
|
||||
import com.fasterxml.jackson.core.JacksonException
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalDirectory
|
||||
import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalFile
|
||||
import com.github.nullptroma.wallenc.data.vaults.local.entity.LocalMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonDirectory
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonFile
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.DataPage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
|
||||
@@ -114,7 +114,7 @@ class LocalStorageAccessor(
|
||||
private class LocalStorageFilePair private constructor(
|
||||
val file: File,
|
||||
val metaFile: File,
|
||||
val meta: LocalMetaInfo
|
||||
val meta: CommonMetaInfo
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -135,23 +135,23 @@ class LocalStorageAccessor(
|
||||
}
|
||||
)
|
||||
val metaFile = metaFilePath.toFile()
|
||||
val metaInfo: LocalMetaInfo
|
||||
val metaInfo: CommonMetaInfo
|
||||
val storageFilePath = "/" + filePath.relativeTo(filesystemBasePath)
|
||||
|
||||
if (!metaFile.exists()) {
|
||||
metaInfo = LocalMetaInfo(
|
||||
metaInfo = CommonMetaInfo(
|
||||
size = filePath.fileSize(),
|
||||
path = storageFilePath
|
||||
)
|
||||
_jackson.writeValue(metaFile, metaInfo)
|
||||
} else {
|
||||
var readMeta: LocalMetaInfo
|
||||
var readMeta: CommonMetaInfo
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
readMeta = _jackson.readValue(reader)
|
||||
} catch (e: JacksonException) {
|
||||
// если файл повреждён - пересоздать
|
||||
readMeta = LocalMetaInfo(
|
||||
readMeta = CommonMetaInfo(
|
||||
size = filePath.fileSize(),
|
||||
path = storageFilePath
|
||||
)
|
||||
@@ -174,7 +174,7 @@ class LocalStorageAccessor(
|
||||
var pair: LocalStorageFilePair? = null
|
||||
try {
|
||||
val reader = metaFile.bufferedReader()
|
||||
val metaInfo: LocalMetaInfo = _jackson.readValue(reader)
|
||||
val metaInfo: CommonMetaInfo = _jackson.readValue(reader)
|
||||
val pathString = Path(filesystemBasePath.pathString, metaInfo.path).pathString
|
||||
val file = File(pathString)
|
||||
if (!file.exists()) {
|
||||
@@ -213,8 +213,8 @@ class LocalStorageAccessor(
|
||||
private suspend fun scanStorage(
|
||||
baseStoragePath: String,
|
||||
maxDepth: Int,
|
||||
fileCallback: (suspend (File, LocalFile) -> Unit)? = null,
|
||||
dirCallback: (suspend (File, LocalDirectory) -> Unit)? = null
|
||||
fileCallback: (suspend (File, CommonFile) -> Unit)? = null,
|
||||
dirCallback: (suspend (File, CommonDirectory) -> Unit)? = null
|
||||
) {
|
||||
if (!checkAvailable())
|
||||
throw Exception("Not available")
|
||||
@@ -234,9 +234,9 @@ class LocalStorageAccessor(
|
||||
workedMetaFiles.add(pair.metaFile.absolutePath)
|
||||
|
||||
if (pair.file.isFile) {
|
||||
fileCallback?.invoke(pair.file, LocalFile(pair.meta))
|
||||
fileCallback?.invoke(pair.file, CommonFile(pair.meta))
|
||||
} else {
|
||||
dirCallback?.invoke(pair.file, LocalDirectory(pair.meta, null))
|
||||
dirCallback?.invoke(pair.file, CommonDirectory(pair.meta, null))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -257,8 +257,8 @@ class LocalStorageAccessor(
|
||||
var size = 0L
|
||||
var numOfFiles = 0
|
||||
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, localFile ->
|
||||
size += localFile.metaInfo.size
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, CommonFile ->
|
||||
size += CommonFile.metaInfo.size
|
||||
numOfFiles++
|
||||
|
||||
if(numOfFiles % DATA_PAGE_LENGTH == 0) {
|
||||
@@ -276,8 +276,8 @@ class LocalStorageAccessor(
|
||||
return@withContext listOf()
|
||||
|
||||
val list = mutableListOf<IFile>()
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, localFile ->
|
||||
list.add(localFile)
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, CommonFile ->
|
||||
list.add(CommonFile)
|
||||
})
|
||||
return@withContext list
|
||||
}
|
||||
@@ -287,8 +287,8 @@ class LocalStorageAccessor(
|
||||
return@withContext listOf()
|
||||
|
||||
val list = mutableListOf<IFile>()
|
||||
scanStorage(baseStoragePath = path, maxDepth = 0, fileCallback = { _, localFile ->
|
||||
list.add(localFile)
|
||||
scanStorage(baseStoragePath = path, maxDepth = 0, fileCallback = { _, CommonFile ->
|
||||
list.add(CommonFile)
|
||||
})
|
||||
return@withContext list
|
||||
}
|
||||
@@ -299,7 +299,7 @@ class LocalStorageAccessor(
|
||||
|
||||
val buf = mutableListOf<IFile>()
|
||||
var pageNumber = 0
|
||||
scanStorage(baseStoragePath = path, maxDepth = 0, fileCallback = { _, localFile ->
|
||||
scanStorage(baseStoragePath = path, maxDepth = 0, fileCallback = { _, CommonFile ->
|
||||
if (buf.size == DATA_PAGE_LENGTH) {
|
||||
val page = DataPage(
|
||||
list = buf.toList(),
|
||||
@@ -312,7 +312,7 @@ class LocalStorageAccessor(
|
||||
emit(page)
|
||||
buf.clear()
|
||||
}
|
||||
buf.add(localFile)
|
||||
buf.add(CommonFile)
|
||||
})
|
||||
// отправка последней страницы
|
||||
val page = DataPage(
|
||||
@@ -381,11 +381,11 @@ class LocalStorageAccessor(
|
||||
emit(page)
|
||||
}.flowOn(ioDispatcher)
|
||||
|
||||
private fun writeMeta(metaFile: File, meta: LocalMetaInfo) {
|
||||
private fun writeMeta(metaFile: File, meta: CommonMetaInfo) {
|
||||
_jackson.writeValue(metaFile, meta)
|
||||
}
|
||||
|
||||
private fun createFile(storagePath: String): LocalFile {
|
||||
private fun createFile(storagePath: String): CommonFile {
|
||||
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||
val file = path.toFile()
|
||||
if(file.exists() && file.isDirectory) {
|
||||
@@ -398,10 +398,10 @@ class LocalStorageAccessor(
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, file) ?: throw Exception("Что то пошло не так") // TODO
|
||||
val newMeta = pair.meta.copy(lastModified = java.time.Clock.systemUTC().instant())
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
return LocalFile(newMeta)
|
||||
return CommonFile(newMeta)
|
||||
}
|
||||
|
||||
private fun createDir(storagePath: String): LocalDirectory {
|
||||
private fun createDir(storagePath: String): CommonDirectory {
|
||||
val path = Path(_filesystemBasePath.pathString, storagePath)
|
||||
val file = path.toFile()
|
||||
if(file.exists() && !file.isDirectory) {
|
||||
@@ -414,7 +414,7 @@ class LocalStorageAccessor(
|
||||
val pair = LocalStorageFilePair.from(_filesystemBasePath, file) ?: throw Exception("Что то пошло не так") // TODO
|
||||
val newMeta = pair.meta.copy(lastModified = java.time.Clock.systemUTC().instant())
|
||||
writeMeta(pair.metaFile, newMeta)
|
||||
return LocalDirectory(newMeta, 0)
|
||||
return CommonDirectory(newMeta, 0)
|
||||
}
|
||||
|
||||
override suspend fun touchFile(path: String): Unit = withContext(ioDispatcher) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local.entity
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
|
||||
|
||||
data class LocalDirectory(
|
||||
override val metaInfo: LocalMetaInfo,
|
||||
override val elementsCount: Int?
|
||||
) : IDirectory
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local.entity
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
||||
|
||||
data class LocalFile(override val metaInfo: LocalMetaInfo) : IFile
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local.entity
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
data class LocalMetaInfo(
|
||||
override val size: Long,
|
||||
override val isDeleted: Boolean = false,
|
||||
override val isHidden: Boolean = false,
|
||||
override val lastModified: Instant = java.time.Clock.systemUTC().instant(),
|
||||
override val path: String
|
||||
) : IMetaInfo
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.nullptroma.wallenc.domain.common.impl
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
|
||||
|
||||
data class CommonDirectory(
|
||||
override val metaInfo: CommonMetaInfo,
|
||||
override val elementsCount: Int?
|
||||
) : IDirectory
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.github.nullptroma.wallenc.domain.common.impl
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
||||
|
||||
data class CommonFile(override val metaInfo: CommonMetaInfo) : IFile
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.github.nullptroma.wallenc.domain.encrypt.entity
|
||||
package com.github.nullptroma.wallenc.domain.common.impl
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
data class EncryptedMetaInfo(
|
||||
data class CommonMetaInfo(
|
||||
override val size: Long,
|
||||
override val isDeleted: Boolean = false,
|
||||
override val isHidden: Boolean = false,
|
||||
override val lastModified: Instant = java.time.Clock.systemUTC().instant(),
|
||||
override val lastModified: Instant = Clock.systemUTC().instant(),
|
||||
override val path: String
|
||||
) : IMetaInfo
|
||||
@@ -1,13 +1,14 @@
|
||||
package com.github.nullptroma.wallenc.domain.encrypt
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonDirectory
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonFile
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.entity.EncryptedDirectory
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.entity.EncryptedFile
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.entity.EncryptedMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -15,10 +16,13 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherInputStream
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.io.encoding.Base64
|
||||
@@ -46,8 +50,6 @@ class EncryptedStorageAccessor(
|
||||
private val _secretKey = SecretKeySpec(key.to32Bytes(), "AES")
|
||||
|
||||
init {
|
||||
val enc = encryptPath("/hello/world/test.txt")
|
||||
val dec = decryptPath(enc)
|
||||
collectSourceState(CoroutineScope(ioDispatcher))
|
||||
}
|
||||
|
||||
@@ -55,16 +57,7 @@ class EncryptedStorageAccessor(
|
||||
coroutineScope.launch {
|
||||
launch {
|
||||
source.filesUpdates.collect {
|
||||
val files = it.data.map {
|
||||
val meta = it.metaInfo
|
||||
EncryptedFile(EncryptedMetaInfo(
|
||||
size = meta.size,
|
||||
isDeleted = meta.isDeleted,
|
||||
isHidden = meta.isHidden,
|
||||
lastModified = meta.lastModified,
|
||||
path = decryptPath(meta.path)
|
||||
))
|
||||
}
|
||||
val files = it.data.map(::decryptEntity)
|
||||
_filesUpdates.emit(DataPackage(
|
||||
data = files,
|
||||
isLoading = it.isLoading,
|
||||
@@ -75,16 +68,7 @@ class EncryptedStorageAccessor(
|
||||
|
||||
launch {
|
||||
source.dirsUpdates.collect {
|
||||
val dirs = it.data.map {
|
||||
val meta = it.metaInfo
|
||||
EncryptedDirectory(EncryptedMetaInfo(
|
||||
size = meta.size,
|
||||
isDeleted = meta.isDeleted,
|
||||
isHidden = meta.isHidden,
|
||||
lastModified = meta.lastModified,
|
||||
path = decryptPath(meta.path)
|
||||
), it.elementsCount)
|
||||
}
|
||||
val dirs = it.data.map(::decryptEntity)
|
||||
_dirsUpdates.emit(DataPackage(
|
||||
data = dirs,
|
||||
isLoading = it.isLoading,
|
||||
@@ -95,6 +79,42 @@ class EncryptedStorageAccessor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun encryptEntity(file: IFile): IFile {
|
||||
return CommonFile(encryptMeta(file.metaInfo))
|
||||
}
|
||||
|
||||
private fun decryptEntity(file: IFile): IFile {
|
||||
return CommonFile(decryptMeta(file.metaInfo))
|
||||
}
|
||||
|
||||
private fun encryptEntity(dir: IDirectory): IDirectory {
|
||||
return CommonDirectory(encryptMeta(dir.metaInfo), dir.elementsCount)
|
||||
}
|
||||
|
||||
private fun decryptEntity(dir: IDirectory): IDirectory {
|
||||
return CommonDirectory(decryptMeta(dir.metaInfo), dir.elementsCount)
|
||||
}
|
||||
|
||||
private fun encryptMeta(meta: IMetaInfo): CommonMetaInfo {
|
||||
return CommonMetaInfo(
|
||||
size = meta.size,
|
||||
isDeleted = meta.isDeleted,
|
||||
isHidden = meta.isHidden,
|
||||
lastModified = meta.lastModified,
|
||||
path = encryptPath(meta.path)
|
||||
)
|
||||
}
|
||||
|
||||
private fun decryptMeta(meta: IMetaInfo): CommonMetaInfo {
|
||||
return CommonMetaInfo(
|
||||
size = meta.size,
|
||||
isDeleted = meta.isDeleted,
|
||||
isHidden = meta.isHidden,
|
||||
lastModified = meta.lastModified,
|
||||
path = decryptPath(meta.path)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
private fun encryptString(str: String): String {
|
||||
val cipher = Cipher.getInstance(AES_FOR_STRINGS)
|
||||
@@ -121,7 +141,6 @@ class EncryptedStorageAccessor(
|
||||
for (segment in path)
|
||||
segments.add(encryptString(segment.pathString))
|
||||
val res = Path("/",*(segments.toTypedArray()))
|
||||
logger.debug("encryptPath", "$pathStr to $res")
|
||||
return res.pathString
|
||||
}
|
||||
|
||||
@@ -131,56 +150,83 @@ class EncryptedStorageAccessor(
|
||||
for (segment in path)
|
||||
segments.add(decryptString(segment.pathString))
|
||||
val res = Path("/",*(segments.toTypedArray()))
|
||||
logger.debug("decryptPath", "$pathStr to $res")
|
||||
return res.pathString
|
||||
}
|
||||
|
||||
override suspend fun getAllFiles(): List<IFile> {
|
||||
TODO("Not yet implemented")
|
||||
return source.getAllFiles().map(::decryptEntity)
|
||||
}
|
||||
|
||||
override suspend fun getFiles(path: String): List<IFile> {
|
||||
TODO("Not yet implemented")
|
||||
return source.getFiles(encryptPath(path)).map(::decryptEntity)
|
||||
}
|
||||
|
||||
override fun getFilesFlow(path: String): Flow<DataPackage<List<IFile>>> {
|
||||
TODO("Not yet implemented")
|
||||
val flow = source.getFilesFlow(encryptPath(path)).map {
|
||||
DataPackage(
|
||||
data = it.data.map(::decryptEntity),
|
||||
isLoading = it.isLoading,
|
||||
isError = it.isError
|
||||
)
|
||||
}
|
||||
return flow
|
||||
}
|
||||
|
||||
override suspend fun getAllDirs(): List<IDirectory> {
|
||||
TODO("Not yet implemented")
|
||||
return source.getAllDirs().map(::decryptEntity)
|
||||
}
|
||||
|
||||
override suspend fun getDirs(path: String): List<IDirectory> {
|
||||
TODO("Not yet implemented")
|
||||
return source.getDirs(encryptPath(path)).map(::decryptEntity)
|
||||
}
|
||||
|
||||
override fun getDirsFlow(path: String): Flow<DataPackage<List<IDirectory>>> {
|
||||
TODO("Not yet implemented")
|
||||
val flow = source.getDirsFlow(encryptPath(path)).map {
|
||||
DataPackage(
|
||||
data = it.data.map(::decryptEntity),
|
||||
isLoading = it.isLoading,
|
||||
isError = it.isError
|
||||
)
|
||||
}
|
||||
return flow
|
||||
}
|
||||
|
||||
override suspend fun touchFile(path: String) {
|
||||
TODO("Not yet implemented")
|
||||
source.touchFile(encryptPath(path))
|
||||
}
|
||||
|
||||
override suspend fun touchDir(path: String) {
|
||||
TODO("Not yet implemented")
|
||||
source.touchDir(encryptPath(path))
|
||||
}
|
||||
|
||||
override suspend fun delete(path: String) {
|
||||
TODO("Not yet implemented")
|
||||
source.delete(encryptPath(path))
|
||||
}
|
||||
|
||||
override suspend fun openWrite(path: String): OutputStream {
|
||||
TODO("Not yet implemented")
|
||||
val stream = source.openWrite(encryptPath(path))
|
||||
val iv = IvParameterSpec(Random.nextBytes(IV_LEN))
|
||||
stream.write(iv.iv) // Запись инициализационного вектора сырой файл
|
||||
val cipher = Cipher.getInstance(AES_FOR_STRINGS)
|
||||
cipher.init(Cipher.ENCRYPT_MODE, _secretKey, iv) // инициализация шифратора
|
||||
return CipherOutputStream(stream, cipher)
|
||||
}
|
||||
|
||||
override suspend fun openRead(path: String): InputStream {
|
||||
TODO("Not yet implemented")
|
||||
val stream = source.openRead(encryptPath(path))
|
||||
val ivBytes = ByteArray(IV_LEN) // Буфер для 16 байт IV
|
||||
val bytesRead = stream.read(ivBytes) // Чтение IV вектора
|
||||
if(bytesRead != IV_LEN)
|
||||
throw Exception("TODO iv не прочитан")
|
||||
val iv = IvParameterSpec(ivBytes)
|
||||
|
||||
val cipher = Cipher.getInstance(AES_FOR_STRINGS)
|
||||
cipher.init(Cipher.DECRYPT_MODE, _secretKey, iv)
|
||||
return CipherInputStream(stream, cipher)
|
||||
}
|
||||
|
||||
override suspend fun moveToTrash(path: String) {
|
||||
TODO("Not yet implemented")
|
||||
source.moveToTrash(encryptPath(path))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.domain.encrypt.entity
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IDirectory
|
||||
|
||||
data class EncryptedDirectory(
|
||||
override val metaInfo: EncryptedMetaInfo,
|
||||
override val elementsCount: Int?
|
||||
) : IDirectory
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.domain.encrypt.entity
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
||||
|
||||
data class EncryptedFile(override val metaInfo: EncryptedMetaInfo) : IFile
|
||||
Reference in New Issue
Block a user