updater: download and verify update

This commit is contained in:
Moritz Zwerger 2024-01-17 18:28:52 +01:00
parent e6d48ddfdc
commit 8a0701e854
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
6 changed files with 78 additions and 60 deletions

View File

@ -14,13 +14,10 @@
package de.bixilon.minosoft.assets.util
import com.github.luben.zstd.ZstdInputStream
import de.bixilon.kutil.buffer.BufferDefinition
import de.bixilon.kutil.random.RandomStringUtil.randomString
import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.KUtil
import java.io.*
import java.nio.file.Files
import java.nio.file.Path
import java.security.MessageDigest
object FileUtil {
@ -60,31 +57,6 @@ object FileUtil {
fun createTempFile(): File {
var file: File
for (i in 0 until AssetsOptions.MAX_FILE_CHECKING) {
file = RunConfiguration.TEMPORARY_FOLDER.resolve(KUtil.RANDOM.randomString(32)).toFile()
if (!file.exists()) {
return file
}
}
throw IOException("Can not find temporary file after ${AssetsOptions.MAX_FILE_CHECKING} tries!")
}
@Deprecated("kutil 1.26")
fun InputStream.copy(vararg output: OutputStream, digest: MessageDigest?) {
val buffer = ByteArray(BufferDefinition.DEFAULT_BUFFER_SIZE)
while (true) {
val length = read(buffer, 0, buffer.size)
if (length < 0) {
break
}
for (stream in output) {
stream.write(buffer, 0, length)
}
digest?.update(buffer, 0, length)
}
return Files.createTempFile(RunConfiguration.TEMPORARY_FOLDER, "", "").toFile()
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2024 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -285,17 +285,11 @@ class OpenGLRenderSystem(
}
override fun getErrors(): List<OpenGLError> {
var error: Int = glGetError()
if (error == GL_NO_ERROR) {
return emptyList()
}
val errors: MutableList<OpenGLError> = mutableListOf()
do {
errors += OpenGLError(error)
error = glGetError()
} while (error != GL_NO_ERROR)
val error = glGetError()
return errors
if (error == GL_NO_ERROR) return emptyList()
// opengl only supports single error
return listOf(OpenGLError(error))
}
private var polygonOffsetFactor: Float = 0.0f

View File

@ -13,14 +13,20 @@
package de.bixilon.minosoft.updater
import com.google.common.io.Files
import de.bixilon.kutil.array.ByteArrayUtil.toHex
import de.bixilon.kutil.base64.Base64Util.fromBase64
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.kutil.hash.HashUtil
import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.kutil.os.PlatformInfo
import de.bixilon.kutil.string.StringUtil.formatPlaceholder
import de.bixilon.kutil.url.URLUtil.toURL
import de.bixilon.minosoft.assets.util.FileUtil
import de.bixilon.minosoft.config.profile.profiles.other.OtherProfileManager
import de.bixilon.minosoft.properties.MinosoftProperties
import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.KUtil.copy
import de.bixilon.minosoft.util.http.HTTP2.get
import de.bixilon.minosoft.util.http.HTTPResponse
import de.bixilon.minosoft.util.http.exceptions.HTTPException
@ -28,7 +34,11 @@ import de.bixilon.minosoft.util.json.Jackson
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import java.io.File
import java.io.FileOutputStream
import java.net.URL
import java.security.MessageDigest
import java.security.SignatureException
object MinosoftUpdater {
var update: MinosoftUpdate? by observed(null)
@ -110,13 +120,36 @@ object MinosoftUpdater {
val download = update.download
if (download == null) {
progress.log?.print("Update is unavailable for download. Please download it manually!")
progress.stage = UpdateProgress.UpdateStage.FAILED
progress.error = IllegalAccessError("Unavailable...")
return
}
progress.log?.print("Downloading update...")
progress.log?.print("TODO :)")
progress.stage = UpdateProgress.UpdateStage.FAILED
// UpdateKey.require(data, signature)
try {
val stream = download.url.openStream()
val digest = MessageDigest.getInstance(HashUtil.SHA_512)
val temp = FileUtil.createTempFile()
val signature = UpdateKey.createInstance()
stream.copy(FileOutputStream(temp), digest = digest, signature = signature)
if (digest.digest().toHex() != download.sha512) throw SignatureException("Hash mismatch of downloaded file: Expected ${download.sha512}, got ${digest.digest().toHex()}")
if (!signature.verify(download.signature.fromBase64())) throw SignatureException("Signature of downloaded file mismatches!")
progress.log?.print("Moving temporary file to final destination")
// move to current directory
val output = File(("./Minosoft-${update.id}.jar"))
Files.move(temp, output)
progress.log?.print("Success, file saved to $output")
// TODO: restart minosoft
} catch (error: Throwable) {
if (progress.log == null) {
error.printStackTrace()
} else {
progress.log?.print(error)
}
progress.error = error
throw error
}
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2024 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -15,23 +15,11 @@ package de.bixilon.minosoft.updater
import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.kutil.latch.SimpleLatch
import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.minosoft.commands.stack.print.PrintTarget
class UpdateProgress(
val progress: AbstractLatch = SimpleLatch(0),
var log: PrintTarget? = null,
) {
var stage by observed(UpdateStage.WAITING)
var error: Throwable? = null
enum class UpdateStage {
WAITING,
DOWNLOADING,
VERIFYING,
STORING,
DONE,
FAILED,
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2024 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -21,6 +21,7 @@ import de.bixilon.kotlinglm.vec2.Vec2t
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kotlinglm.vec3.Vec3t
import de.bixilon.kotlinglm.vec4.Vec4t
import de.bixilon.kutil.buffer.BufferDefinition
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.collections.CollectionUtil.synchronizedListOf
import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf
@ -29,6 +30,7 @@ import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedSet
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.kutil.concurrent.pool.runnable.ForcePooledRunnable
import de.bixilon.kutil.concurrent.schedule.TaskScheduler
import de.bixilon.kutil.exception.ExceptionUtil
import de.bixilon.kutil.primitive.BooleanUtil.decide
import de.bixilon.kutil.primitive.DoubleUtil
import de.bixilon.kutil.primitive.DoubleUtil.matches
@ -69,7 +71,11 @@ import io.netty.channel.SimpleChannelInboundHandler
import javafx.application.Platform
import org.kamranzafar.jtar.TarHeader
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.security.MessageDigest
import java.security.SecureRandom
import java.security.Signature
import java.util.*
import javax.net.ssl.SSLContext
@ -355,4 +361,29 @@ object KUtil {
stream.close()
println("Packet dumped to $path")
}
@Deprecated("kutil 1.26")
fun InputStream.copy(vararg output: OutputStream, digest: MessageDigest? = null, signature: Signature? = null, closeIn: Boolean = true, closeOut: Boolean = true) {
val buffer = ByteArray(BufferDefinition.DEFAULT_BUFFER_SIZE)
while (true) {
val length = read(buffer, 0, buffer.size)
if (length < 0) {
break
}
for (stream in output) {
stream.write(buffer, 0, length)
}
digest?.update(buffer, 0, length)
signature?.update(buffer, 0, length)
}
if (closeIn) ExceptionUtil.ignoreAll { close() }
if (closeOut) {
for (stream in output) {
ExceptionUtil.ignoreAll { stream.close() }
}
}
}
}

View File

@ -36,7 +36,7 @@ abstract class SignatureSigner(
key = keyFactory.generatePublic(spec)
}
protected fun createInstance(): Signature {
fun createInstance(): Signature {
val instance = Signature.getInstance(algorithm)
instance.initVerify(key)