From c266561e0cd048a7e0c2f8617a35bc1a827df318 Mon Sep 17 00:00:00 2001 From: Linnea Gräf Date: Sat, 27 Apr 2024 15:05:18 +0200 Subject: Fix access transformer crash and invalid SSL context for auto updater (#1121) --- .github/workflows/publish.yml | 5 +- build.gradle.kts | 3 +- .../miscfeatures/updater/SignedGithubUpdateData.kt | 70 ++++++++++++++++++++++ .../miscfeatures/updater/SigningGithubSource.kt | 31 +--------- .../miscfeatures/updater/AutoUpdater.kt | 25 ++++++-- .../miscfeatures/updater/ConfigVersionGuiOption.kt | 2 +- 6 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SignedGithubUpdateData.kt diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index de01458a..621072f9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,10 @@ jobs: - name: Set up gradle cache uses: gradle/gradle-build-action@v2 - name: Build with Gradle - run: ./gradlew clean test includeBackupRepo remapJar --no-daemon + run: | + ./gradlew clean + ./gradlew includeBackupRepo + ./gradlew test remapJar env: NEU_RELEASE: true - uses: actions/upload-artifact@v3 diff --git a/build.gradle.kts b/build.gradle.kts index 087212ef..7dccd301 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -163,7 +163,7 @@ dependencies { "oneconfigAnnotationProcessor"("org.projectlombok:lombok:1.18.24") shadowImplementation("com.mojang:brigadier:1.0.18") - shadowImplementation("moe.nea:libautoupdate:1.2.0") + shadowImplementation("moe.nea:libautoupdate:1.3.1") mixinRTDependencies("org.spongepowered:mixin:0.7.11-SNAPSHOT") { isTransitive = false // Dependencies of mixin are already bundled by minecraft @@ -227,6 +227,7 @@ tasks.withType(Jar::class) { this["FMLCorePluginContainsFMLMod"] = "true" this["ForceLoadAsMod"] = "true" this["Manifest-Version"] = "1.0" + this["FMLAT"] = "accesstransformer.cfg" } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SignedGithubUpdateData.kt b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SignedGithubUpdateData.kt new file mode 100644 index 00000000..7cea041c --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SignedGithubUpdateData.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2024 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see . + */ + +package io.github.moulberry.notenoughupdates.miscfeatures.updater + +import com.google.gson.JsonElement +import moe.nea.libautoupdate.GithubReleaseUpdateSource.GithubRelease +import moe.nea.libautoupdate.UpdateData +import moe.nea.libautoupdate.UpdateUtils +import java.io.ByteArrayInputStream +import java.net.URL + +class SignedGithubUpdateData( + versionName: String, + versionNumber: JsonElement, + sha256: String, + download: String, + val signatures: List +) : UpdateData( + versionName, + versionNumber, + sha256, + download +) { + override fun toString(): String { + return "${super.toString()} + Signatures(signatures = ${signatures.map { it.name }}})" + } + + fun verifyAnySignature(): Boolean { + val signatories = validSignatories + for (signatory in signatories) { + println("Accepted signature from ${signatory.name}") + } + return signatories.size >= 2 + } + + val validSignatories by lazy { + findValidSignatories() + } + + + private fun findValidSignatories(): List { + val signatures = signatures + return signatures.filter { verifySignature(it) } + } + + private fun verifySignature(signatureDownload: GithubRelease.Download): Boolean { + val name = signatureDownload.name.substringBeforeLast('.').substringAfterLast("_") + val signatureBytes = UpdateUtils.openUrlConnection(URL(signatureDownload.browserDownloadUrl)).readBytes() + val hashBytes = ByteArrayInputStream(sha256.uppercase().encodeToByteArray()) + return SigningPool.verifySignature(name, hashBytes, signatureBytes) + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SigningGithubSource.kt b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SigningGithubSource.kt index b8c804ff..5b7c08df 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SigningGithubSource.kt +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/SigningGithubSource.kt @@ -21,44 +21,19 @@ package io.github.moulberry.notenoughupdates.miscfeatures.updater import moe.nea.libautoupdate.GithubReleaseUpdateSource import moe.nea.libautoupdate.UpdateData -import java.io.ByteArrayInputStream -import java.io.File -import java.net.URL class SigningGithubSource(username: String, repo: String) : GithubReleaseUpdateSource(username, repo) { val hashRegex = "sha256sum: `(?[a-fA-F0-9]{64})`".toPattern() override fun findAsset(release: GithubRelease): UpdateData? { - var asset = super.findAsset(release) ?: return null + val asset = super.findAsset(release) ?: return null val match = release.body.lines() .firstNotNullOfOrNull { line -> hashRegex.matcher(line).takeIf { it.matches() } } ?: return null // Inject our custom sha256sum - asset = UpdateData(asset.versionName, asset.versionNumber, match.group("hash"), asset.download) - // Verify at least 2 signatures are present on this release - if (!verifyAnySignature(release, asset)) - return null - return asset + return SignedGithubUpdateData(asset.versionName, asset.versionNumber, match.group("hash"), asset.download, + release.assets.filter { it.name.endsWith(".asc") }) } - private fun verifyAnySignature(release: GithubRelease, asset: UpdateData): Boolean { - val signatories = findValidSignatories(release, asset) - for (signatory in signatories) { - println("Accepted signature from ${signatory.name}") - } - return signatories.size >= 2 - } - - fun findValidSignatories(release: GithubRelease, asset: UpdateData): List { - val signatures = release.assets?.filter { it.name.endsWith(".asc") } ?: emptyList() - return signatures.filter { verifySignature(it, asset) } - } - - fun verifySignature(signatureDownload: GithubRelease.Download, asset: UpdateData): Boolean { - val name = signatureDownload.name.substringBeforeLast('.').removePrefix("_") - val signatureBytes = URL(signatureDownload.browserDownloadUrl).openStream().readBytes() - val hashBytes = ByteArrayInputStream(asset.sha256.uppercase().encodeToByteArray()) - return SigningPool.verifySignature(name, hashBytes, signatureBytes) - } } diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt index b9ce28b2..a522cb47 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.kt @@ -22,12 +22,10 @@ package io.github.moulberry.notenoughupdates.miscfeatures.updater import io.github.moulberry.notenoughupdates.NotEnoughUpdates import io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSubscribe import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent +import io.github.moulberry.notenoughupdates.util.ApiUtil import io.github.moulberry.notenoughupdates.util.MinecraftExecutor import io.github.moulberry.notenoughupdates.util.brigadier.thenExecute -import moe.nea.libautoupdate.CurrentVersion -import moe.nea.libautoupdate.PotentialUpdate -import moe.nea.libautoupdate.UpdateContext -import moe.nea.libautoupdate.UpdateTarget +import moe.nea.libautoupdate.* import net.minecraft.client.Minecraft import net.minecraft.event.ClickEvent import net.minecraft.util.ChatComponentText @@ -36,6 +34,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent import org.apache.logging.log4j.LogManager import java.util.concurrent.CompletableFuture +import javax.net.ssl.HttpsURLConnection @NEUAutoSubscribe object AutoUpdater { @@ -45,6 +44,15 @@ object AutoUpdater { CurrentVersion.ofTag(NotEnoughUpdates.VERSION.substringBefore("+")), "notenoughupdates" ) + + init { + UpdateUtils.patchConnection { + if (it is HttpsURLConnection) { + ApiUtil.patchHttpsRequest(it) + } + } + } + val logger = LogManager.getLogger("NEUUpdater") private var activePromise: CompletableFuture<*>? = null set(value) { @@ -80,6 +88,11 @@ object AutoUpdater { logger.info("Starting update check") val updateStream = config.updateStream.get() activePromise = updateContext.checkUpdate(updateStream.stream) + .thenApplyAsync({ + if (it.isUpdateAvailable) + (it.update as? SignedGithubUpdateData)?.verifyAnySignature() + it + }, MinecraftExecutor.OffThread) .thenAcceptAsync({ logger.info("Update check completed") if (updateState != UpdateState.NONE) { @@ -88,6 +101,10 @@ object AutoUpdater { } potentialUpdate = it if (it.isUpdateAvailable) { + if ((it.update as? SignedGithubUpdateData)?.verifyAnySignature() != true) { + logger.error("Found unsigned github update: ${it.update}") + return@thenAcceptAsync + } updateState = UpdateState.AVAILABLE Minecraft.getMinecraft().thePlayer?.addChatMessage(ChatComponentText("§e[NEU] §aNEU found a new update: ${it.update.versionName}. Click here to automatically install this update.").apply { this.chatStyle = this.chatStyle.setChatClickEvent( diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt index 3a920aec..da74ce22 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/miscfeatures/updater/ConfigVersionGuiOption.kt @@ -62,7 +62,7 @@ class ConfigVersionGuiOption(option: ProcessedOption) : GuiOptionEditor(option) GlStateManager.scale(2F, 2F, 1F) TextRenderUtils.drawStringCenteredScaledMaxWidth( "${if (AutoUpdater.updateState == AutoUpdater.UpdateState.NONE) GREEN else RED}${AutoUpdater.getCurrentVersion()}" + - if (nextVersion != null) "➜ ${GREEN}${nextVersion}" else "", + if (nextVersion != null && AutoUpdater.updateState != AutoUpdater.UpdateState.NONE) "➜ ${GREEN}${nextVersion}" else "", widthRemaining / 4F, 10F, true, -- cgit