Skip to content

Commit

Permalink
Added 260 new Crypto algorithms by #1589
Browse files Browse the repository at this point in the history
  • Loading branch information
T8RIN committed Jan 14, 2025
1 parent e727edc commit 3efb859
Show file tree
Hide file tree
Showing 33 changed files with 594 additions and 349 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,14 @@ import android.app.Application
import com.arkivanov.decompose.DecomposeExperimentFlags
import com.arkivanov.decompose.ExperimentalDecomposeApi
import dagger.hilt.android.HiltAndroidApp
import org.bouncycastle.jce.provider.BouncyCastleProvider
import ru.tech.imageresizershrinker.core.domain.model.ChecksumType
import java.security.Security
import ru.tech.imageresizershrinker.app.presentation.components.utils.registerSecurityProviders


@HiltAndroidApp
class ImageToolboxApplication : Application() {

init {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
Security.addProvider(BouncyCastleProvider())
ChecksumType.registerSecurityMessageDigests(
Security.getAlgorithms("MessageDigest")
.filterNotNull()
)
registerSecurityProviders()
}

@OptIn(ExperimentalDecomposeApi::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2025 T8RIN (Malik Mukhametzyanov)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
*/

package ru.tech.imageresizershrinker.app.presentation.components.utils

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.operator.DefaultAlgorithmNameFinder
import ru.tech.imageresizershrinker.core.domain.model.CipherType
import ru.tech.imageresizershrinker.core.domain.model.HashingType
import java.security.Security
import kotlin.text.all

internal fun registerSecurityProviders() {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
Security.addProvider(BouncyCastleProvider())

HashingType.registerSecurityMessageDigests(
Security.getAlgorithms("MessageDigest").filterNotNull()
)

CoroutineScope(Dispatchers.Default).launch {
val finder = DefaultAlgorithmNameFinder()

CipherType.registerSecurityCiphers(
Security.getAlgorithms("Cipher").filterNotNull().map { cipher ->
val oid = cipher.removePrefix("OID.")
if (oid.all { it.isDigit() || it.isWhitespace() || it == '.' }) {
finder.getAlgorithmName(
ASN1ObjectIdentifier(oid)
)
} else {
oid
}
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal class AndroidFilenameCreator @Inject constructor(
): String {
val extension = saveTarget.extension

val checksumType = settingsState.checksumTypeForFilename
val checksumType = settingsState.hashingTypeForFilename
if (checksumType != null && saveTarget.data.isNotEmpty()) {
val name = checksumType.computeFromByteArray(saveTarget.data)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@

package ru.tech.imageresizershrinker.core.data.utils

import ru.tech.imageresizershrinker.core.domain.model.ChecksumType
import ru.tech.imageresizershrinker.core.domain.model.HashingType
import ru.tech.imageresizershrinker.core.domain.saving.io.Readable
import java.io.InputStream
import java.security.MessageDigest

private const val STREAM_BUFFER_LENGTH = 1024

fun ChecksumType.computeFromReadable(
fun HashingType.computeFromReadable(
readable: Readable
): String = computeFromByteArray(readable.readBytes())

internal fun ChecksumType.computeFromByteArray(
internal fun HashingType.computeFromByteArray(
byteArray: ByteArray
): String = computeFromInputStream(byteArray.inputStream())

internal fun ChecksumType.computeFromInputStream(
internal fun HashingType.computeFromInputStream(
inputStream: InputStream
): String = inputStream.buffered().use {
val byteArray = updateDigest(it).digest()
Expand Down Expand Up @@ -92,11 +92,11 @@ internal fun encodeHex(
/**
* Reads through an InputStream and updates the digest for the data
*
* @param ChecksumType The ChecksumType to use (e.g. MD5)
* @param HashingType The ChecksumType to use (e.g. MD5)
* @param data Data to digest
* @return the digest
*/
private fun ChecksumType.updateDigest(
private fun HashingType.updateDigest(
data: InputStream
): MessageDigest {
val digest = toDigest()
Expand All @@ -123,4 +123,4 @@ private val DIGITS_UPPER =
charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')


private fun ChecksumType.toDigest(): MessageDigest = MessageDigest.getInstance(digest)
private fun HashingType.toDigest(): MessageDigest = MessageDigest.getInstance(digest)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2025 T8RIN (Malik Mukhametzyanov)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
*/

package ru.tech.imageresizershrinker.core.domain.model

@ConsistentCopyVisibility
/**
* [CipherType] multiplatform domain wrapper for java Cipher, in order to add custom digests, you need to call [registerSecurityCiphers] when process created
**/
data class CipherType private constructor(
val cipher: String,
val name: String = cipher
) {
companion object {
val AES_NO_PADDING = CipherType("AES/GCM/NoPadding")

private var securityCiphers: List<String>? = null

fun registerSecurityCiphers(ciphers: List<String>) {
if (!securityCiphers.isNullOrEmpty()) {
throw IllegalArgumentException("SecurityCiphers already registered")
}
securityCiphers = ciphers.distinctBy { it.replace("OID.", "").uppercase() }
}

val entries: List<CipherType> by lazy {
val available = securityCiphers?.mapNotNull { cipher ->
if (cipher.isEmpty()) null
else {
val strippedCipher = cipher.replace("OID.", "")
SecureAlgorithmsMapping.findMatch(strippedCipher)?.let { mapping ->
CipherType(
cipher = cipher,
name = mapping.algorithm
)
} ?: CipherType(cipher = cipher)
}
}?.sortedBy { it.cipher } ?: emptyList()

listOf(
AES_NO_PADDING
).let {
it + available
}.distinctBy { it.name.replace("-", "") }
}

fun fromString(
cipher: String?
): CipherType? = cipher?.let {
entries.find {
it.cipher == cipher
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,40 @@ package ru.tech.imageresizershrinker.core.domain.model

@ConsistentCopyVisibility
/**
* [ChecksumType] multiplatform domain wrapper for java MessageDigest, in order to add custom digests, you need to call [registerSecurityMessageDigests] when process created
* [HashingType] multiplatform domain wrapper for java MessageDigest, in order to add custom digests, you need to call [registerSecurityMessageDigests] when process created
**/
data class ChecksumType private constructor(
data class HashingType private constructor(
val digest: String,
val name: String = digest
) {
companion object {
val MD5 = ChecksumType("MD5")
val SHA_1 = ChecksumType("SHA-1")
val SHA_224 = ChecksumType("SHA-224")
val SHA_256 = ChecksumType("SHA-256")
val SHA_384 = ChecksumType("SHA-384")
val SHA_512 = ChecksumType("SHA-512")
val MD5 = HashingType("MD5")
val SHA_1 = HashingType("SHA-1")
val SHA_224 = HashingType("SHA-224")
val SHA_256 = HashingType("SHA-256")
val SHA_384 = HashingType("SHA-384")
val SHA_512 = HashingType("SHA-512")

private var securityMessageDigests: List<String>? = null

fun registerSecurityMessageDigests(digests: List<String>) {
if (!securityMessageDigests.isNullOrEmpty()) {
throw IllegalArgumentException("SecurityMessageDigests already registered")
}
securityMessageDigests = digests.distinctBy { it.replace("OID.", "") }
securityMessageDigests = digests.distinctBy { it.replace("OID.", "").uppercase() }
}

val entries: List<ChecksumType> by lazy {
val entries: List<HashingType> by lazy {
val available = securityMessageDigests?.mapNotNull { messageDigest ->
if (messageDigest.isEmpty()) null
else {
val digest = messageDigest.replace("OID.", "")
ChecksumTypeMapping.findMatch(digest)?.let { mapping ->
ChecksumType(
SecureAlgorithmsMapping.findMatch(digest)?.let { mapping ->
HashingType(
digest = messageDigest,
name = mapping.algorithm
)
} ?: ChecksumType(digest = messageDigest)
} ?: HashingType(digest = messageDigest)
}
}?.sortedBy { it.digest } ?: emptyList()

Expand All @@ -70,7 +70,7 @@ data class ChecksumType private constructor(

fun fromString(
digest: String?
): ChecksumType? = digest?.let {
): HashingType? = digest?.let {
entries.find {
it.digest == digest
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ package ru.tech.imageresizershrinker.core.domain.model
* is determined by existing usage and may be in lowercase/uppercase in
* order to match existing output.
*/
enum class ChecksumTypeMapping {
enum class SecureAlgorithmsMapping {
// X.500 Attributes 2.5.4.*
CommonName("2.5.4.3"),
Surname("2.5.4.4"),
Expand Down Expand Up @@ -424,7 +424,44 @@ enum class ChecksumTypeMapping {
JAVASOFT_JCEKeyProtector("1.3.6.1.4.1.42.2.19.1"),
MICROSOFT_ExportApproved("1.3.6.1.4.1.311.10.3.3"),

Blowfish("1.3.6.1.4.1.3029.1.1.2");
Blowfish("1.3.6.1.4.1.3029.1.1.2"),

asn1_module("1.2.410.200046.1.1.1", "ASN1-module"),
aria256_ecb("1.2.410.200046.1.1.11", "ARIA256/ECB"),
aria256_cbc("1.2.410.200046.1.1.12", "ARIA256/CBC"),
aria256_cfb("1.2.410.200046.1.1.13", "ARIA256/CFB"),
aria256_ofb("1.2.410.200046.1.1.14", "ARIA256/OFB"),
aria128_cbc("1.2.410.200046.1.1.2", "ARIA128/CBC"),
aria128_cfb("1.2.410.200046.1.1.3", "ARIA128/CFB"),
aria128_ofb("1.2.410.200046.1.1.4", "ARIA128/OFB"),
aria192_ecb("1.2.410.200046.1.1.6", "ARIA192/ECB"),
aria192_cbc("1.2.410.200046.1.1.7", "ARIA192/CBC"),
aria192_cfb("1.2.410.200046.1.1.8", "ARIA192/CFB"),
aria192_ofb("1.2.410.200046.1.1.9", "ARIA192/OFB"),
GOST_R28147_89("1.2.643.2.2.13.0", "GOST-R28147-89"),
CryptoPro("1.2.643.2.2.13.1", "CryptoPro"),
GOST_28147_89("1.2.643.2.2.21", "GOST-28147-89"),
dstu7624_ecb_128("1.2.804.2.1.1.1.1.1.3.1.1", "DSTU7624/ECB-128"),
dstu7624_ecb_256("1.2.804.2.1.1.1.1.1.3.1.2", "DSTU7624/ECB-256"),
dstu7624_ecb_512("1.2.804.2.1.1.1.1.1.3.1.3", "DSTU7624/ECB-512"),
dstu7624_ctr_128("1.2.804.2.1.1.1.1.1.3.2.1", "DSTU7624/CTR-128"),
dstu7624_ctr_256("1.2.804.2.1.1.1.1.1.3.2.2", "DSTU7624/CTR-256"),
dstu7624_ctr_512("1.2.804.2.1.1.1.1.1.3.2.3", "DSTU7624/CTR-512"),
dstu7624_cfb_128("1.2.804.2.1.1.1.1.1.3.3.1", "DSTU7624/CFB-128"),
dstu7624_cfb_256("1.2.804.2.1.1.1.1.1.3.3.2", "DSTU7624/CFB-256"),
dstu7624_cfb_512("1.2.804.2.1.1.1.1.1.3.3.3", "DSTU7624/CFB-512"),
dstu7624_cbc_128("1.2.804.2.1.1.1.1.1.3.5.1", "DSTU7624/CBC-128"),
dstu7624_cbc_256("1.2.804.2.1.1.1.1.1.3.5.2", "DSTU7624/CBC-256"),
dstu7624_cbc_512("1.2.804.2.1.1.1.1.1.3.5.3", "DSTU7624/CBC-512"),
dstu7624_ofb_128("1.2.804.2.1.1.1.1.1.3.6.1", "DSTU7624/OFB-128"),
dstu7624_ofb_256("1.2.804.2.1.1.1.1.1.3.6.2", "DSTU7624/OFB-256"),
dstu7624_ofb_512("1.2.804.2.1.1.1.1.1.3.6.3", "DSTU7624/OFB-512"),
dstu7624_ccm_128("1.2.804.2.1.1.1.1.1.3.8.1", "DSTU7624/CCM-128"),
dstu7624_ccm_256("1.2.804.2.1.1.1.1.1.3.8.2", "DSTU7624/CCM-256"),
dstu7624_ccm_512("1.2.804.2.1.1.1.1.1.3.8.3", "DSTU7624/CCM-512"),
CMS3DESwrap("1.2.840.113549.1.9.16.3.6", "CMS3DESwrap"),
des_CBC("1.3.14.3.2.7", "DES/CBC"),
Joint_RSA("2.5.8.1.1", "Joint-RSA");

val algorithm: String
val oid: String
Expand All @@ -449,7 +486,7 @@ enum class ChecksumTypeMapping {
companion object {
fun findMatch(
oidOrName: String
): ChecksumTypeMapping? = ChecksumTypeMapping.entries.find {
): SecureAlgorithmsMapping? = SecureAlgorithmsMapping.entries.find {
it.oid == oidOrName || it.algorithm == oidOrName || it.aliases.contains(oidOrName)
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/resources/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@
<string name="draw_on_background_sub">Pick background color and draw on top of it</string>
<string name="background_color">Background color</string>
<string name="cipher">Cipher</string>
<string name="cipher_sub">Encrypt and Decrypt any file (not only the image) based on AES crypto algorithm</string>
<string name="cipher_sub">Encrypt and Decrypt any file (not only the image) based on various available crypto algorithms</string>
<string name="pick_file">Pick file</string>
<string name="encrypt">Encrypt</string>
<string name="decrypt">Decrypt</string>
Expand All @@ -279,7 +279,7 @@
<string name="implementation">Implementation</string>
<string name="compatibility">Compatibility</string>
<string name="features_sub">Password-based encryption of files. Proceeded files can be stored in the selected directory or shared. Decrypted files can also be opened directly.</string>
<string name="implementation_sub">AES-256, GCM mode, no padding, 12 bytes random IVs. Keys are used as SHA-3 hashes (256 bits).</string>
<string name="implementation_sub">AES-256, GCM mode, no padding, 12 bytes random IVs. (By default, but you can select needed algorithm) Keys are used as SHA-3 hashes (256 bits).</string>
<string name="file_size">File size</string>
<string name="file_size_sub">The maximum file size is restricted by the Android OS and available memory, which is device dependent.
\nPlease note: memory is not storage.</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ package ru.tech.imageresizershrinker.core.settings.domain

import ru.tech.imageresizershrinker.core.domain.image.model.ImageScaleMode
import ru.tech.imageresizershrinker.core.domain.image.model.ResizeType
import ru.tech.imageresizershrinker.core.domain.model.ChecksumType
import ru.tech.imageresizershrinker.core.domain.model.ColorModel
import ru.tech.imageresizershrinker.core.domain.model.HashingType
import ru.tech.imageresizershrinker.core.domain.model.PerformanceClass
import ru.tech.imageresizershrinker.core.domain.model.SystemBarsVisibility
import ru.tech.imageresizershrinker.core.settings.domain.model.ColorHarmonizer
Expand Down Expand Up @@ -213,7 +213,7 @@ interface SettingsInteractor : SimpleSettingsInteractor {

suspend fun setFastSettingsSide(side: FastSettingsSide)

suspend fun setChecksumTypeForFilename(type: ChecksumType?)
suspend fun setChecksumTypeForFilename(type: HashingType?)

suspend fun setCustomFonts(fonts: List<DomainFontFamily.Custom>)

Expand Down
Loading

0 comments on commit 3efb859

Please sign in to comment.