Статичный IV для имён файлов
This commit is contained in:
@@ -9,10 +9,7 @@ import java.time.Instant
|
||||
|
||||
|
||||
data class CommonStorageMetaInfo(
|
||||
override val encInfo: StorageEncryptionInfo = StorageEncryptionInfo(
|
||||
isEncrypted = false,
|
||||
encryptedTestData = null
|
||||
),
|
||||
override val encInfo: StorageEncryptionInfo? = null,
|
||||
override val name: String? = null,
|
||||
override val lastModified: Instant = Clock.systemUTC().instant()
|
||||
) : IStorageMetaInfo
|
||||
@@ -1,6 +1,24 @@
|
||||
package com.github.nullptroma.wallenc.domain.datatypes
|
||||
|
||||
data class StorageEncryptionInfo(
|
||||
val isEncrypted: Boolean,
|
||||
val encryptedTestData: String?
|
||||
)
|
||||
val encryptedTestData: String,
|
||||
val pathIv: ByteArray
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as StorageEncryptionInfo
|
||||
|
||||
if (encryptedTestData != other.encryptedTestData) return false
|
||||
if (!pathIv.contentEquals(other.pathIv)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = encryptedTestData.hashCode()
|
||||
result = 31 * result + pathIv.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
import kotlin.random.Random
|
||||
|
||||
class Encryptor(private var _secretKey: SecretKey?) : DisposableHandle {
|
||||
class Encryptor(private var secretKey: SecretKey) : DisposableHandle {
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun encryptString(str: String): String {
|
||||
val bytesToEncrypt = str.toByteArray(Charsets.UTF_8)
|
||||
@@ -30,54 +30,57 @@ class Encryptor(private var _secretKey: SecretKey?) : DisposableHandle {
|
||||
}
|
||||
|
||||
fun encryptBytes(bytes: ByteArray): ByteArray {
|
||||
val secretKey = _secretKey ?: throw Exception("Object was disposed")
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
val iv = IvParameterSpec(Random.nextBytes(IV_LEN))
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv)
|
||||
val encryptedBytes = iv.iv + cipher.doFinal(bytes) // iv + зашифрованные байты
|
||||
val ivSpec = IvParameterSpec(Random.nextBytes(IV_LEN))
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec)
|
||||
val encryptedBytes = ivSpec.iv + cipher.doFinal(bytes) // iv + зашифрованные байты
|
||||
return encryptedBytes
|
||||
}
|
||||
|
||||
fun decryptBytes(bytes: ByteArray): ByteArray {
|
||||
val secretKey = _secretKey ?: throw Exception("Object was disposed")
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
val iv = IvParameterSpec(bytes.take(IV_LEN).toByteArray())
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv)
|
||||
val ivSpec = IvParameterSpec(bytes.take(IV_LEN).toByteArray())
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
|
||||
val decryptedBytes = cipher.doFinal(bytes.drop(IV_LEN).toByteArray())
|
||||
return decryptedBytes
|
||||
}
|
||||
|
||||
fun encryptStream(stream: OutputStream): OutputStream {
|
||||
val secretKey = _secretKey ?: throw Exception("Object was disposed")
|
||||
val iv = IvParameterSpec(Random.nextBytes(IV_LEN))
|
||||
stream.write(iv.iv) // Запись инициализационного вектора сырой файл
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val ivSpec = IvParameterSpec(Random.nextBytes(IV_LEN))
|
||||
stream.write(ivSpec.iv) // Запись инициализационного вектора сырой файл
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv) // инициализация шифратора
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec) // инициализация шифратора
|
||||
return CipherOutputStream(stream, cipher)
|
||||
}
|
||||
|
||||
fun decryptStream(stream: InputStream): InputStream {
|
||||
val secretKey = _secretKey ?: throw Exception("Object was disposed")
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
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 ivSpec = IvParameterSpec(ivBytes)
|
||||
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv)
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
|
||||
return CipherInputStream(stream, cipher)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
_secretKey?.destroy()
|
||||
_secretKey = null
|
||||
secretKey.destroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val IV_LEN = 16
|
||||
public const val IV_LEN = 16
|
||||
public const val AES_SETTINGS = "AES/CBC/PKCS5Padding"
|
||||
private const val TEST_DATA_LEN = 512
|
||||
private const val AES_SETTINGS = "AES/CBC/PKCS5Padding"
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun generateEncryptionInfo(key: EncryptKey) : StorageEncryptionInfo {
|
||||
@@ -85,15 +88,13 @@ class Encryptor(private var _secretKey: SecretKey?) : DisposableHandle {
|
||||
val testData = ByteArray(TEST_DATA_LEN)
|
||||
val encryptedData = encryptor.encryptBytes(testData)
|
||||
return StorageEncryptionInfo(
|
||||
isEncrypted = true,
|
||||
encryptedTestData = Base64.Default.encode(encryptedData)
|
||||
encryptedTestData = Base64.Default.encode(encryptedData),
|
||||
pathIv = Random.nextBytes(IV_LEN)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun checkKey(key: EncryptKey, encInfo: StorageEncryptionInfo): Boolean {
|
||||
if(encInfo.encryptedTestData == null)
|
||||
return false
|
||||
val encryptor = Encryptor(key.toAesKey())
|
||||
try {
|
||||
val encData = Base64.Default.decode(encInfo.encryptedTestData)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.github.nullptroma.wallenc.domain.encrypt
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor.Companion.AES_SETTINGS
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor.Companion.IV_LEN
|
||||
import kotlinx.coroutines.DisposableHandle
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherInputStream
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.SecretKey
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
import kotlin.random.Random
|
||||
|
||||
class EncryptorWithStaticIv(private var secretKey: SecretKey, iv: ByteArray) : DisposableHandle {
|
||||
private val ivSpec = IvParameterSpec(iv)
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun encryptString(str: String): String {
|
||||
val bytesToEncrypt = str.toByteArray(Charsets.UTF_8)
|
||||
val encryptedBytes = encryptBytes(bytesToEncrypt)
|
||||
return Base64.Default.encode(encryptedBytes).replace("/", ".")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun decryptString(str: String): String {
|
||||
val bytesToDecrypt = Base64.Default.decode(str.replace(".", "/"))
|
||||
val decryptedBytes = decryptBytes(bytesToDecrypt)
|
||||
return String(decryptedBytes, Charsets.UTF_8)
|
||||
}
|
||||
|
||||
fun encryptBytes(bytes: ByteArray): ByteArray {
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec)
|
||||
val encryptedBytes = cipher.doFinal(bytes) // зашифрованные байты
|
||||
return encryptedBytes
|
||||
}
|
||||
|
||||
fun decryptBytes(bytes: ByteArray): ByteArray {
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
|
||||
val decryptedBytes = cipher.doFinal(bytes)
|
||||
return decryptedBytes
|
||||
}
|
||||
|
||||
fun encryptStream(stream: OutputStream): OutputStream {
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec) // инициализация шифратора
|
||||
return CipherOutputStream(stream, cipher)
|
||||
}
|
||||
|
||||
fun decryptStream(stream: InputStream): InputStream {
|
||||
if(secretKey.isDestroyed)
|
||||
throw Exception("Object was destroyed")
|
||||
val cipher = Cipher.getInstance(AES_SETTINGS)
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
|
||||
return CipherInputStream(stream, cipher)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
secretKey.destroy()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,18 @@
|
||||
package com.github.nullptroma.wallenc.domain.interfaces
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
sealed interface IStorageInfo {
|
||||
val uuid: UUID
|
||||
val isAvailable: StateFlow<Boolean>
|
||||
val size: StateFlow<Long?>
|
||||
val numberOfFiles: StateFlow<Int?>
|
||||
val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
val isVirtualStorage: Boolean
|
||||
}
|
||||
|
||||
interface IStorage: IStorageInfo {
|
||||
val accessor: IStorageAccessor
|
||||
@@ -8,3 +20,9 @@ interface IStorage: IStorageInfo {
|
||||
suspend fun rename(newName: String)
|
||||
suspend fun setEncInfo(encInfo: StorageEncryptionInfo)
|
||||
}
|
||||
|
||||
interface IStorageMetaInfo {
|
||||
val encInfo: StorageEncryptionInfo?
|
||||
val name: String?
|
||||
val lastModified: Instant
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.domain.interfaces
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.UUID
|
||||
|
||||
sealed interface IStorageInfo {
|
||||
val uuid: UUID
|
||||
val isAvailable: StateFlow<Boolean>
|
||||
val size: StateFlow<Long?>
|
||||
val numberOfFiles: StateFlow<Int?>
|
||||
val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
val isVirtualStorage: Boolean
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.github.nullptroma.wallenc.domain.interfaces
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import java.time.Instant
|
||||
|
||||
interface IStorageMetaInfo {
|
||||
val encInfo: StorageEncryptionInfo
|
||||
val name: String?
|
||||
val lastModified: Instant
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user