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 --- buildSrc/generate-public-key.sh | 31 ++++++++ buildSrc/moulsign.sh | 26 +++++++ .../commands/misc/UpdateCommand.java | 18 ----- .../miscfeatures/updater/AutoUpdater.java | 9 +++ .../notenoughupdates/util/MoulSigner.java | 80 +++++++++++++++++++++ src/main/resources/moulberry.key | Bin 0 -> 422 bytes 6 files changed, 146 insertions(+), 18 deletions(-) create mode 100755 buildSrc/generate-public-key.sh create mode 100755 buildSrc/moulsign.sh create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/util/MoulSigner.java create mode 100644 src/main/resources/moulberry.key diff --git a/buildSrc/generate-public-key.sh b/buildSrc/generate-public-key.sh new file mode 100755 index 00000000..3f778c53 --- /dev/null +++ b/buildSrc/generate-public-key.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# +# 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 . +# + + +output="$(dirname $(dirname $(readlink -f "$0")))/src/main/resources/moulberry.key" + +echo processing rsa input key from $1, and outputting to $output + +tempfile="$(mktemp)" +ssh-keygen -f "$1" -e -m pkcs8 > "$tempfile" +openssl rsa -pubin -in "$tempfile" -outform der > $output + +echo saved x509 public key at $output + diff --git a/buildSrc/moulsign.sh b/buildSrc/moulsign.sh new file mode 100755 index 00000000..dacb8ec3 --- /dev/null +++ b/buildSrc/moulsign.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# +# 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 . +# + +echo use key $1, signing file $2 +openssl dgst -sign "$1" "$2" > "$2.asc" +echo signature saved to "$2.asc" + + + 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; + } + } +} diff --git a/src/main/resources/moulberry.key b/src/main/resources/moulberry.key new file mode 100644 index 00000000..5a7a3307 Binary files /dev/null and b/src/main/resources/moulberry.key differ -- cgit