diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt index 15360c7..9e2d84b 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt @@ -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() - 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() - 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() 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) { diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalDirectory.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalDirectory.kt deleted file mode 100644 index a98b8d3..0000000 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalDirectory.kt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalFile.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalFile.kt deleted file mode 100644 index 9076819..0000000 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalFile.kt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalMetaInfo.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalMetaInfo.kt deleted file mode 100644 index 81c3734..0000000 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/entity/LocalMetaInfo.kt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonDirectory.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonDirectory.kt new file mode 100644 index 0000000..d6ce90a --- /dev/null +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonDirectory.kt @@ -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 \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt new file mode 100644 index 0000000..4199828 --- /dev/null +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt @@ -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 \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedMetaInfo.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonMetaInfo.kt similarity index 58% rename from domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedMetaInfo.kt rename to domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonMetaInfo.kt index 9857a98..477bd94 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedMetaInfo.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonMetaInfo.kt @@ -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 \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt index 7ee9733..1b0989e 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt @@ -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 { - TODO("Not yet implemented") + return source.getAllFiles().map(::decryptEntity) } override suspend fun getFiles(path: String): List { - TODO("Not yet implemented") + return source.getFiles(encryptPath(path)).map(::decryptEntity) } override fun getFilesFlow(path: String): Flow>> { - 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 { - TODO("Not yet implemented") + return source.getAllDirs().map(::decryptEntity) } override suspend fun getDirs(path: String): List { - TODO("Not yet implemented") + return source.getDirs(encryptPath(path)).map(::decryptEntity) } override fun getDirsFlow(path: String): Flow>> { - 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)) } diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedDirectory.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedDirectory.kt deleted file mode 100644 index ac7acd8..0000000 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedDirectory.kt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedFile.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedFile.kt deleted file mode 100644 index 15c47dc..0000000 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/entity/EncryptedFile.kt +++ /dev/null @@ -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 \ No newline at end of file