diff --git a/CHANGELOG.md b/CHANGELOG.md index b03c87e77b..73e15253e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # TelegramBotAPI changelog +## 0.38.16 + +* `Core`: + * `TelegramAPIUrlsKeeper` now have two new things: properties `webAppDataSecretKey` and fun `checkWebAppLink` + ## 0.38.15 * `Common`: diff --git a/gradle.properties b/gradle.properties index b7a490f218..b09681206c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,6 +20,6 @@ javax_activation_version=1.1.1 dokka_version=1.6.10 library_group=dev.inmo -library_version=0.38.15 +library_version=0.38.16 github_release_plugin_version=2.3.7 diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Crypto.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Crypto.kt new file mode 100644 index 0000000000..af150b34fa --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/Crypto.kt @@ -0,0 +1,19 @@ +package dev.inmo.tgbotapi.utils + +import dev.inmo.micro_utils.crypto.SourceBytes +import dev.inmo.micro_utils.crypto.SourceString + +internal expect fun SourceString.hmacSha256(key: String): String +private val HEX_ARRAY = "0123456789abcdef".toCharArray() + +internal fun SourceBytes.hex(): String { + val hexChars = CharArray(size * 2) + for (j in indices) { + val v: Int = this[j].toInt() and 0xFF + hexChars[j * 2] = HEX_ARRAY[v ushr 4] + hexChars[j * 2 + 1] = HEX_ARRAY[v and 0x0F] + } + return hexChars.concatToString() +} + +internal fun SourceString.hex(): String = encodeToByteArray().hex() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt index 422634479e..649a8ccc2b 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt @@ -18,6 +18,10 @@ class TelegramAPIUrlsKeeper( token: String, hostUrl: String = telegramBotAPIDefaultUrl ) { + val webAppDataSecretKey by lazy { + token.hmacSha256("WebAppData") + } + val commonAPIUrl: String val fileBaseUrl: String @@ -28,4 +32,10 @@ class TelegramAPIUrlsKeeper( } fun createFileLinkUrl(filePath: String) = "${fileBaseUrl}/$filePath" + + /** + * @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData] + * @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash] + */ + fun checkWebAppLink(rawData: String, hash: String) = rawData.hmacSha256(webAppDataSecretKey).hex() == hash } diff --git a/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/utils/CryptoActual.kt b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/utils/CryptoActual.kt new file mode 100644 index 0000000000..a8b23eae8a --- /dev/null +++ b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/utils/CryptoActual.kt @@ -0,0 +1,6 @@ +package dev.inmo.tgbotapi.utils + +import dev.inmo.micro_utils.crypto.CryptoJS +import dev.inmo.micro_utils.crypto.SourceString + +actual fun SourceString.hmacSha256(key: String) = CryptoJS.asDynamic().HmacSHA256(this, key).toString().unsafeCast() diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/CryptoActual.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/CryptoActual.kt new file mode 100644 index 0000000000..65075f0119 --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/CryptoActual.kt @@ -0,0 +1,14 @@ +package dev.inmo.tgbotapi.utils + +import dev.inmo.micro_utils.crypto.SourceString +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +actual fun SourceString.hmacSha256(key: String): String { + val mac = Mac.getInstance("HmacSHA256") + + val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256") + mac.init(secretKey) + + return mac.doFinal(toByteArray()).hex() +} diff --git a/tgbotapi.webapps/build.gradle b/tgbotapi.webapps/build.gradle index 257fcf8f02..951c331574 100644 --- a/tgbotapi.webapps/build.gradle +++ b/tgbotapi.webapps/build.gradle @@ -27,6 +27,13 @@ repositories { } kotlin { + jvm { + compilations.main { + kotlinOptions { + jvmTarget = "1.8" + } + } + } js(IR) { browser() nodejs() diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt index 5422c340c2..dd1d788940 100644 --- a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt +++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.webapps import dev.inmo.micro_utils.crypto.CryptoJS +import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper external class WebApp { val initData: String @@ -76,6 +77,7 @@ fun WebApp.onMainButtonClicked(eventHandler: EventHandler) = onEvent(EventType.M */ fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler) -fun WebApp.isInitDataSafe(botToken: String) = CryptoJS.hex( - CryptoJS.HmacSHA256(botToken, "WebAppData") -) == initDataUnsafe.hash +fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppLink( + initData, + initDataUnsafe.hash +)