diff --git a/.gitignore b/.gitignore index 56580994de..b90e00204b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,13 @@ local.properties /.idea/misc.xml /release /docker-mailserver/maildata_volume/ + # Keystore files *.jks + # keystore info -/FlowCrypt/keystore.properties \ No newline at end of file +/FlowCrypt/keystore.properties + +# some runtime files +.attach* + diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/util/gson/MsgBlockAdapter.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/util/gson/MsgBlockAdapter.kt index 923214a4a3..9e1a4a7b63 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/util/gson/MsgBlockAdapter.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/util/gson/MsgBlockAdapter.kt @@ -5,7 +5,7 @@ package com.flowcrypt.email.util.gson -import com.flowcrypt.email.api.retrofit.response.model.node.BaseMsgBlock +import com.flowcrypt.email.api.retrofit.response.model.node.GenericMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.DecryptErrorMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.PublicKeyMsgBlock @@ -31,7 +31,7 @@ class MsgBlockAdapter : JsonDeserializer { MsgBlock.Type.DECRYPT_ERROR -> return context.deserialize(json, DecryptErrorMsgBlock::class.java) - else -> return context.deserialize(json, BaseMsgBlock::class.java) + else -> return context.deserialize(json, GenericMsgBlock::class.java) } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/IncomingMessageInfo.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/IncomingMessageInfo.kt index 5a2f7daedc..95cbff22e2 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/IncomingMessageInfo.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/email/model/IncomingMessageInfo.kt @@ -7,7 +7,7 @@ package com.flowcrypt.email.api.email.model import android.os.Parcel import android.os.Parcelable -import com.flowcrypt.email.api.retrofit.response.model.node.BaseMsgBlock +import com.flowcrypt.email.api.retrofit.response.model.node.GenericMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock import com.flowcrypt.email.database.entity.MessageEntity import com.flowcrypt.email.model.MessageEncryptionType @@ -90,7 +90,7 @@ data class IncomingMessageInfo constructor(val msgEntity: MessageEntity, source.readParcelable(LocalFolder::class.java.classLoader), source.readString(), source.readString(), - mutableListOf().apply { source.readTypedList(this, BaseMsgBlock.CREATOR) }, + mutableListOf().apply { source.readTypedList(this, GenericMsgBlock.CREATOR) }, source.readString(), source.readParcelable(MessageEncryptionType::class.java.classLoader)!! ) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/node/gson/MsgBlockDeserializer.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/node/gson/MsgBlockDeserializer.kt index a5ab837f11..5e17ff4e00 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/node/gson/MsgBlockDeserializer.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/node/gson/MsgBlockDeserializer.kt @@ -5,7 +5,7 @@ package com.flowcrypt.email.api.retrofit.node.gson -import com.flowcrypt.email.api.retrofit.response.model.node.BaseMsgBlock +import com.flowcrypt.email.api.retrofit.response.model.node.GenericMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.DecryptErrorMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.DecryptedAttMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock @@ -28,14 +28,13 @@ class MsgBlockDeserializer : JsonDeserializer { override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): MsgBlock? { val jsonObject = json.asJsonObject - val msgBlock: MsgBlock? = when (context.deserialize(jsonObject.get(BaseMsgBlock.TAG_TYPE), MsgBlock.Type::class.java)) { + val msgBlock: MsgBlock? = when ( + context.deserialize(jsonObject.get(MsgBlock.TAG_TYPE), MsgBlock.Type::class.java) + ) { MsgBlock.Type.PUBLIC_KEY -> context.deserialize(json, PublicKeyMsgBlock::class.java) - MsgBlock.Type.DECRYPT_ERROR -> context.deserialize(json, DecryptErrorMsgBlock::class.java) - MsgBlock.Type.DECRYPTED_ATT -> context.deserialize(json, DecryptedAttMsgBlock::class.java) - - else -> context.deserialize(json, BaseMsgBlock::class.java) + else -> context.deserialize(json, GenericMsgBlock::class.java) } if (msgBlock?.type == MsgBlock.Type.PUBLIC_KEY) { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/BaseMsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/GenericMsgBlock.kt similarity index 54% rename from FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/BaseMsgBlock.kt rename to FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/GenericMsgBlock.kt index 9f44a7e193..d7869b9c9d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/BaseMsgBlock.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/GenericMsgBlock.kt @@ -1,6 +1,8 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: + * DenBond7 + * Ivan Pizhenko */ package com.flowcrypt.email.api.retrofit.response.model.node @@ -10,16 +12,18 @@ import android.os.Parcelable import com.google.gson.annotations.Expose /** - * It's a base [MsgBlock] + * Generic message block represents any message block without a dedicated support. * * @author Denis Bondarenko * Date: 3/26/19 * Time: 9:46 AM * E-mail: DenBond7@gmail.com + * + * @author Ivan Pizhenko */ -data class BaseMsgBlock(@Expose override val type: MsgBlock.Type = MsgBlock.Type.UNKNOWN, - @Expose override val content: String?, - @Expose override val complete: Boolean) : MsgBlock { +data class GenericMsgBlock(@Expose override val type: MsgBlock.Type = MsgBlock.Type.UNKNOWN, + @Expose override val content: String?, + @Expose override val complete: Boolean) : MsgBlock { constructor(source: Parcel, type: MsgBlock.Type) : this( type, @@ -35,34 +39,14 @@ data class BaseMsgBlock(@Expose override val type: MsgBlock.Type = MsgBlock.Type } companion object { - const val TAG_TYPE = "type" - - val handledMsgBlockTypes = listOf( - MsgBlock.Type.PUBLIC_KEY, - MsgBlock.Type.DECRYPT_ERROR, - MsgBlock.Type.DECRYPTED_ATT - ) - @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(source: Parcel): MsgBlock { val partType = source.readParcelable(MsgBlock.Type::class.java.classLoader)!! - return genMsgBlockFromType(source, partType) + return MsgBlockFactory.fromParcel(partType, source) } override fun newArray(size: Int): Array = arrayOfNulls(size) } - - fun genMsgBlockFromType(source: Parcel, type: MsgBlock.Type): MsgBlock { - return when (type) { - MsgBlock.Type.PUBLIC_KEY -> PublicKeyMsgBlock(source) - - MsgBlock.Type.DECRYPT_ERROR -> DecryptErrorMsgBlock(source) - - MsgBlock.Type.DECRYPTED_ATT -> DecryptedAttMsgBlock(source) - - else -> BaseMsgBlock(source, type) - } - } } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlock.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlock.kt index 02964fd525..11decab35e 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlock.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlock.kt @@ -63,7 +63,13 @@ interface MsgBlock : Parcelable { DECRYPTED_HTML, @SerializedName("decryptErr") - DECRYPT_ERROR; + DECRYPT_ERROR, + + @SerializedName("certificate") + CERTIFICATE, + + @SerializedName("signature") + SIGNATURE; override fun describeContents(): Int { return 0 @@ -81,4 +87,8 @@ interface MsgBlock : Parcelable { } } } + + companion object { + const val TAG_TYPE = "type" + } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlockFactory.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlockFactory.kt new file mode 100644 index 0000000000..47bf473251 --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/node/MsgBlockFactory.kt @@ -0,0 +1,47 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: Ivan Pizhenko + */ + +package com.flowcrypt.email.api.retrofit.response.model.node + +import android.os.Parcel +import java.lang.IllegalArgumentException + +object MsgBlockFactory { + @JvmStatic + val supportedMsgBlockTypes = listOf( + MsgBlock.Type.PUBLIC_KEY, + MsgBlock.Type.DECRYPT_ERROR, + MsgBlock.Type.DECRYPTED_ATT + ) + + @JvmStatic + fun fromParcel(type: MsgBlock.Type, source: Parcel): MsgBlock { + return when (type) { + MsgBlock.Type.PUBLIC_KEY -> PublicKeyMsgBlock(source) + MsgBlock.Type.DECRYPT_ERROR -> DecryptErrorMsgBlock(source) + MsgBlock.Type.DECRYPTED_ATT -> DecryptedAttMsgBlock(source) + else -> GenericMsgBlock(source, type) + } + } + + @JvmStatic + fun fromContent(type: MsgBlock.Type, content: String, missingEnd: Boolean = false): MsgBlock { + val complete = !missingEnd + return when (type) { + MsgBlock.Type.PUBLIC_KEY -> PublicKeyMsgBlock(content, complete, null) + MsgBlock.Type.DECRYPT_ERROR -> DecryptErrorMsgBlock(content, complete, null) + else -> GenericMsgBlock(type, content, complete) + } + } + + @JvmStatic + fun fromAttachment(type: MsgBlock.Type, content: String, attMeta: AttMeta): MsgBlock { + return when (type) { + MsgBlock.Type.DECRYPTED_ATT -> DecryptedAttMsgBlock(content, true, attMeta, null) + else -> + throw IllegalArgumentException("Can't create block of type ${type.name} from attachment") + } + } +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/node/ParseDecryptedMsgResult.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/node/ParseDecryptedMsgResult.kt index 552366854f..08084c36c4 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/node/ParseDecryptedMsgResult.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/node/ParseDecryptedMsgResult.kt @@ -11,7 +11,7 @@ import android.os.Parcelable import android.util.Base64 import com.flowcrypt.email.api.retrofit.node.gson.NodeGson import com.flowcrypt.email.api.retrofit.response.base.ApiError -import com.flowcrypt.email.api.retrofit.response.model.node.BaseMsgBlock +import com.flowcrypt.email.api.retrofit.response.model.node.GenericMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.DecryptedAttMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock import com.flowcrypt.email.model.MessageEncryptionType @@ -123,7 +123,7 @@ data class ParseDecryptedMsgResult constructor( source.readString()!!, source.readString(), source.readParcelable(ApiError::class.java.classLoader), - mutableListOf().apply { source.readTypedList(this, BaseMsgBlock.CREATOR) } + mutableListOf().apply { source.readTypedList(this, GenericMsgBlock.CREATOR) } ) override fun describeContents() = 0 diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/core/msg/MsgBlockParser.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/core/msg/MsgBlockParser.kt new file mode 100644 index 0000000000..37e9bd0e4e --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/core/msg/MsgBlockParser.kt @@ -0,0 +1,128 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: ivan + */ + +package com.flowcrypt.email.core.msg + +import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock +import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlockFactory +import com.flowcrypt.email.security.pgp.PgpArmor + +@Suppress("unused") +object MsgBlockParser { + + private const val ARMOR_HEADER_MAX_LENGTH = 50 + + @JvmStatic + fun detectBlocks(text: String): List { + val normalized = normalize(text) + val blocks = mutableListOf() + var startAt = 0 + while (true) { + val continueAt = detectBlockNext(normalized, startAt, blocks) + if (startAt >= continueAt) return blocks + startAt = continueAt + } + } + + @JvmStatic + private fun detectBlockNext(text: String, startAt: Int, blocks: MutableList): Int { + val initialBlockCount = blocks.size + var continueAt = -1 + val beginIndex = text.indexOf( + PgpArmor.ARMOR_HEADER_DICT[MsgBlock.Type.UNKNOWN]!!.begin, startAt) + if (beginIndex != -1) { // found + val potentialBeginHeader = text.substring(beginIndex, beginIndex + ARMOR_HEADER_MAX_LENGTH) + for (blockHeaderKvp in PgpArmor.ARMOR_HEADER_DICT) { + val blockHeaderDef = blockHeaderKvp.value + if (!blockHeaderDef.replace || potentialBeginHeader.indexOf(blockHeaderDef.begin) != 0) { + continue + } + + if (beginIndex > startAt) { + // only replace blocks if they begin on their own line + // contains deliberate block: `-----BEGIN PGP PUBLIC KEY BLOCK-----\n...` + // contains deliberate block: `Hello\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n...` + // just plaintext (accidental block): `Hello -----BEGIN PGP PUBLIC KEY BLOCK-----\n...` + // block treated as plaintext, not on dedicated line - considered accidental + // this will actually cause potential deliberate blocks + // that follow accidental block to be ignored + // but if the message already contains accidental + // (not on dedicated line) blocks, it's probably a good thing to ignore the rest + var textBeforeBlock = text.substring(startAt, beginIndex) + if (!textBeforeBlock.endsWith('\n')) continue + textBeforeBlock = textBeforeBlock.trim() + if (textBeforeBlock.isNotEmpty()) { + blocks.add(MsgBlockFactory.fromContent(MsgBlock.Type.PLAIN_TEXT, textBeforeBlock)) + } + } + + val endHeaderIndex: Int + val endHeaderLength: Int + if (blockHeaderDef.endRegexp != null) { + val found = blockHeaderDef.endRegexp.find(text.substring(beginIndex)) + if (found != null) { + endHeaderIndex = beginIndex + found.range.first + endHeaderLength = found.range.last - found.range.first + } else { + endHeaderIndex = -1 + endHeaderLength = 0 + } + } else { // string + val end = blockHeaderDef.end!! + endHeaderIndex = text.indexOf(end, beginIndex + blockHeaderDef.begin.length) + endHeaderLength = if (endHeaderIndex == -1) 0 else end.length + } + + if (endHeaderIndex != -1) { + // identified end of the same block + continueAt = endHeaderIndex + endHeaderLength + blocks.add( + MsgBlockFactory.fromContent( + blockHeaderKvp.key, + text.substring(beginIndex, continueAt).trim() + ) + ) + } else { + // corresponding end not found + blocks.add( + MsgBlockFactory.fromContent(blockHeaderKvp.key, text.substring(beginIndex), true) + ) + } + break + } + } + + if (text.isNotEmpty() && blocks.size == initialBlockCount) { + // didn't find any blocks, but input is non-empty + val remainingText = text.substring(startAt).trim() + if (remainingText.isNotEmpty()) { + blocks.add(MsgBlockFactory.fromContent(MsgBlock.Type.PLAIN_TEXT, remainingText)) + } + } + + return continueAt + } + + @JvmStatic + fun normalize(str: String): String { + return normalizeSpaces(normalizeDashes(str)) + } + + @JvmStatic + private fun normalizeSpaces(str: String): String { + return str.replace(char160, ' ') + } + + @JvmStatic + private fun normalizeDashes(str: String): String { + return str.replace(dashesRegex, "-----") + } + + @JvmStatic + private val char160 = 160.toChar() + + @JvmStatic + private val dashesRegex = Regex("^—–|—–$") +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/CryptoArmorHeaderDefinition.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/CryptoArmorHeaderDefinition.kt new file mode 100644 index 0000000000..7c4f8d17d2 --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/CryptoArmorHeaderDefinition.kt @@ -0,0 +1,14 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: Ivan Pizhenko + */ + +package com.flowcrypt.email.security.pgp + +data class CryptoArmorHeaderDefinition ( + val begin: String, + val middle: String? = null, + val end: String?, + val endRegexp: Regex? = null, + val replace: Boolean +) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpArmor.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpArmor.kt new file mode 100644 index 0000000000..056114e1c2 --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/security/pgp/PgpArmor.kt @@ -0,0 +1,50 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors = Ivan Pizhenko + */ + +package com.flowcrypt.email.security.pgp + +import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock + +object PgpArmor { + @JvmStatic + val ARMOR_HEADER_DICT: Map = mapOf( + MsgBlock.Type.UNKNOWN to CryptoArmorHeaderDefinition( + begin = "-----BEGIN", + end = "-----END", + replace = false + ), + MsgBlock.Type.PUBLIC_KEY to CryptoArmorHeaderDefinition( + begin = "-----BEGIN PGP PUBLIC KEY BLOCK-----", + end = "-----END PGP PUBLIC KEY BLOCK-----", + replace = true + ), + MsgBlock.Type.PRIVATE_KEY to CryptoArmorHeaderDefinition( + begin = "-----BEGIN PGP PRIVATE KEY BLOCK-----", + end = "-----END PGP PRIVATE KEY BLOCK-----", + replace = true + ), + MsgBlock.Type.CERTIFICATE to CryptoArmorHeaderDefinition( + begin = "-----BEGIN CERTIFICATE-----", + end = "-----END CERTIFICATE-----", + replace = true + ), + MsgBlock.Type.SIGNED_MSG to CryptoArmorHeaderDefinition( + begin = "-----BEGIN PGP SIGNED MESSAGE-----", + middle = "-----BEGIN PGP SIGNATURE-----", + end = "-----END PGP SIGNATURE-----", + replace = true + ), + MsgBlock.Type.SIGNATURE to CryptoArmorHeaderDefinition( + begin = "-----BEGIN PGP SIGNATURE-----", + end = "-----END PGP SIGNATURE-----", + replace = false + ), + MsgBlock.Type.ENCRYPTED_MSG to CryptoArmorHeaderDefinition( + begin = "-----BEGIN PGP MESSAGE-----", + end = "-----END PGP MESSAGE-----", + replace = true + ) + ) +} diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt index 2cc2e3b357..ce27474109 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/ParcelableTest.kt @@ -9,7 +9,7 @@ import android.os.Build import android.os.Parcel import android.os.Parcelable import com.flextrade.jfixture.JFixture -import com.flowcrypt.email.api.retrofit.response.model.node.BaseMsgBlock +import com.flowcrypt.email.api.retrofit.response.model.node.GenericMsgBlock import com.flowcrypt.email.api.retrofit.response.model.node.MsgBlock import com.flowcrypt.email.jfixture.MsgBlockGenerationCustomization import com.flowcrypt.email.jfixture.SelectConstructorCustomisation @@ -39,7 +39,7 @@ class ParcelableTest(val name: String, private val currentClass: Class { - val msgBlockTypes = BaseMsgBlock.handledMsgBlockTypes val classForSpecimen: Class<*> = - when (msgBlockTypes.shuffled().first()) { + when (MsgBlockFactory.supportedMsgBlockTypes.shuffled().first()) { MsgBlock.Type.PUBLIC_KEY -> PublicKeyMsgBlock::class.java - MsgBlock.Type.DECRYPT_ERROR -> DecryptErrorMsgBlock::class.java - MsgBlock.Type.DECRYPTED_ATT -> DecryptedAttMsgBlock::class.java - - else -> BaseMsgBlock::class.java + else -> GenericMsgBlock::class.java } - context.resolve(classForSpecimen) } } } -} \ No newline at end of file +} diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpPwdTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpPwdTest.kt index 0a0f0daa8c..41fb080e47 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpPwdTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/security/pgp/PgpPwdTest.kt @@ -11,50 +11,50 @@ import org.junit.Test import java.math.BigInteger class PgpPwdTest { - @Test - fun testEstimateStrength() { - val actualResult = PgpPwd.estimateStrength( - BigInteger("88946283684264"), PgpPwd.PwdType.PASSPHRASE) - val expectedResult = PgpPwd.PwdStrengthResult( - word = PgpPwd.Word( - match = "week", - word = "poor", - bar = 30, - color = "darkred", - pass = false - ), - seconds = BigInteger.valueOf(1111829), - time = "2 weeks" - ) - assertEquals(expectedResult, actualResult) + @Test + fun testEstimateStrength() { + val actualResult = PgpPwd.estimateStrength( + BigInteger("88946283684264"), PgpPwd.PwdType.PASSPHRASE) + val expectedResult = PgpPwd.PwdStrengthResult( + word = PgpPwd.Word( + match = "week", + word = "poor", + bar = 30, + color = "darkred", + pass = false + ), + seconds = BigInteger.valueOf(1111829), + time = "2 weeks" + ) + assertEquals(expectedResult, actualResult) + } + + @Test + fun testBytesToPassword() { + val bytes = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15) + assertEquals("1234-5678-90AB-CDEF", PgpPwd.bytesToPassword(bytes)) + } + + @Test + fun testBytesToPasswordRejectsTooShortByteArray() { + val bytes = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + + // I'd better use assertThrows(), but the strange thing happens: + // in the IntelliJ it resolves fine, but during compilation it says + // something like "Unresolved symbol assertThrows" + try { + PgpPwd.bytesToPassword(bytes) + throw Exception("IllegalArgumentException not thrown") + } catch (ex: IllegalArgumentException) { + // this is expected } + } - @Test - fun testBytesToPassword() { - val bytes = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15) - assertEquals("1234-5678-90AB-CDEF", PgpPwd.bytesToPassword(bytes)) - } - - @Test - fun testBytesToPasswordRejectsTooShortByteArray() { - val bytes = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) - - // I'd better use assertThrows(), but the strange thing happens: - // in the IntelliJ it resolves fine, but during compilation it says - // something like "Unresolved symbol assertThrows" - try { - PgpPwd.bytesToPassword(bytes) - throw Exception("IllegalArgumentException not thrown") - } catch (ex: IllegalArgumentException) { - // this is expected - } - } + private val passwordRegex = Regex("[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}") - private val passwordRegex = Regex("[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}") - - @Test - fun testRandom() { - val password = PgpPwd.random() - assertTrue("Password structure mismatch", passwordRegex.matches(password)) - } + @Test + fun testRandom() { + val password = PgpPwd.random() + assertTrue("Password structure mismatch", passwordRegex.matches(password)) + } }