diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DataCredentials.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DataCredentials.kt deleted file mode 100644 index 4db61a1f7e..0000000000 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DataCredentials.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.inmo.tgbotapi.types.passport.credentials - -import dev.inmo.tgbotapi.types.dataHashField -import dev.inmo.tgbotapi.types.secretField -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class DataCredentials( - @SerialName(dataHashField) - val dataHash: String, - @SerialName(secretField) - val secret: String -) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DecryptedCredentials.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DecryptedCredentials.kt index b732b38281..c33a6bcdbf 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DecryptedCredentials.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/DecryptedCredentials.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable @Serializable data class DecryptedCredentials( @SerialName(secureDataField) - val secureData: List, + val secureData: SecureData, @SerialName(nonceField) val nonce: String ) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/EndDataCredentials.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/EndDataCredentials.kt new file mode 100644 index 0000000000..7bcc7838b2 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/EndDataCredentials.kt @@ -0,0 +1,35 @@ +package dev.inmo.tgbotapi.types.passport.credentials + +import dev.inmo.micro_utils.crypto.SourceBytes +import dev.inmo.micro_utils.serialization.base64.Base64BytesToFromStringSerializer +import dev.inmo.tgbotapi.types.* +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +sealed class EndDataCredentials { + @Serializable(Base64BytesToFromStringSerializer::class) + abstract val hash: SourceBytes + @Serializable(Base64BytesToFromStringSerializer::class) + abstract val secret: SourceBytes +} + +@Serializable +data class DataCredentials( + @SerialName(dataHashField) + @Serializable(Base64BytesToFromStringSerializer::class) + override val hash: SourceBytes, + @SerialName(secretField) + @Serializable(Base64BytesToFromStringSerializer::class) + override val secret: SourceBytes +) : EndDataCredentials() + +@Serializable +data class FileCredentials( + @SerialName(fileHashField) + @Serializable(Base64BytesToFromStringSerializer::class) + override val hash: SourceBytes, + @SerialName(secretField) + @Serializable(Base64BytesToFromStringSerializer::class) + override val secret: SourceBytes +) : EndDataCredentials() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/FileCredentials.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/FileCredentials.kt deleted file mode 100644 index f73784af17..0000000000 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/credentials/FileCredentials.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.inmo.tgbotapi.types.passport.credentials - -import dev.inmo.tgbotapi.types.* -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class FileCredentials( - @SerialName(fileHashField) - val fileHash: String, - @SerialName(secretField) - val secret: String -) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/AddressSecureValue.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/AddressSecureValue.kt index b47fe28fdc..d403e11db3 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/AddressSecureValue.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/AddressSecureValue.kt @@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.types.passport.decrypted import dev.inmo.tgbotapi.types.dataField import dev.inmo.tgbotapi.types.passport.credentials.DataCredentials +import dev.inmo.tgbotapi.types.passport.credentials.EndDataCredentials import dev.inmo.tgbotapi.types.passport.decrypted.abstracts.SecureValueWithData import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,4 +11,6 @@ import kotlinx.serialization.Serializable data class AddressSecureValue( @SerialName(dataField) override val data: DataCredentials -) : SecureValueWithData \ No newline at end of file +) : SecureValueWithData { + override val credentials: List = listOf(data) +} \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/IdentityWithReverseSideSecureValue.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/IdentityWithReverseSideSecureValue.kt index ff65ae0c5d..c05a7a7692 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/IdentityWithReverseSideSecureValue.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/IdentityWithReverseSideSecureValue.kt @@ -1,39 +1,41 @@ package dev.inmo.tgbotapi.types.passport.decrypted import dev.inmo.tgbotapi.types.* -import dev.inmo.tgbotapi.types.passport.credentials.DataCredentials -import dev.inmo.tgbotapi.types.passport.credentials.FileCredentials +import dev.inmo.tgbotapi.types.passport.credentials.* import dev.inmo.tgbotapi.types.passport.decrypted.abstracts.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -sealed class IdentityWithReverseSideSecureValue : SecureValueIdentity, SecureValueWithData, SecureValueWithTranslations, SecureValueWithReverseSide +sealed class IdentityWithReverseSideSecureValue : SecureValueIdentity, SecureValueWithData, SecureValueWithTranslations, SecureValueWithReverseSide { + override val credentials: List + get() = listOfNotNull(data, frontSide, reverseSide, selfie) + translation +} @Serializable data class DriverLicenseSecureValue( @SerialName(dataField) - override val data: DataCredentials, + override val data: DataCredentials? = null, @SerialName(frontSideField) - override val frontSide: FileCredentials, + override val frontSide: FileCredentials? = null, @SerialName(reverseSideField) - override val reverseSide: FileCredentials, + override val reverseSide: FileCredentials? = null, @SerialName(selfieField) - override val selfie: FileCredentials, + override val selfie: FileCredentials? = null, @SerialName(translationField) - override val translation: List + override val translation: List = emptyList() ) : IdentityWithReverseSideSecureValue() @Serializable data class IdentityCardSecureValue( @SerialName(dataField) - override val data: DataCredentials, + override val data: DataCredentials? = null, @SerialName(frontSideField) - override val frontSide: FileCredentials, + override val frontSide: FileCredentials? = null, @SerialName(reverseSideField) - override val reverseSide: FileCredentials, + override val reverseSide: FileCredentials? = null, @SerialName(selfieField) - override val selfie: FileCredentials, + override val selfie: FileCredentials? = null, @SerialName(translationField) - override val translation: List + override val translation: List = emptyList() ) : IdentityWithReverseSideSecureValue() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/OtherDocumentsSecureValue.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/OtherDocumentsSecureValue.kt index 8c304ed8af..778a773cbf 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/OtherDocumentsSecureValue.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/OtherDocumentsSecureValue.kt @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.types.passport.decrypted import dev.inmo.tgbotapi.types.filesField +import dev.inmo.tgbotapi.types.passport.credentials.EndDataCredentials import dev.inmo.tgbotapi.types.passport.credentials.FileCredentials import dev.inmo.tgbotapi.types.passport.decrypted.abstracts.SecureValueWithFiles import dev.inmo.tgbotapi.types.passport.decrypted.abstracts.SecureValueWithTranslations @@ -9,44 +10,47 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -sealed class OtherDocumentsSecureValue : SecureValueWithTranslations, SecureValueWithFiles +sealed class OtherDocumentsSecureValue : SecureValueWithTranslations, SecureValueWithFiles { + override val credentials: List + get() = translation + files +} @Serializable data class UtilityBillSecureValue( @SerialName(translationField) - override val translation: List, + override val translation: List = emptyList(), @SerialName(filesField) - override val files: List + override val files: List = emptyList() ) : OtherDocumentsSecureValue() @Serializable data class BankStatementSecureValue( @SerialName(translationField) - override val translation: List, + override val translation: List = emptyList(), @SerialName(filesField) - override val files: List + override val files: List = emptyList() ) : OtherDocumentsSecureValue() @Serializable data class RentalAgreementSecureValue( @SerialName(translationField) - override val translation: List, + override val translation: List = emptyList(), @SerialName(filesField) - override val files: List + override val files: List = emptyList() ) : OtherDocumentsSecureValue() @Serializable data class PassportRegistrationSecureValue( @SerialName(translationField) - override val translation: List, + override val translation: List = emptyList(), @SerialName(filesField) - override val files: List + override val files: List = emptyList() ) : OtherDocumentsSecureValue() @Serializable data class TemporalRegistrationSecureValue( @SerialName(translationField) - override val translation: List, + override val translation: List = emptyList(), @SerialName(filesField) - override val files: List + override val files: List = emptyList() ) : OtherDocumentsSecureValue() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PassportSecureValue.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PassportSecureValue.kt index 9037c02f6b..effe727275 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PassportSecureValue.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PassportSecureValue.kt @@ -1,36 +1,38 @@ package dev.inmo.tgbotapi.types.passport.decrypted import dev.inmo.tgbotapi.types.* -import dev.inmo.tgbotapi.types.passport.credentials.DataCredentials -import dev.inmo.tgbotapi.types.passport.credentials.FileCredentials +import dev.inmo.tgbotapi.types.passport.credentials.* import dev.inmo.tgbotapi.types.passport.decrypted.abstracts.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -sealed class PassportSecureValue : SecureValueIdentity, SecureValueWithData, SecureValueWithTranslations +sealed class PassportSecureValue : SecureValueIdentity, SecureValueWithData, SecureValueWithTranslations { + override val credentials: List + get() = listOfNotNull(data, frontSide, selfie) + translation +} @Serializable data class CommonPassportSecureValue( @SerialName(dataField) - override val data: DataCredentials, + override val data: DataCredentials? = null, @SerialName(frontSideField) - override val frontSide: FileCredentials, + override val frontSide: FileCredentials? = null, @SerialName(selfieField) - override val selfie: FileCredentials, + override val selfie: FileCredentials? = null, @SerialName(translationField) - override val translation: List + override val translation: List = emptyList() ) : PassportSecureValue() @Serializable data class InternalPassportSecureValue( @SerialName(dataField) - override val data: DataCredentials, + override val data: DataCredentials? = null, @SerialName(frontSideField) - override val frontSide: FileCredentials, + override val frontSide: FileCredentials? = null, @SerialName(selfieField) - override val selfie: FileCredentials, + override val selfie: FileCredentials? = null, @SerialName(translationField) - override val translation: List + override val translation: List = emptyList() ) : PassportSecureValue() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PersonalDetailsSecureValue.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PersonalDetailsSecureValue.kt index 185c710fc9..120b2bf77a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PersonalDetailsSecureValue.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/PersonalDetailsSecureValue.kt @@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.types.passport.decrypted import dev.inmo.tgbotapi.types.dataField import dev.inmo.tgbotapi.types.passport.credentials.DataCredentials +import dev.inmo.tgbotapi.types.passport.credentials.EndDataCredentials import dev.inmo.tgbotapi.types.passport.decrypted.abstracts.SecureValueWithData import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,4 +11,6 @@ import kotlinx.serialization.Serializable data class PersonalDetailsSecureValue( @SerialName(dataField) override val data: DataCredentials -) : SecureValueWithData \ No newline at end of file +) : SecureValueWithData { + override val credentials: List = listOf(data) +} \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/SecureData.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/SecureData.kt index dd2fb40eed..c2b9092603 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/SecureData.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/SecureData.kt @@ -9,7 +9,7 @@ data class SecureData( @SerialName(personalDetailsField) val personalDetails: PersonalDetailsSecureValue? = null, @SerialName(passportField) - val passport: PassportSecureValue? = null, + val passport: CommonPassportSecureValue? = null, @SerialName(internalPassportField) val internalPassport: InternalPassportSecureValue? = null, @SerialName(driverLicenseField) @@ -26,4 +26,17 @@ data class SecureData( val passportRegistration: PassportRegistrationSecureValue? = null, @SerialName(temporaryRegistrationField) val temporaryRegistration: TemporalRegistrationSecureValue? = null, -) +) { + val allCredentials by lazy { + (personalDetails ?.credentials ?: emptyList()) + + (passport ?.credentials ?: emptyList()) + + (internalPassport ?.credentials ?: emptyList()) + + (driverLicense ?.credentials ?: emptyList()) + + (identityCard ?.credentials ?: emptyList()) + + (utilityBill ?.credentials ?: emptyList()) + + (bankStatement ?.credentials ?: emptyList()) + + (rentalAgreement ?.credentials ?: emptyList()) + + (passportRegistration ?.credentials ?: emptyList()) + + (temporaryRegistration ?.credentials ?: emptyList()) + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue.kt index 9db0d7c823..b9bb54371c 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValue.kt @@ -1,3 +1,7 @@ package dev.inmo.tgbotapi.types.passport.decrypted.abstracts -interface SecureValue +import dev.inmo.tgbotapi.types.passport.credentials.EndDataCredentials + +interface SecureValue { + val credentials: List +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueIdentity.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueIdentity.kt index 1c4847a181..e1de83733a 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueIdentity.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueIdentity.kt @@ -3,6 +3,6 @@ package dev.inmo.tgbotapi.types.passport.decrypted.abstracts import dev.inmo.tgbotapi.types.passport.credentials.FileCredentials interface SecureValueIdentity : SecureValue { - val frontSide: FileCredentials - val selfie: FileCredentials + val frontSide: FileCredentials? + val selfie: FileCredentials? } \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithData.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithData.kt index c3eb5a8b35..9e984cd3b0 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithData.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithData.kt @@ -3,5 +3,5 @@ package dev.inmo.tgbotapi.types.passport.decrypted.abstracts import dev.inmo.tgbotapi.types.passport.credentials.DataCredentials interface SecureValueWithData : SecureValue { - val data: DataCredentials + val data: DataCredentials? } \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithReverseSide.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithReverseSide.kt index b833b9284d..9706c5d041 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithReverseSide.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/passport/decrypted/abstracts/SecureValueWithReverseSide.kt @@ -3,5 +3,5 @@ package dev.inmo.tgbotapi.types.passport.decrypted.abstracts import dev.inmo.tgbotapi.types.passport.credentials.FileCredentials interface SecureValueWithReverseSide : SecureValue { - val reverseSide: FileCredentials + val reverseSide: FileCredentials? } \ No newline at end of file diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/passport/DecryptionContext.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/passport/DecryptionContext.kt index cf0e4e7624..f179e3f627 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/passport/DecryptionContext.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/passport/DecryptionContext.kt @@ -1,32 +1,15 @@ package dev.inmo.tgbotapi.utils.passport +import dev.inmo.micro_utils.crypto.SourceBytes import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.requests.DownloadFile import dev.inmo.tgbotapi.requests.get.GetFile import dev.inmo.tgbotapi.types.passport.credentials.EncryptedCredentials +import dev.inmo.tgbotapi.types.passport.credentials.EncryptedData import dev.inmo.tgbotapi.types.passport.encrypted.PassportFile import dev.inmo.tgbotapi.utils.nonstrictJsonFormat import kotlinx.serialization.json.JsonObject interface Decryptor { - fun ByteArray.decrypt(): ByteArray + fun decrypt(data: EncryptedData): SourceBytes } - -suspend fun Decryptor.decrypt( - file: PassportFile, - bot: TelegramBot -): ByteArray { - return bot.execute( - DownloadFile( - bot.execute( - GetFile(file.fileId) - ).filePath - ) - ).decrypt() -} -fun Decryptor.decryptData( - data: EncryptedCredentials -) = nonstrictJsonFormat.decodeFromString( - JsonObject.serializer(), - data.data.decrypt().decodeToString() -) diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/AESDecryptor.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/AESDecryptor.kt new file mode 100644 index 0000000000..99b5185bd0 --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/AESDecryptor.kt @@ -0,0 +1,21 @@ +package dev.inmo.tgbotapi.utils.passport + +import dev.inmo.micro_utils.crypto.SourceBytes +import dev.inmo.tgbotapi.types.passport.credentials.EncryptedData +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +class AESDecryptor(key: SourceBytes, private val iv: ByteArray) : Decryptor { + private val key = SecretKeySpec(key, "AES"); + + override fun decrypt(data: EncryptedData): SourceBytes { + return Cipher.getInstance("AES/CBC/NOPADDING").run { + init(Cipher.DECRYPT_MODE, key, IvParameterSpec(this@AESDecryptor.iv)) + val decryptedCredentials = doFinal(data) + + val padding = decryptedCredentials.first() + decryptedCredentials.copyOfRange(padding.toInt(), decryptedCredentials.size) + } + } +} diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/CredentialsDecrypting.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/CredentialsDecrypting.kt new file mode 100644 index 0000000000..7639ee5845 --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/CredentialsDecrypting.kt @@ -0,0 +1,29 @@ +package dev.inmo.tgbotapi.utils.passport + +import dev.inmo.micro_utils.crypto.decodeBase64 +import dev.inmo.tgbotapi.types.passport.credentials.DecryptedCredentials +import dev.inmo.tgbotapi.types.passport.credentials.EncryptedCredentials +import dev.inmo.tgbotapi.utils.nonstrictJsonFormat +import java.security.* +import java.security.spec.PKCS8EncodedKeySpec +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +private val regexToRemoveFromKey = Regex("(-----(BEGIN|END) ((?:.*? KEY)|CERTIFICATE)-----|[\\s])") + +fun EncryptedCredentials.decryptWithPKCS8PrivateKey(privateKey: PrivateKey): DecryptedCredentials { + val decrypted = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding").run { + init(Cipher.DECRYPT_MODE, privateKey) + doFinal(secret) + } + val dataDecryptor = (decrypted to hash).createDecryptor() + val decryptedCredentials = dataDecryptor.decrypt(data).decodeToString() + return nonstrictJsonFormat.decodeFromString(DecryptedCredentials.serializer(), decryptedCredentials) +} + +fun EncryptedCredentials.decryptWithPKCS8PrivateKey(key: String) = decryptWithPKCS8PrivateKey( + KeyFactory.getInstance("RSA").generatePrivate( + PKCS8EncodedKeySpec(key.replace(regexToRemoveFromKey, "").decodeBase64()) + ) +) diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/CredentialsDecryptor.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/CredentialsDecryptor.kt deleted file mode 100644 index d2bffb492e..0000000000 --- a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/CredentialsDecryptor.kt +++ /dev/null @@ -1,2 +0,0 @@ -package dev.inmo.tgbotapi.utils.passport - diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/ElementDecryptingWithSecureData.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/ElementDecryptingWithSecureData.kt new file mode 100644 index 0000000000..e184139b0d --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/ElementDecryptingWithSecureData.kt @@ -0,0 +1,28 @@ +package dev.inmo.tgbotapi.utils.passport + +import dev.inmo.micro_utils.crypto.SourceBytes +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.DownloadFile +import dev.inmo.tgbotapi.requests.get.GetFile +import dev.inmo.tgbotapi.types.passport.credentials.* +import dev.inmo.tgbotapi.types.passport.encrypted.PassportFile + +fun EndDataCredentials.decryptData( + bytes: EncryptedData +): SourceBytes { + return createDecryptor().decrypt(bytes) +} + +fun FileCredentials.decryptFile( + fileBytes: ByteArray +): SourceBytes { + return createDecryptor().decrypt(fileBytes) +} +suspend fun FileCredentials.decryptFile( + bot: TelegramBot, + passportFile: PassportFile +): SourceBytes { + val pathedFile = bot.execute(GetFile(passportFile.fileId)) + val bytes = bot.execute(DownloadFile(pathedFile.filePath)) + return decryptFile(bytes) +} diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/EndDataDecryptor.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/EndDataDecryptor.kt new file mode 100644 index 0000000000..02a509a53e --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/EndDataDecryptor.kt @@ -0,0 +1,15 @@ +package dev.inmo.tgbotapi.utils.passport + +import dev.inmo.micro_utils.crypto.SourceBytes +import dev.inmo.tgbotapi.types.passport.credentials.EndDataCredentials +import java.security.MessageDigest + +fun Pair.createDecryptor(): Decryptor { + val secretHash = MessageDigest.getInstance("SHA-512").digest(first + second) + val key = secretHash.copyOf(32) + val iv = secretHash.copyOfRange(32, 48) + + return AESDecryptor(key, iv) +} + +fun EndDataCredentials.createDecryptor() = (secret to hash).createDecryptor() diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/PKCS8Decryptor.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/PKCS8Decryptor.kt deleted file mode 100644 index 05563313bb..0000000000 --- a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/PKCS8Decryptor.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.inmo.tgbotapi.utils.passport - -import dev.inmo.micro_utils.crypto.decodeBase64 -import java.security.KeyFactory -import java.security.interfaces.RSAPrivateKey -import java.security.spec.PKCS8EncodedKeySpec -import javax.crypto.Cipher - -private val regexToRemoveFromKey = Regex("(-----(BEGIN|END) ((?:.*? KEY)|CERTIFICATE)-----|[\\s])") - -/** - * @param key PKCS8 - */ -class PKCS8Decryptor (key: String): Decryptor { - private val privateKey: RSAPrivateKey = KeyFactory.getInstance("RSA").generatePrivate( - PKCS8EncodedKeySpec(key.replace(regexToRemoveFromKey, "").decodeBase64()) - ) as RSAPrivateKey - private val chunkSize: Int = privateKey.modulus.bitLength() / 8 - - override fun ByteArray.decrypt(): ByteArray { - return Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING").run { - init(Cipher.DECRYPT_MODE, privateKey) - (0 until size step chunkSize).flatMap { - val firstIndex = it - val lastIndexExclusive = if (it + chunkSize > size) { - size - } else { - it + chunkSize - } - doFinal(copyOfRange(firstIndex, lastIndexExclusive)).toList() - }.toByteArray() - } - } -} - -fun Decryptor(key: String) = PKCS8Decryptor(key) - -inline fun doWithDecryptor(decryptor: Decryptor, crossinline block: Decryptor.() -> T) = decryptor.run(block) -inline fun doWithDecryptor(key: String, crossinline block: Decryptor.() -> T) = doWithDecryptor(Decryptor(key), block) diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/PassportDataDecryptionHandling.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/PassportDataDecryptionHandling.kt new file mode 100644 index 0000000000..4743dd5ac1 --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/passport/PassportDataDecryptionHandling.kt @@ -0,0 +1,15 @@ +package dev.inmo.tgbotapi.utils.passport + +import dev.inmo.tgbotapi.types.passport.PassportData +import dev.inmo.tgbotapi.types.passport.decrypted.SecureData +import java.security.PrivateKey + +inline fun PassportData.doInDecryptionContextWithPKCS8Key( + pkcs8Key: PrivateKey, + expectedNonce: String? = null, + crossinline block: SecureData.() -> T +): T { + val decryptedCredentials = credentials.decryptWithPKCS8PrivateKey(pkcs8Key) + expectedNonce ?.let { require(expectedNonce == decryptedCredentials.nonce) } + return decryptedCredentials.secureData.run(block) +} diff --git a/tgbotapi.core/src/jvmTest/kotlin/dev/inmo/tgbotapi/passport/DecryptionTest.kt b/tgbotapi.core/src/jvmTest/kotlin/dev/inmo/tgbotapi/passport/DecryptionTest.kt deleted file mode 100644 index b96cde1744..0000000000 --- a/tgbotapi.core/src/jvmTest/kotlin/dev/inmo/tgbotapi/passport/DecryptionTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package dev.inmo.tgbotapi.passport - -import dev.inmo.micro_utils.crypto.decodeBase64 -import dev.inmo.tgbotapi.utils.passport.Decryptor -import dev.inmo.tgbotapi.utils.passport.doWithDecryptor -import kotlin.test.Test -import kotlin.test.assertEquals - -//expect val privateKey: String -//expect val encryptedLoremIpsum: EncryptedAndBase64EncodedData -val privateKey: String = """-----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCaaMFkmMBi6Vl5 -vzy7wQjoZNLN+e9e6Lb/WBekJxfbNMt+EN9Zv7xT9xzfY68DX7v0uGoLcAqS5Mh5 -8ELiJbzg4O8dbxkggt0G+kxcbT+a3ofm1Aqhr+LkpsFh5qbLqbAHSQJAF0HPqrAc -5c6MoQVTZlWGU/j3enCcUFo7Jpsi8La6MqYboF5xGwGEZ9tOA6rMDSekr5Rdm637 -D/w4h284UX7VnqPdpZ/943VK2qvmPjm8HkFSxkSAB8RHlFyjazzm9FNOcVRGLynv -/cq7mWgyxt5nqiCG7w+lFNAFNQNvAO0rfeiTJbEzubquu4Mg31QrvLIuz+fUbsGk -ipfpN1czAgMBAAECggEAPlAoO8CpY0FoqolSqTKttZt6t0U2JMclksaqQ8TDC+Oy -e52zhTSre/ct37kK2AG6iHgj05nTqpRJk2wykbFJGDeuR+Kd8VDeggJg7qvoD0fe -8HiCEd45Yq0pPaknhulj8Iy2K8c29+eaSw8y2+3fiFi0CxG4V6dB6tNClrxtvxtl -IE85Utzmk4PLTCs010zxNVm+FwrvqlpjjhspljzleUuWE1KuldO9tNSlwUUfBr4i -BJJn4jDkQMloXoM/ArZEp7mn57N9+ZuEgVjRMCpRGEAbQI+aLCznBB1kCOY7WL8S -CwkP++SvcptaUpOjoXZZyuGU4CgBnu0+40ORbNchgQKBgQDLIbB9+WHDcZAn1g6M -8jaNDRU4HGu5vJSajFQK8Uaczg87hAxmk6R1GF0LOyoadaVmD5ax93mQxWSE/4hT -wyAPaQOFl7A/i1k/Dx/kURjRwF1sLWDFdMo/8t5bXumzrzb1bHYnHPS7S0lBAU+X -SrbIw+hv69aKawcQqdd54S1OBwKBgQDCmMarwMVn4xnOh+DOuLS4IQzPT84WgzH+ -wKw55hZGMqZ4S3JRVv256daweEbKAeR5NCccPmir0H86jwUW6WFAtxqfPWF4piVi -UD6DrUt0gWwzMGy3kbAp0hLBtsQRp/41mKuAXkvMVQ2ysInn6lmHhZvjduK8lvKh -fMRDMjxidQKBgFiciK5blI86wgTutwg7PRrI40HH/CJZJoZIwvzHBeOvbCutTe+N -ZoeCKkyU8af7PDzKfhWCfHBv+4qdIi5QB3NRfyzO4B7IPhVpFqN10RrnDJn9LaLV -cMj2vJMlU1OEErh7KQuk8QmnLPyDguHfwN7Rv1rbiYp2Z+2X+Zx8Y1QPAoGBAJIf -nq/CJXoJMou/xLP2Rt4tEy1pQ9vr0FL341vmxrsXtaGHJeSmagh861XAO4fdO+83 -llbDFl5ORft3Ad9eiETMOhVxRgwO1uuoTgkazBpERTd7GWgO4jXFJYiI8VpAx8b/ -SWkvZcOd6pdPsX6Qn4IAdjqsPz5WKwPQaJ/8zRMxAoGAGaOuyXaFZNl8IeTGYMDL -OEg9BHa+Du8jSv0vL/fCN2s/2TcvSa9uEnv8KHz59JHevvxoDNbQYlN8Ge8adFv4 -xZYQ3h/qh2ohnTUfrG4hcFUVBuv5HBdwgWhVFm5v98KQsqVzGfKorN2DemEv3/au -PFGkuu4lCPeDqu5u0dBojoA= ------END PRIVATE KEY-----""" -val encryptedLoremIpsum = "F57wnFj3PTQ+xNgDFLCNRIHVf8Kp7YiaQdYOOEpuWMuICFBvVDrTst82jxhRuKj8w9qDZ0ys9pQ30QjnmCP0h/d8HhmLcG3sYY9fnDgd4hegY0D917Xhc0F5KwheFxNgHFn5uRuTdAoyBVUeAzclpR7XoEO8Zp+kYvNty7eOpLVC2jXBTiTBClXmxM15M48Pw7ClZr2lvjFf7r+4Bh8UAYM8IIKctV/Vk1iR7SIilHOw/GNhgBB1zy0SYlwSuvPEIPfZnEPL4zS9VqDN4Di53Zt7ZLhySBMDi1qA4V6OnjSoF6nexCrytMqIsLHrTbfin5ni20F+vIITFxa7kJU2+AVQFTsYrquL5zN9ms56kkLd3HMv/fdrz9Ry19vSaIjyajipoWPM6BvzNccYk2xnZx+5qzpZUQjsh+Xr+HD7GcpdGwavcz7WqGlx9qKBPwU0rq0+QCRpDz/hnl2gdChk+bjTAfuRWJDoUUetVV283BG8UY5SENH7EjOhtaMsEgog6VI2yyiei6azxZ+MTqkREnHWmjefsBHmbAbf2EEZag+nSjIZhNT62FkhWq5jBghjduoKixS5OUvWniY3wSx0RSXsJPVg9C80EyeO8wyOop7x0wN5zxKoXrniX6AGruEt74SJt9RZamniog2X5CJ27D94HvoF2qXSv9QeqK+qUj9XxGhn0AtqFnvg/yU/jUp6z4yGQ7CkIMHEMbq4ylRA87ydhu3HbqdrW3q8aRXtvo+VFQ8jmsOq+ssj2q7y2WegBMNCBMRjn197S/eBW4EweIqh9+3VsDRoabfUUuCDXBlA+qFOAYEB44TGiaXqneHjUwhDahKlui8556ILcUP4yNnFNA6wx/tgRiW40cEa6vs4CF4UPtzKrVgWwS8/hRd7OBaK/jU3KnMGKZej9AtV28ErDmBTcixMzpflPmUFQABwH7IhUzJMVlqDifZuVjthGZ3FWuPaFOcesQoScQ1q93pohz1vjwtyT5fU36JzyhYpEpmN6h3GiAFV6qT0VDiE" - - -class DecryptionTest { - val inputText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." - - @Test - fun testThatDecryptionIsWorkingCorrectly() { - doWithDecryptor(privateKey) { - val decrypted = encryptedLoremIpsum.decodeBase64().decrypt().decodeToString() - assertEquals(inputText, decrypted) - } - } -}