Skip to content

Commit

Permalink
Fix access transformer crash and invalid SSL context for auto updater (
Browse files Browse the repository at this point in the history
  • Loading branch information
nea89o authored Apr 27, 2024
1 parent 9547ebe commit c266561
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 35 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -227,6 +227,7 @@ tasks.withType(Jar::class) {
this["FMLCorePluginContainsFMLMod"] = "true"
this["ForceLoadAsMod"] = "true"
this["Manifest-Version"] = "1.0"
this["FMLAT"] = "accesstransformer.cfg"
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*/

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<GithubRelease.Download>
) : 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<GithubRelease.Download> {
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)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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: `(?<hash>[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<GithubRelease.Download> {
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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit c266561

Please sign in to comment.