From 54eea328ad47ff55f3c8498fdc13e61a30200087 Mon Sep 17 00:00:00 2001 From: Roman / Linnea Gräf Date: Tue, 11 Oct 2022 21:43:00 +0200 Subject: Add ability to sign update.json, needed for autoupdates (#354) * Add ability to sign update.json, needed for autoupdates * Remove update from URL --- .../commands/misc/UpdateCommand.java | 18 ----- .../miscfeatures/updater/AutoUpdater.java | 9 +++ .../notenoughupdates/util/MoulSigner.java | 80 ++++++++++++++++++++++ 3 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/util/MoulSigner.java (limited to 'src/main/java') diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java index 36443b27..cac93369 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/UpdateCommand.java @@ -49,10 +49,6 @@ public class UpdateCommand extends ClientCommandBase { "" + "§e[NEU] §b/neuupdate help - View help.\n" + "§e[NEU] §b/neuupdate check - Check for updates.\n" + - "§e[NEU] §b/neuupdate url - Load an update from an direct download URL.\n" + - " §cONLY DO THIS WITH TRUSTED URLS OR IT MIGHT RESULT IN A RAT!\n" + - "§e[NEU] §b/neuupdate fromartifact - Load an update from an artifact.\n" + - " §cIf you don't know what this is, don't use it.\n" + "" )); @@ -68,20 +64,6 @@ public class UpdateCommand extends ClientCommandBase { case "check": neu.autoUpdater.displayUpdateMessageIfOutOfDate(); break; - case "url": - if (args.length != 2) { - sender.addChatMessage(new ChatComponentText("§e[NEU] §cPlease provide an URL")); - } - URL url; - try { - url = new URL(args[1]); - } catch (MalformedURLException e) { - e.printStackTrace(); - sender.addChatMessage(new ChatComponentText("§e[NEU] §cInvalid URL")); - return; - } - neu.autoUpdater.updateFromURL(url); - break; case "scheduledownload": neu.autoUpdater.scheduleDownload(); break; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java index 389cfc75..54fcc204 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/updater/AutoUpdater.java @@ -26,6 +26,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.util.MoulSigner; import io.github.moulberry.notenoughupdates.util.NotificationHandler; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; @@ -171,6 +172,14 @@ public class AutoUpdater { File repo = neu.manager.repoLocation; File updateJson = new File(repo, "update.json"); if (updateJson.exists()) { + if (!MoulSigner.verifySignature(updateJson)) { + NotEnoughUpdates.LOGGER.error("update.json found without signature"); + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText( + "§e[NEU] §cThere has been an error checking for updates. Check the log or join the discord for more information.").setChatStyle( + Utils.createClickStyle( + ClickEvent.Action.OPEN_URL, "https://discord.gg/moulberry"))); + return; + } try { JsonObject o = neu.manager.getJsonFromFile(updateJson); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/MoulSigner.java b/src/main/java/io/github/moulberry/notenoughupdates/util/MoulSigner.java new file mode 100644 index 00000000..6510bc20 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/MoulSigner.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.util; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +public class MoulSigner { + private MoulSigner() {} + + static PublicKey publicKey; + + static { + try (InputStream is = MoulSigner.class.getResourceAsStream("/moulberry.key")) { + byte[] publicKeyBytes = IOUtils.toByteArray(is); + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes); + publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec); + } catch (IOException | NullPointerException | NoSuchAlgorithmException | InvalidKeySpecException e) { + NotEnoughUpdates.LOGGER.error("Cannot initialize MoulSigner", e); + } + } + + public static boolean verifySignature(byte[] data, byte[] signatureBytes) { + if (Boolean.getBoolean("neu.noverifysignature")) return true; + if (publicKey == null) { + NotEnoughUpdates.LOGGER.warn("MoulSigner could not be initialized, will fail this request"); + return false; + } + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(publicKey); + signature.update(data); + return signature.verify(signatureBytes); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { + NotEnoughUpdates.LOGGER.error("Error while verifying signature. Considering this as invalid signature", e); + return false; + } + } + + public static boolean verifySignature(File file) { + try { + return verifySignature( + IOUtils.toByteArray(file.toURI()), + IOUtils.toByteArray(new File(file.getParentFile(), file.getName() + ".asc").toURI()) + ); + } catch (IOException e) { + NotEnoughUpdates.LOGGER.error("Ran into an IOException while verifying a signature", e); + return false; + } + } +} -- cgit