From 190e84af65d83994d5bb1a941d034ab7a5e633d7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 17:07:11 +0100 Subject: [PATCH 01/15] Removed useless translation --- app/src/processing/app/UpdateCheck.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index cdca1b71783..4bf2c814d5f 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -51,7 +51,6 @@ */ public class UpdateCheck implements Runnable { Base base; - String downloadURL = tr("https://www.arduino.cc/latest.txt"); static final long ONE_DAY = 24 * 60 * 60 * 1000; @@ -87,7 +86,7 @@ public void run() { System.getProperty("os.version") + "\t" + System.getProperty("os.arch"), "UTF-8"); - int latest = readInt(downloadURL + "?" + info); + int latest = readInt("https://www.arduino.cc/latest.txt?" + info); String lastString = PreferencesData.get("update.last"); long now = System.currentTimeMillis(); @@ -116,7 +115,7 @@ public void run() { options, options[0]); if (result == JOptionPane.YES_OPTION) { - Base.openURL(tr("https://www.arduino.cc/en/Main/Software")); + Base.openURL("https://www.arduino.cc/en/Main/Software"); } } } From 4e7caa8219f835579bf1d1b1007ee7b5cea52d21 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 17:08:47 +0100 Subject: [PATCH 02/15] Slightly refactored update-check method Cosmetic-only change that makes the logic of the method clearer --- app/src/processing/app/UpdateCheck.java | 30 ++++++++++++------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 4bf2c814d5f..0db272b2f63 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -52,19 +52,27 @@ public class UpdateCheck implements Runnable { Base base; - static final long ONE_DAY = 24 * 60 * 60 * 1000; - - public UpdateCheck(Base base) { Thread thread = new Thread(this); this.base = base; thread.start(); } - public void run() { - //System.out.println("checking for updates..."); + // Ensure updates-check are made only once per day + String lastString = PreferencesData.get("update.last"); + long now = System.currentTimeMillis(); + if (lastString != null) { + final long ONE_DAY = 24 * 60 * 60 * 1000; + long when = Long.parseLong(lastString); + if ((now - when) < ONE_DAY) { + // don't annoy the shit outta people + return; + } + } + PreferencesData.set("update.last", String.valueOf(now)); + // Set update id long id; String idString = PreferencesData.get("update.id"); if (idString != null) { @@ -76,6 +84,7 @@ public void run() { PreferencesData.set("update.id", String.valueOf(id)); } + // Check for updates of the IDE try { String info; info = URLEncoder.encode(id + "\t" + @@ -88,17 +97,6 @@ public void run() { int latest = readInt("https://www.arduino.cc/latest.txt?" + info); - String lastString = PreferencesData.get("update.last"); - long now = System.currentTimeMillis(); - if (lastString != null) { - long when = Long.parseLong(lastString); - if (now - when < ONE_DAY) { - // don't annoy the shit outta people - return; - } - } - PreferencesData.set("update.last", String.valueOf(now)); - String prompt = tr("A new version of Arduino is available,\n" + "would you like to visit the Arduino download page?"); From 6d304c26da9389b71853c8494364f6794e9cc9ed Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 18:37:45 +0100 Subject: [PATCH 03/15] Added PreferencesData.setLong/getLong helpers --- app/src/processing/app/UpdateCheck.java | 24 +++++++------------ .../src/processing/app/PreferencesData.java | 12 ++++++++++ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 0db272b2f63..03611c41f34 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -60,28 +60,22 @@ public UpdateCheck(Base base) { public void run() { // Ensure updates-check are made only once per day - String lastString = PreferencesData.get("update.last"); + Long when = PreferencesData.getLong("update.last"); long now = System.currentTimeMillis(); - if (lastString != null) { - final long ONE_DAY = 24 * 60 * 60 * 1000; - long when = Long.parseLong(lastString); - if ((now - when) < ONE_DAY) { - // don't annoy the shit outta people - return; - } + final long ONE_DAY = 24 * 60 * 60 * 1000; + if (when != null && (now - when) < ONE_DAY) { + // don't annoy the shit outta people + return; } - PreferencesData.set("update.last", String.valueOf(now)); + PreferencesData.setLong("update.last", now); // Set update id - long id; - String idString = PreferencesData.get("update.id"); - if (idString != null) { - id = Long.parseLong(idString); - } else { + Long id = PreferencesData.getLong("update.id"); + if (id == null) { // generate a random id in case none exists yet Random r = new Random(); id = r.nextLong(); - PreferencesData.set("update.id", String.valueOf(id)); + PreferencesData.setLong("update.id", id); } // Check for updates of the IDE diff --git a/arduino-core/src/processing/app/PreferencesData.java b/arduino-core/src/processing/app/PreferencesData.java index 01f4568ad5b..162c4090224 100644 --- a/arduino-core/src/processing/app/PreferencesData.java +++ b/arduino-core/src/processing/app/PreferencesData.java @@ -282,4 +282,16 @@ public static boolean areInsecurePackagesAllowed() { } return getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL, false); } + + public static void setLong(String k, long v) { + set(k, String.valueOf(v)); + } + + public static Long getLong(String k) { + try { + return Long.parseLong(get(k)); + } catch (NumberFormatException e) { + return null; + } + } } From 864cea63bbb17269d44dea2e378d5baaa60429e4 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 18:40:07 +0100 Subject: [PATCH 04/15] Added other helper functions to download data from URLs --- app/src/processing/app/UpdateCheck.java | 45 ++++++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 03611c41f34..015d5fb3443 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -22,18 +22,27 @@ package processing.app; -import org.apache.commons.compress.utils.IOUtils; -import processing.app.legacy.PApplet; +import static processing.app.I18n.tr; -import javax.swing.*; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.List; import java.util.Random; +import java.util.stream.Collectors; -import static processing.app.I18n.tr; +import javax.swing.JOptionPane; + +import org.apache.commons.compress.utils.IOUtils; + +import processing.app.legacy.PApplet; /** @@ -89,7 +98,7 @@ public void run() { System.getProperty("os.version") + "\t" + System.getProperty("os.arch"), "UTF-8"); - int latest = readInt("https://www.arduino.cc/latest.txt?" + info); + int latest = readIntFromURL("https://www.arduino.cc/latest.txt?" + info); String prompt = tr("A new version of Arduino is available,\n" + @@ -118,14 +127,24 @@ public void run() { } - protected int readInt(String filename) throws IOException { - URL url = new URL(filename); - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(url.openStream())); - return Integer.parseInt(reader.readLine()); - } finally { - IOUtils.closeQuietly(reader); + protected int readIntFromURL(String _url) throws Exception { + List lines = readFileFromURL(_url); + return Integer.parseInt(lines.get(0)); + } + + protected List readFileFromURL(String _url) throws IOException { + URL url = new URL(_url); + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));) { + return in.lines().collect(Collectors.toList()); + } + } + + protected void downloadFileFromURL(String _url, File dest) throws IOException { + URL url = new URL(_url); + try (InputStream in = url.openStream()) { + try (FileOutputStream out = new FileOutputStream(dest)) { + IOUtils.copy(in, out); + } } } } From b907ea5b2ab5289e2c8fbab00d683fbb1ab0c4da Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 19:11:14 +0100 Subject: [PATCH 05/15] Allow splash image to be updated via network --- .../cc/arduino/view/SplashScreenHelper.java | 21 ++++++++++- app/src/processing/app/UpdateCheck.java | 36 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/app/src/cc/arduino/view/SplashScreenHelper.java b/app/src/cc/arduino/view/SplashScreenHelper.java index 108c1c8b2f6..522448c1bed 100644 --- a/app/src/cc/arduino/view/SplashScreenHelper.java +++ b/app/src/cc/arduino/view/SplashScreenHelper.java @@ -31,11 +31,18 @@ package cc.arduino.view; -import java.awt.*; +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.SplashScreen; +import java.awt.Toolkit; import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.IOException; import java.util.Map; import processing.app.Theme; +import processing.app.UpdateCheck; public class SplashScreenHelper { @@ -57,6 +64,10 @@ public SplashScreenHelper(SplashScreen splash) { } else { desktopHints = null; } + File image = UpdateCheck.getUpdatedSplashImageFile(); + if (image != null) { + splashImage(image); + } } public void splashText(String text) { @@ -120,4 +131,12 @@ private void printText(String str) { System.err.println(str); } + public void splashImage(File f) { + try { + splash.setImageURL(f.toURI().toURL()); + } catch (NullPointerException | IllegalStateException | IOException e) { + e.printStackTrace(); + } + } + } diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 015d5fb3443..aed7ae42b76 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -124,8 +124,44 @@ public void run() { //e.printStackTrace(); //System.err.println("Error while trying to check for an update."); } + + try { + // Check for updates of the splash screen + List lines = readFileFromURL("https://go.bug.st/latest_splash.txt"); + if (lines.size() > 0) { + // if the splash image has been changed download the new file + String newSplashUrl = lines.get(0); + String oldSplashUrl = PreferencesData.get("splash.imageurl"); + if (!newSplashUrl.equals(oldSplashUrl)) { + File tmpFile = BaseNoGui.getSettingsFile("splash.png.tmp"); + downloadFileFromURL(newSplashUrl, tmpFile); + File destFile = BaseNoGui.getSettingsFile("splash.png"); + Files.move(tmpFile.toPath(), destFile.toPath(), + StandardCopyOption.REPLACE_EXISTING); + PreferencesData.set("splash.imageurl", newSplashUrl); + } + + // extend expiration by 24h + PreferencesData.setLong("splash.expire", now + ONE_DAY); + } + } catch (Exception e) { + // e.printStackTrace(); + } } + public static File getUpdatedSplashImageFile() { + if (PreferencesData.has("splash.expire")) { + Long expire = PreferencesData.getLong("splash.expire"); + long now = System.currentTimeMillis(); + if (expire != null && now < expire) { + File f = BaseNoGui.getSettingsFile("splash.png"); + if (f.isFile()) { + return f; + } + } + } + return null; + } protected int readIntFromURL(String _url) throws Exception { List lines = readFileFromURL(_url); From 7614e7317971ff4511d0570c018f7ffd01a5bbce Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 20:16:07 +0100 Subject: [PATCH 06/15] Check for updated splashimage only if SplashScreen is active --- app/src/cc/arduino/view/SplashScreenHelper.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/cc/arduino/view/SplashScreenHelper.java b/app/src/cc/arduino/view/SplashScreenHelper.java index 522448c1bed..a67c3f4d48f 100644 --- a/app/src/cc/arduino/view/SplashScreenHelper.java +++ b/app/src/cc/arduino/view/SplashScreenHelper.java @@ -61,13 +61,13 @@ public SplashScreenHelper(SplashScreen splash) { if (splash != null) { Toolkit tk = Toolkit.getDefaultToolkit(); desktopHints = (Map) tk.getDesktopProperty("awt.font.desktophints"); + File image = UpdateCheck.getUpdatedSplashImageFile(); + if (image != null) { + splashImage(image); + } } else { desktopHints = null; } - File image = UpdateCheck.getUpdatedSplashImageFile(); - if (image != null) { - splashImage(image); - } } public void splashText(String text) { From d2beb993ff00a8678c6d441d372916b53046531e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 27 Mar 2020 13:09:15 +0100 Subject: [PATCH 07/15] Added helper method to scale images in Theme class --- app/src/processing/app/Theme.java | 35 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/Theme.java b/app/src/processing/app/Theme.java index d38875b3597..24a7c2db934 100644 --- a/app/src/processing/app/Theme.java +++ b/app/src/processing/app/Theme.java @@ -575,23 +575,42 @@ static public Image getLibImage(String filename, Component who, int width, image = tk.getImage(imageFile.getUrl()); } + image = rescaleImage(image, who, width, height); + + return image; + } + + public static Image rescaleImage(Image image, Component who, int width, int height) { MediaTracker tracker = new MediaTracker(who); try { tracker.addImage(image, 0); tracker.waitForAll(); } catch (InterruptedException e) { } + if (image.getWidth(null) == width && image.getHeight(null) == height) { + return image; + } - if (image.getWidth(null) != width || image.getHeight(null) != height) { - image = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); - try { - tracker.addImage(image, 1); - tracker.waitForAll(); - } catch (InterruptedException e) { - } + Image rescaled = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); + try { + tracker.addImage(rescaled, 1); + tracker.waitForAll(); + } catch (InterruptedException e) { } + return rescaled; + } - return image; + public static Image scale(Image image, Component who) { + MediaTracker tracker = new MediaTracker(who); + try { + tracker.addImage(image, 0); + tracker.waitForAll(); + } catch (InterruptedException e) { + } + + int w = image.getWidth(null); + int h = image.getHeight(null); + return rescaleImage(image, who, scale(w), scale(h)); } /** From 87a9af3369ffc102fbaed8deb1bb6d2c58badf4d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 26 Mar 2020 20:17:08 +0100 Subject: [PATCH 08/15] Use updated splash image also in About screen --- app/src/processing/app/Base.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 7af728fdd49..138101b025e 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -1886,8 +1886,16 @@ static public String[] headerListFromIncludePath(File path) throws IOException { */ @SuppressWarnings("serial") public void handleAbout() { - final Image image = Theme.getLibImage("about", activeEditor, - Theme.scale(475), Theme.scale(300)); + Image image; + File f = UpdateCheck.getUpdatedSplashImageFile(); + if (f != null) { + Toolkit tk = Toolkit.getDefaultToolkit(); + Image unscaled = tk.getImage(f.getAbsolutePath()); + image = Theme.scale(unscaled, activeEditor); + } else { + image = Theme.getLibImage("about", activeEditor, // + Theme.scale(475), Theme.scale(300)); + } final Window window = new Window(activeEditor) { public void paint(Graphics graphics) { Graphics2D g = Theme.setupGraphics2D(graphics); From 0cd9e490ea8ccbda58f70c2c3ebb78f62dd6925e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Mar 2020 20:24:21 +0200 Subject: [PATCH 09/15] Added 'Arduino Packages' public gpg key Also moved test key into proper package. --- app/build.xml | 1 + .../GPGDetachedSignatureVerifierTest.java | 4 +- .../{public.gpg.key => test.public.gpg.key} | Bin 3824 -> 6080 bytes .../contributions/SignatureVerifier.java | 64 ------------------ build/shared/lib/public.gpg.key | Bin 3824 -> 6080 bytes 5 files changed, 3 insertions(+), 66 deletions(-) rename app/test/cc/arduino/contributions/{public.gpg.key => test.public.gpg.key} (62%) delete mode 100644 arduino-core/src/cc/arduino/contributions/SignatureVerifier.java diff --git a/app/build.xml b/app/build.xml index a2926990222..b268b777842 100644 --- a/app/build.xml +++ b/app/build.xml @@ -101,6 +101,7 @@ + diff --git a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java index 7dd7285a064..d4219b9cdd6 100644 --- a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java +++ b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java @@ -50,7 +50,7 @@ public void setUp() throws Exception { public void testSignatureSuccessfulVerification() throws Exception { File signedFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json").getFile()); File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); - File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./public.gpg.key").getFile()); + File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); assertTrue(GPGDetachedSignatureVerifier.verify(signedFile, sign, publickKey)); } @@ -59,7 +59,7 @@ public void testSignatureFailingVerification() throws Exception { File fakeSignedFile = File.createTempFile("fakeSigned", "txt"); fakeSignedFile.deleteOnExit(); File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); - File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./public.gpg.key").getFile()); + File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); assertFalse(GPGDetachedSignatureVerifier.verify(fakeSignedFile, sign, publickKey)); } } diff --git a/app/test/cc/arduino/contributions/public.gpg.key b/app/test/cc/arduino/contributions/test.public.gpg.key similarity index 62% rename from app/test/cc/arduino/contributions/public.gpg.key rename to app/test/cc/arduino/contributions/test.public.gpg.key index 5de39fed1f7fb56720dd634a9c61d1b2f3153bd6..fde092a92ac5c0dae64fd0477ebdb61cb7078be4 100644 GIT binary patch delta 2253 zcmV;;2r~EZ9l$TJ@D6{O0u2OM(KEdP5CFU9e9S*LjzQt``ZZ{?1i^qJ{*C00OE$Pc zqwZ9%kZ+Ak-L-*_Q42NX!O`mCmeU?lNKMe>>X*c@ECWu>WNzIJe|Eo7R~4TBiri#G z-Xy!+xSwNYoQYwAYg{F9bgb$-eul?J9!+5thkq|ka?pP^pgw<{I}t*qFDnN!x<7^! z$|=kNl|J9neb7)O)-f5;Xv0{iCmrGF`^2uK(@T)8QL5u9$kzFqh?Hzcx%$hfWSfvK zmAGkl7zmQ%FwB8j#IVdPM)Xy{hE%XCl`F)hw9LnB!R6?tI1}c{OC+W89RY0Msl!Q> z820g7BiF>sdoq75aoP~-x!XDlG^uyV_N3lt3I8*z2wGVD7e=JysmS;I+17;?@CAN-Dr;T0Sj?F*Ke@wC zwB`CHI3}X!Jd)b`0stZf z0$9;Ay^}!#Ab*wmZ$sh{h{S(n5C3b93!R4y*ft!$_XCRI#a>|$9mX^7x~##wH=za4 z8?1{y+};tFBIs$LPb7hE_*~2mAMI-*q>K_Ww;w4rorr@m1i~>6NRm!GO0kLVjAyjh zi_4?s|F)NY9Apt{K1m7Z@>WtRpp$or(YjMZQ!=S<`F}!094rE1sI3qBb+CN9CTPH_ z8!!tm<%#3N26LNZFSW{8u*xH5Ee3D9VRJiFVDYZ_c8Dy;<+=T(x=PoB)}br(KJ}8% zQ(0hKWlqyXU0yE6x8R1)Je7W2rQW>-kZJdGm96Fb)aKp2F&*A+CaM(4>xrY=Z9#MC# zNPsOQd9UEreA93KWHUAtf-3?D610^_Yc1IwwdyFb$2+`$p7qTmgE}(}U*UCguEt6K z;M9t^D`hSdN^kLW$0~^vK8nm>%La$Y1bj*P(tlFbhduESYwXTiso+srG4v%qR?Lt0 z?g0WDi&4~&jqw33B|<_EhF2&ccRnPY92s-IiG-H7B=)a8N@~vWlJ-bTzu)+T=BPQs z;KwqhHjNY?I}qh?sN4p|cqxkBkmebFbRCaT??j_@x-AjWAlu1LislQ$=lDI}*Q`%? zSbvF~B%XxCo_%-=>V|@X%t^2Y00U=mX8^eZ4Fp)xGra*20Mu@wS$|0is8(#BDCzGP9tnu#UAPt@mO|~#pZbj% zMgC{v-^IDB%#>{d@rKiS^gX9)A0|U1er!``O^Gx zlU(L+y-WiM{gREg;|Nu$S$_k2OJHYc_#R76;fOWS_Ci8#Kp|{Fx#F^d)6S-@o+~fQao`Q&9NW# z;gqH7FDhzaiJ_-Ki%VaXXww!?xo8kJ6p>t{$?GrU6B(!*Lv0o8;(thSvGL-G>Ww@2 z-92bNqnEWW`J(??44IXKh@$4dIvyc|^JwoWIkwHxp+usmjs;?_OE^v7?RyfbbH(c` zH++sgmgRTwBgXiqZ#ZBa`{%P=>f-TUmd8O9ookgHN%=R*;A#H?I?$9=RxlV0M}xt3 zXgD6$e#n@cE6{alaDUqPY9w?&K}j7QnWH29^&%>%@a9s z;wG!;MjyRs$QG@FbXWY`aMyZ8@z1hZdHnFN^xDV(93hY!Q)$T3&E+nrmT1OV)1wK` bgR!)D>X*c@ECWu>WNzIJe|Eo7R~4TBiri#G z-Xy!+xSwNYoQYwAYg{F9bgb$-eul?J9!+5thkq|ka?pP^pgw<{I}t*qFDnN!x<7^! z$|=kNl|J9neb7)O)-f5;Xv0{iCmrGF`^2uK(@T)8QL5u9$kzFqh?Hzcx%$hfWSfvK zmAGkl7zmQ%FwB8j#IVdPM)Xy{hE%XCl`F)hw9LnB!R6?tI1}c{OC+W89RY0Msl!Q> z820g7BiF>sdoq75aoP~-x!XDlG^uyV_N3lt3I8*z2wGVD7e=JysmS;I+17;?@CAN-Dr;T0Sj?F*Ke@wC zwB`CHI3}X!Jd)b`0stZf z0$9;Ay^}!#Ab*wmZ$sh{h{S(n5C3b93!R4y*ft!$_XCRI#a>|$9mX^7x~##wH=za4 z8?1{y+};tFBIs$LPb7hE_*~2mAMI-*q>K_Ww;w4rorr@m1i~>6NRm!GO0kLVjAyjh zi_4?s|F)NY9Apt{K1m7Z@>WtRpp$or(YjMZQ!=S<`F}!094rE1sI3qBb+CN9CTPH_ z8!!tm<%#3N26LNZFSW{8u*xH5Ee3D9VRJiFVDYZ_c8Dy;<+=T(x=PoB)}br(KJ}8% zQ(0hKWlqyXU0yE6x8R1)Je7W2rQW>-kZJdGm96Fb)aKp2F&*A+CaM(4>xrY=Z9#MC# zNPsOQd9UEreA93KWHUAtf-3?D610^_Yc1IwwdyFb$2+`$p7qTmgE}(}U*UCguEt6K z;M9t^D`hSdN^kLW$0~^vK8nm>%La$Y1bj*P(tlFbhduESYwXTiso+srG4v%qR?Lt0 z?g0WDi&4~&jqw33B|<_EhF2&ccRnPY92s-IiG-H7B=)a8N@~vWlJ-bTzu)+T=BPQs z;KwqhHjNY?I}qh?sN4p|cqxkBkmebFbRCaT??j_@x-AjWAlu1LislQ$=lDI}*Q`%? zSbvF~B%XxCo_%-=>V|@X%t^2Y00U=mX8^eZ4Fp)xGra*20Mu@wS$|0is8(#BDCzGP9tnu#UAPt@mO|~#pZbj% zMgC{v-^IDB%#>{d@rKiS^gX9)A0|U1er!``O^Gx zlU(L+y-WiM{gREg;|Nu$S$_k2OJHYc_#R76;fOWS_Ci8#Kp|{Fx#F^d)6S-@o+~fQao`Q&9NW# z;gqH7FDhzaiJ_-Ki%VaXXww!?xo8kJ6p>t{$?GrU6B(!*Lv0o8;(thSvGL-G>Ww@2 z-92bNqnEWW`J(??44IXKh@$4dIvyc|^JwoWIkwHxp+usmjs;?_OE^v7?RyfbbH(c` zH++sgmgRTwBgXiqZ#ZBa`{%P=>f-TUmd8O9ookgHN%=R*;A#H?I?$9=RxlV0M}xt3 zXgD6$e#n@cE6{alaDUqPY9w?&K}j7QnWH29^&%>%@a9s z;wG!;MjyRs$QG@FbXWY`aMyZ8@z1hZdHnFN^xDV(93hY!Q)$T3&E+nrmT1OV)1wK` bgR!)D Date: Mon, 30 Mar 2020 21:30:49 +0200 Subject: [PATCH 10/15] Inglobated GPGDetachedSignatureVerifier into SignatureVerifier There is no point to have this abstract class. --- app/src/processing/app/Base.java | 2 +- .../GPGDetachedSignatureVerifierTest.java | 8 ++--- ...reVerifier.java => SignatureVerifier.java} | 32 ++++++++++++++++--- .../libraries/LibraryInstaller.java | 6 ++-- .../packages/ContributionInstaller.java | 2 +- .../src/processing/app/BaseNoGui.java | 4 +-- 6 files changed, 39 insertions(+), 15 deletions(-) rename arduino-core/src/cc/arduino/contributions/{GPGDetachedSignatureVerifier.java => SignatureVerifier.java} (82%) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 138101b025e..336487893c1 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -296,7 +296,7 @@ public Base(String[] args) throws Exception { pdeKeywords = new PdeKeywords(); pdeKeywords.reload(); - final GPGDetachedSignatureVerifier gpgDetachedSignatureVerifier = new GPGDetachedSignatureVerifier(); + final SignatureVerifier gpgDetachedSignatureVerifier = new SignatureVerifier(); contributionInstaller = new ContributionInstaller(BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier); libraryInstaller = new LibraryInstaller(BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier); diff --git a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java index d4219b9cdd6..36283d7011a 100644 --- a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java +++ b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java @@ -39,11 +39,11 @@ public class GPGDetachedSignatureVerifierTest { - private GPGDetachedSignatureVerifier GPGDetachedSignatureVerifier; + private SignatureVerifier verifier; @Before public void setUp() throws Exception { - GPGDetachedSignatureVerifier = new GPGDetachedSignatureVerifier(); + verifier = new SignatureVerifier(); } @Test @@ -51,7 +51,7 @@ public void testSignatureSuccessfulVerification() throws Exception { File signedFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json").getFile()); File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); - assertTrue(GPGDetachedSignatureVerifier.verify(signedFile, sign, publickKey)); + assertTrue(verifier.verify(signedFile, sign, publickKey)); } @Test @@ -60,6 +60,6 @@ public void testSignatureFailingVerification() throws Exception { fakeSignedFile.deleteOnExit(); File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); - assertFalse(GPGDetachedSignatureVerifier.verify(fakeSignedFile, sign, publickKey)); + assertFalse(verifier.verify(fakeSignedFile, sign, publickKey)); } } diff --git a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java similarity index 82% rename from arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java rename to arduino-core/src/cc/arduino/contributions/SignatureVerifier.java index ead276f4731..44b291ac4c4 100644 --- a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java +++ b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java @@ -34,22 +34,46 @@ import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import processing.app.BaseNoGui; + import java.io.*; import java.util.Iterator; -public class GPGDetachedSignatureVerifier extends SignatureVerifier { +public class SignatureVerifier { private String keyId; - public GPGDetachedSignatureVerifier() { + public SignatureVerifier() { this("7F294291"); } - public GPGDetachedSignatureVerifier(String keyId) { + public SignatureVerifier(String keyId) { this.keyId = keyId; } - @Override + public boolean isSigned(File indexFile) { + File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig"); + if (!signature.exists()) { + return false; + } + + try { + return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + } catch (Exception e) { + BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); + return false; + } + } + + public boolean isSigned(File indexFile, File signature) { + try { + return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + } catch (Exception e) { + BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); + return false; + } + } + protected boolean verify(File signedFile, File signature, File publicKey) throws IOException { FileInputStream signatureInputStream = null; FileInputStream signedFileInputStream = null; diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java index 3f00f909b0d..858734fd88b 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java @@ -31,7 +31,7 @@ import cc.arduino.Constants; import cc.arduino.contributions.DownloadableContributionsDownloader; -import cc.arduino.contributions.GPGDetachedSignatureVerifier; +import cc.arduino.contributions.SignatureVerifier; import cc.arduino.contributions.GZippedJsonDownloader; import cc.arduino.contributions.ProgressListener; import cc.arduino.utils.ArchiveExtractor; @@ -60,9 +60,9 @@ public class LibraryInstaller { private static Logger log = LogManager.getLogger(LibraryInstaller.class); private final Platform platform; - private final GPGDetachedSignatureVerifier signatureVerifier; + private final SignatureVerifier signatureVerifier; - public LibraryInstaller(Platform platform, GPGDetachedSignatureVerifier signatureVerifier) { + public LibraryInstaller(Platform platform, SignatureVerifier signatureVerifier) { this.platform = platform; this.signatureVerifier = signatureVerifier; } diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java index 2b6ff4cdea8..a58ee552691 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java @@ -32,8 +32,8 @@ import cc.arduino.Constants; import cc.arduino.contributions.DownloadableContribution; import cc.arduino.contributions.DownloadableContributionsDownloader; -import cc.arduino.contributions.ProgressListener; import cc.arduino.contributions.SignatureVerifier; +import cc.arduino.contributions.ProgressListener; import cc.arduino.filters.FileExecutablePredicate; import cc.arduino.utils.ArchiveExtractor; import cc.arduino.utils.MultiStepProgress; diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index c47a82d69b8..dc8bd7091a0 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -1,7 +1,7 @@ package processing.app; import cc.arduino.Constants; -import cc.arduino.contributions.GPGDetachedSignatureVerifier; +import cc.arduino.contributions.SignatureVerifier; import cc.arduino.contributions.VersionComparator; import cc.arduino.contributions.libraries.LibrariesIndexer; import cc.arduino.contributions.packages.ContributedPlatform; @@ -477,7 +477,7 @@ static public void initLogger() { static public void initPackages() throws Exception { indexer = new ContributionsIndexer(getSettingsFolder(), getHardwareFolder(), getPlatform(), - new GPGDetachedSignatureVerifier()); + new SignatureVerifier()); try { indexer.parseIndex(); From baea4b46856e3199dbd6c8b017cba1b7f387ac32 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Mar 2020 22:01:02 +0200 Subject: [PATCH 11/15] Greatly simplified SignatureVerifier and removed some resource-leaks --- .../GPGDetachedSignatureVerifierTest.java | 8 +- .../contributions/SignatureVerifier.java | 104 ++++++++---------- 2 files changed, 50 insertions(+), 62 deletions(-) diff --git a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java index 36283d7011a..69f916046d2 100644 --- a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java +++ b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java @@ -44,14 +44,15 @@ public class GPGDetachedSignatureVerifierTest { @Before public void setUp() throws Exception { verifier = new SignatureVerifier(); + File keyRingFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); + verifier.setKeyRingFile(keyRingFile); } @Test public void testSignatureSuccessfulVerification() throws Exception { File signedFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json").getFile()); File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); - File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); - assertTrue(verifier.verify(signedFile, sign, publickKey)); + assertTrue(verifier.verify(signedFile, sign)); } @Test @@ -59,7 +60,6 @@ public void testSignatureFailingVerification() throws Exception { File fakeSignedFile = File.createTempFile("fakeSigned", "txt"); fakeSignedFile.deleteOnExit(); File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); - File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); - assertFalse(verifier.verify(fakeSignedFile, sign, publickKey)); + assertFalse(verifier.verify(fakeSignedFile, sign)); } } diff --git a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java index 44b291ac4c4..ada5a7f30d8 100644 --- a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java +++ b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java @@ -29,26 +29,34 @@ package cc.arduino.contributions; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + import org.apache.commons.compress.utils.IOUtils; -import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; import processing.app.BaseNoGui; -import java.io.*; -import java.util.Iterator; - public class SignatureVerifier { - private String keyId; + private File keyRingFile; public SignatureVerifier() { - this("7F294291"); + keyRingFile = new File(BaseNoGui.getContentFile("lib"), "public.gpg.key"); } - public SignatureVerifier(String keyId) { - this.keyId = keyId; + public void setKeyRingFile(File keyRingFile) { + this.keyRingFile = keyRingFile; } public boolean isSigned(File indexFile) { @@ -58,7 +66,7 @@ public boolean isSigned(File indexFile) { } try { - return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + return verify(indexFile, signature); } catch (Exception e) { BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); return false; @@ -67,76 +75,56 @@ public boolean isSigned(File indexFile) { public boolean isSigned(File indexFile, File signature) { try { - return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + return verify(indexFile, signature); } catch (Exception e) { BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); return false; } } - protected boolean verify(File signedFile, File signature, File publicKey) throws IOException { - FileInputStream signatureInputStream = null; - FileInputStream signedFileInputStream = null; + protected boolean verify(File signedFile, File signatureFile) throws IOException { try { - signatureInputStream = new FileInputStream(signature); - PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(signatureInputStream, new BcKeyFingerprintCalculator()); - - Object nextObject; - try { - nextObject = pgpObjectFactory.nextObject(); - if (!(nextObject instanceof PGPSignatureList)) { + // Read signature from signatureFile + PGPSignature signature; + try (FileInputStream in = new FileInputStream(signatureFile)) { + PGPObjectFactory objFactory = new PGPObjectFactory(in, new BcKeyFingerprintCalculator()); + Object obj = objFactory.nextObject(); + if (!(obj instanceof PGPSignatureList)) { + return false; + } + PGPSignatureList signatureList = (PGPSignatureList) obj; + if (signatureList.size() != 1) { return false; } - } catch (IOException e) { + signature = signatureList.get(0); + } catch (Exception e) { return false; } - PGPSignatureList pgpSignatureList = (PGPSignatureList) nextObject; - assert pgpSignatureList.size() == 1; - PGPSignature pgpSignature = pgpSignatureList.get(0); - - PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId); - pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey); - signedFileInputStream = new FileInputStream(signedFile); - pgpSignature.update(IOUtils.toByteArray(signedFileInputStream)); + // Extract public key from keyring + PGPPublicKey pgpPublicKey = readPublicKey(signature.getKeyID()); - return pgpSignature.verify(); + // Check signature + signature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey); + try (FileInputStream in = new FileInputStream(signedFile)) { + signature.update(IOUtils.toByteArray(in)); + return signature.verify(); + } } catch (PGPException e) { throw new IOException(e); - } finally { - IOUtils.closeQuietly(signatureInputStream); - IOUtils.closeQuietly(signedFileInputStream); } } - private PGPPublicKey readPublicKey(File file, String id) throws IOException, PGPException { - InputStream keyIn = null; - try { - keyIn = new BufferedInputStream(new FileInputStream(file)); - return readPublicKey(keyIn, id); - } finally { - IOUtils.closeQuietly(keyIn); - } - } - - private PGPPublicKey readPublicKey(InputStream input, String id) throws IOException, PGPException { - PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator()); - - Iterator keyRingIter = pgpPub.getKeyRings(); - while (keyRingIter.hasNext()) { - PGPPublicKeyRing keyRing = keyRingIter.next(); + private PGPPublicKey readPublicKey(long id) throws IOException, PGPException { + try (InputStream in = PGPUtil.getDecoderStream(new FileInputStream(keyRingFile))) { + PGPPublicKeyRingCollection pubRing = new PGPPublicKeyRingCollection(in, new BcKeyFingerprintCalculator()); - Iterator keyIter = keyRing.getPublicKeys(); - while (keyIter.hasNext()) { - PGPPublicKey key = keyIter.next(); - - if (Long.toHexString(key.getKeyID()).toUpperCase().endsWith(id)) { - return key; - } + PGPPublicKey publicKey = pubRing.getPublicKey(id); + if (publicKey == null) { + throw new IllegalArgumentException("Can't find public key in key ring."); } + return publicKey; } - - throw new IllegalArgumentException("Can't find encryption key in key ring."); } } From 2889410a3d04bf7b3feab17f18828ffc1fe9b354 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Mar 2020 22:02:38 +0200 Subject: [PATCH 12/15] Rename test GPGDetachedSignatureVerifierTest -> SignatureVerifierTest --- ...ureVerifierTest.java => SignatureVerifierTest.java} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename app/test/cc/arduino/contributions/{GPGDetachedSignatureVerifierTest.java => SignatureVerifierTest.java} (80%) diff --git a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/contributions/SignatureVerifierTest.java similarity index 80% rename from app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java rename to app/test/cc/arduino/contributions/SignatureVerifierTest.java index 69f916046d2..00a3b8d80d4 100644 --- a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java +++ b/app/test/cc/arduino/contributions/SignatureVerifierTest.java @@ -37,21 +37,21 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -public class GPGDetachedSignatureVerifierTest { +public class SignatureVerifierTest { private SignatureVerifier verifier; @Before public void setUp() throws Exception { verifier = new SignatureVerifier(); - File keyRingFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); + File keyRingFile = new File(SignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile()); verifier.setKeyRingFile(keyRingFile); } @Test public void testSignatureSuccessfulVerification() throws Exception { - File signedFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json").getFile()); - File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); + File signedFile = new File(SignatureVerifierTest.class.getResource("./package_index.json").getFile()); + File sign = new File(SignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); assertTrue(verifier.verify(signedFile, sign)); } @@ -59,7 +59,7 @@ public void testSignatureSuccessfulVerification() throws Exception { public void testSignatureFailingVerification() throws Exception { File fakeSignedFile = File.createTempFile("fakeSigned", "txt"); fakeSignedFile.deleteOnExit(); - File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); + File sign = new File(SignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); assertFalse(verifier.verify(fakeSignedFile, sign)); } } From 764b5865259908df02c3396e4d7113be0bff7eb7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 30 Mar 2020 23:57:52 +0200 Subject: [PATCH 13/15] Added PGP clearsign verifier --- .../contributions/SignatureVerifierTest.java | 28 ++++++-- .../contributions/escaped-text.txt.asc | 29 ++++++++ .../contributions/no-blank-lines.txt.asc | 20 ++++++ .../contributions/one-blank-line.txt.asc | 21 ++++++ .../cc/arduino/contributions/text.txt.asc | 21 ++++++ .../contributions/two-blank-lines.txt.asc | 22 ++++++ .../arduino/contributions/wrong-text.txt.asc | 21 ++++++ .../contributions/SignatureVerifier.java | 69 +++++++++++++++++++ 8 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 app/test/cc/arduino/contributions/escaped-text.txt.asc create mode 100644 app/test/cc/arduino/contributions/no-blank-lines.txt.asc create mode 100644 app/test/cc/arduino/contributions/one-blank-line.txt.asc create mode 100644 app/test/cc/arduino/contributions/text.txt.asc create mode 100644 app/test/cc/arduino/contributions/two-blank-lines.txt.asc create mode 100644 app/test/cc/arduino/contributions/wrong-text.txt.asc diff --git a/app/test/cc/arduino/contributions/SignatureVerifierTest.java b/app/test/cc/arduino/contributions/SignatureVerifierTest.java index 00a3b8d80d4..d99c5e15494 100644 --- a/app/test/cc/arduino/contributions/SignatureVerifierTest.java +++ b/app/test/cc/arduino/contributions/SignatureVerifierTest.java @@ -29,13 +29,13 @@ package cc.arduino.contributions; -import org.junit.Before; -import org.junit.Test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.File; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; public class SignatureVerifierTest { @@ -62,4 +62,24 @@ public void testSignatureFailingVerification() throws Exception { File sign = new File(SignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); assertFalse(verifier.verify(fakeSignedFile, sign)); } + + @Test + public void testClearTextSignatureVerification() throws Exception { + File signedFile = new File(SignatureVerifierTest.class.getResource("./text.txt.asc").getFile()); + assertTrue(verifier.verifyCleartextSignature(signedFile)); + File signedFile2 = new File(SignatureVerifierTest.class.getResource("./escaped-text.txt.asc").getFile()); + assertTrue(verifier.verifyCleartextSignature(signedFile2)); + File oneBlankLines = new File(SignatureVerifierTest.class.getResource("./one-blank-line.txt.asc").getFile()); + assertTrue(verifier.verifyCleartextSignature(oneBlankLines)); + File twoBlankLines = new File(SignatureVerifierTest.class.getResource("./two-blank-lines.txt.asc").getFile()); + assertTrue(verifier.verifyCleartextSignature(twoBlankLines)); + File noBlankLines = new File(SignatureVerifierTest.class.getResource("./no-blank-lines.txt.asc").getFile()); + assertTrue(verifier.verifyCleartextSignature(noBlankLines)); + } + + @Test + public void testClearTextSignatureFailingVerification() throws Exception { + File signedFile = new File(SignatureVerifierTest.class.getResource("./wrong-text.txt.asc").getFile()); + assertFalse(verifier.verifyCleartextSignature(signedFile)); + } } diff --git a/app/test/cc/arduino/contributions/escaped-text.txt.asc b/app/test/cc/arduino/contributions/escaped-text.txt.asc new file mode 100644 index 00000000000..9c89d0720c4 --- /dev/null +++ b/app/test/cc/arduino/contributions/escaped-text.txt.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +- - +- - +- - abc +- -- abc +helllo!!!! + + + + + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6CcaUACgkQlfpvQ+IR +iMQD9w/+KA2ZusmFnpt7dXTy/6ohNCijMp3pFSVsBzGWBDpGJjYfwek2N8HssXIa +Tq0uzCiTm1Z4kn+SWjIHxKzdihUi9mwkMCqdhNTZN0+FKXc+c358KUYXOlyigOmX +256bG7Ep2P/3ZBzhvVC5WKLIwYhq6cj9fSnt26XP02qt/9ztczGJHDpEfIhPA0LI +PYnUUo4KftQzHp41EPENIyLTkT1YzhypnIHCv2mG8qql+W9blx1eO8gIUGmzQQ0y +CnTY0AIvmrAGd5WQWwKpKy5aLpWDmIW/zSSoDc7jC74i2V6n5Y+Fqq++SVDvIApd +yP+BmL6pDfZf9cV8nDQOI9Jd+/JT3tUct5js9lDhj74g1ZGobx06kZPv/ojbSE42 +jJi75HQ3NDG8dBDMtYzrDeO0QhcpT94LNTdZ8IdR5jCf3I9SkpHB6sb27elVgcBb +stXLRhrf0se2U2pI3CGDkjumm04cDOhY9tLt2CHMlL9yl9LZejyT3xUoLGAy1Ird +Jw6knZM5O/bWzFq1bxlqgz6EspafNy+ZM8/1s+b9ecxKkds7UEZLpOVd+QYMO6i1 +lxm4ZQqRoTiIcVxzhwChd41zxPw9bMNq03prBMnetJ9tvb76qgGcCvRh/ANYWBfa +2zeH3UGaOyMB79oKOSw0qzXkFA+0qXzROObVWTzjGOPZJtkfaA8= +=BqjQ +-----END PGP SIGNATURE----- diff --git a/app/test/cc/arduino/contributions/no-blank-lines.txt.asc b/app/test/cc/arduino/contributions/no-blank-lines.txt.asc new file mode 100644 index 00000000000..9b5e995b022 --- /dev/null +++ b/app/test/cc/arduino/contributions/no-blank-lines.txt.asc @@ -0,0 +1,20 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +hello +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6C/AsACgkQlfpvQ+IR +iMQYUA//SEHkb60xiXyP8TfTfxiJORr7ltvLtL7N9IwpnrkeKdvvrxC71O5U4kdf +Ek5XUALXUPArWJHByKBZ3saQapRPMUNbsLDG9QqtIffORqgPnfPAgMiGYceIaT1o +QChspIQRL6xt6k8c6o2M5R7CBMjiDqaldpJHsq3kcjcgHBmJFPdDdhjA2D9s4mCo +nymy3PqLcZ4RARrED61VlF4hcVHv5BHPnDZDXug3oGiR2EQTpdQgvBg/rL2xiVHl +MgZ6/5owF6omeNM30JNPQfNdDnV2jDhxILfjY2Rqyj9zClrnZB6qox6h1RoSKIFy +UkBUAs3WapZMqR2UKq3K7dL8cqUKoOFdSpd/R2T4ZBC/6exiwVnbDGa161ur8f4/ +55RxAe/VE+75bC7HI3jNAUn7xGMUGBWgTKh+xTnyh50BHifmCIzyLOgRWds/rvMH +PT0fmtbTWhjrJOfuhi15uSJ0Lvq/XuM/z/aIgeZHaIBnIxvRYUOdP6Rz1xKHmc2q ++puI2FxpbENUyt8TNlrnHJeq72UubVIRJ08CE2iiuWjP+1jFxPYA1tWBYrqtbK7y +2ZPq5c+vy8LMij/SfZkeOa388Ss3lXr5CTJ+O2VMLellyCdUd0G47T26umCr6zaF +jX1huU0rfA+vGLVfr1Z2nexX0r7kwebkz/PWIO1+Kc41gBY+cek= +=Rejh +-----END PGP SIGNATURE----- diff --git a/app/test/cc/arduino/contributions/one-blank-line.txt.asc b/app/test/cc/arduino/contributions/one-blank-line.txt.asc new file mode 100644 index 00000000000..7a48cf72987 --- /dev/null +++ b/app/test/cc/arduino/contributions/one-blank-line.txt.asc @@ -0,0 +1,21 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +hello + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6C/E8ACgkQlfpvQ+IR +iMS/iA/+KxhaOTokmXTKgSkZwaCdPwTIdcfIVWMsK6mxXcSeWtmQpCxMyH3sudaA +MvVg8qMHTTiYbFD5TMBF79Eu3nJuLemnm7WbpQMAQ7/Ay8qDrH6MIIqeR6TLa6CO +VQw2a9H3AoqVxBtH0y6vVk+mOnr8NnZA3XoT0B2TuLApqY4uFt9quHBkrycHdXve +6++EyiYeDgY6oG1bAR0S/peFhCL45CId9xJe5RIHT7aFoDKw78LPcfoTLdKCtzzE +CdX1IpzP/tOVlv0LTNPWQQeRQ07BKpquQnQbzuAgVFbLf27kI2e+AxjIdmozEjO9 +rk9Za0tpKYCYT5lF9zfmaCWzkhTfrjB8OPXWYHjozzv4aUhfvgQ+TF9dynWU4ct8 +ACRkb+bp+ENZVy0/1VdDDs3wrFnMHuZyrBWXNI8sBu6f1JSG4ddgy7NfPveOJEdG +wg/4mxzSnBQ7jogRaJLioC+mjxulmN1FCoDMwphOLkmaIX1qIL3OxkEBfSzpfkfe +wAYhjSqLHHXwx0W0resDahC2L81gBnTbf0yP1jEWjnSs96AqwvLSAj0gyC4BtVoN +LSgYDFeNitoBMDx8osi0KjLydOzbQW9L5u//Rm/Q/8/p7+4u/X3X8EWHULHIsrRW +T9M6jun8zTr9OLAk9W1T76Yu2Wyu+ps6f6iAeSlvqTTpr3+j2W4= +=0JE+ +-----END PGP SIGNATURE----- diff --git a/app/test/cc/arduino/contributions/text.txt.asc b/app/test/cc/arduino/contributions/text.txt.asc new file mode 100644 index 00000000000..23b47f73ba3 --- /dev/null +++ b/app/test/cc/arduino/contributions/text.txt.asc @@ -0,0 +1,21 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +helllo!!!! + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6CPtwACgkQlfpvQ+IR +iMS8AA//dCIbYCPwTdOFf3YRFO2Z7YSgAgEpctcH1UW9IaZpGlfZDqiskgNT5zWx +ooW0EqVagyyY+1Fips4a52/urQmbQ+0RIU6r4zDUvyMoN/f41jFKd1eY365oqORo +G5avMFhZ7PwbYXKhDeVQrDCRSYSvWYyRXH97VpFvo/SSuvB7KWvlTFFK9rt1xysx +rjikCqMWuPeReSclLCMCGQGjcJExNsu7E9l55NcJMOP8a3yVlY9b1LwH5nuVXOfw +UvSzHK2K16O3COb0otWN8VZHB0x2y3Y1boIF2J/Wt9zBaB/d4cmacwL4KLHiw7KR +q5YzDfEpmwMM9S5QLsLPYBWr9B1XSQ+PrylPSha1NnDStr/RkFtwVsags3igJu5E +Ye+aJra6D/VREFc/IQYsEmcDHYdKp3CNJn/BdNwTDI4BIdj4lBDd6lVrcL+Id8pl +ivdk1bV/575eTH9cDf65aF/lmd/z6dGNLcke4N76n8g/xjAuSkN7FDm91KGv9oCJ +e0eO0+GDJbfMHz61vQ1DPfUWqyQLC3z9OeX+bUvqSb1Xy9i2OI0FlY3GMtwDONto +qShjcbeXXYt74bINsAEV78pSoYPHc2rYco1/qrAL1bYY9Hngy3FvgD7mVNd7nC/J +buFQ4Ek07urJpA9lw8o/z49O7iGisc8UXuPeHY2z7LmUuCvKpKI= +=DKVh +-----END PGP SIGNATURE----- diff --git a/app/test/cc/arduino/contributions/two-blank-lines.txt.asc b/app/test/cc/arduino/contributions/two-blank-lines.txt.asc new file mode 100644 index 00000000000..ec8f2f5b18c --- /dev/null +++ b/app/test/cc/arduino/contributions/two-blank-lines.txt.asc @@ -0,0 +1,22 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +hello + + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6C/H8ACgkQlfpvQ+IR +iMQD9RAAk34n7WegdXdreKLU5nAkx9/lcPwZYRcwTeV6pgroxQ3XuC3oEqUwDQtD +RLT+Gc+1j7XMGxTIJtHhpwpJk2IeT5x/lfqpMwpHoJUEOA/QZ7YtUTUj5aqc3bJV +QEWXrNyceqaYkUguR7gk0ahtniTuaj9WaixRpxiu9+AMht74g0iZm99h2fpgiIb1 +7M2SdMbmyW1mK7BFN9Cghz/8NedV6TeKpQnWWpN+hdO1fsjGIVRNRfTMod7gL6QK +KhPa3eQW+yxlEshKZwQJwIe3vcgquK6bAl/p20EU7+2ytnFd3zBhcCFJtC6fV+zF +s89BOAoAJPRaBKbQp01IsBB5mNZUxKFOa+e94tFLmaaI7KhHB7oQ7Qnx+hTgq7bG +9PRza0bHMkBPumACM8FNjOlzJNY5eTnfDcAq2kgaAdoX5LFLrIiYUNtz9gmIIHNj +xekG9LB3WUv013jPSOUnbmGkch0VvVgFvcsOtGWTHp+VlZLOWZjXkO+Cco2qWcVr +F7ckPTXs+nzoOTVsYZLfN8g92y2sT548+Q9bCtT1QStGCSTyNF1o0JW9Ih1hsSkn +qJVgo8wGO45mTT4GJ6VWUkFkAjoaZM14F70CJp0eLw5PSxDIkSsmkL7CYwGeA+6N +UtKIJ1ZoFMElW5bMuSayLEjtC9RkGILjplkiFY+h7gJd/vitTvI= +=g/9c +-----END PGP SIGNATURE----- diff --git a/app/test/cc/arduino/contributions/wrong-text.txt.asc b/app/test/cc/arduino/contributions/wrong-text.txt.asc new file mode 100644 index 00000000000..de08872c6be --- /dev/null +++ b/app/test/cc/arduino/contributions/wrong-text.txt.asc @@ -0,0 +1,21 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +!!helllo!!!! + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6CPtwACgkQlfpvQ+IR +iMS8AA//dCIbYCPwTdOFf3YRFO2Z7YSgAgEpctcH1UW9IaZpGlfZDqiskgNT5zWx +ooW0EqVagyyY+1Fips4a52/urQmbQ+0RIU6r4zDUvyMoN/f41jFKd1eY365oqORo +G5avMFhZ7PwbYXKhDeVQrDCRSYSvWYyRXH97VpFvo/SSuvB7KWvlTFFK9rt1xysx +rjikCqMWuPeReSclLCMCGQGjcJExNsu7E9l55NcJMOP8a3yVlY9b1LwH5nuVXOfw +UvSzHK2K16O3COb0otWN8VZHB0x2y3Y1boIF2J/Wt9zBaB/d4cmacwL4KLHiw7KR +q5YzDfEpmwMM9S5QLsLPYBWr9B1XSQ+PrylPSha1NnDStr/RkFtwVsags3igJu5E +Ye+aJra6D/VREFc/IQYsEmcDHYdKp3CNJn/BdNwTDI4BIdj4lBDd6lVrcL+Id8pl +ivdk1bV/575eTH9cDf65aF/lmd/z6dGNLcke4N76n8g/xjAuSkN7FDm91KGv9oCJ +e0eO0+GDJbfMHz61vQ1DPfUWqyQLC3z9OeX+bUvqSb1Xy9i2OI0FlY3GMtwDONto +qShjcbeXXYt74bINsAEV78pSoYPHc2rYco1/qrAL1bYY9Hngy3FvgD7mVNd7nC/J +buFQ4Ek07urJpA9lw8o/z49O7iGisc8UXuPeHY2z7LmUuCvKpKI= +=DKVh +-----END PGP SIGNATURE----- diff --git a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java index ada5a7f30d8..940e38666c1 100644 --- a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java +++ b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java @@ -31,10 +31,12 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import org.apache.commons.compress.utils.IOUtils; +import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPPublicKey; @@ -42,6 +44,7 @@ import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureList; import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; @@ -127,4 +130,70 @@ private PGPPublicKey readPublicKey(long id) throws IOException, PGPException { } } + public String[] extractTextFromCleartextSignature(File inFile) throws FileNotFoundException, IOException { + try (ArmoredInputStream in = new ArmoredInputStream(new FileInputStream(inFile))) { + return extractTextFromCleartextSignature(in); + } + } + + public boolean verifyCleartextSignature(File inFile) { + try (ArmoredInputStream in = new ArmoredInputStream(new FileInputStream(inFile))) { + String[] clearTextLines = extractTextFromCleartextSignature(in); + int clearTextSize = clearTextLines.length; + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(in); + PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject(); + PGPSignature sig = p3.get(0); + PGPPublicKey publicKey = readPublicKey(sig.getKeyID()); + + sig.init(new BcPGPContentVerifierBuilderProvider(), publicKey); + for (int i = 0; i < clearTextSize; i++) { + sig.update(clearTextLines[i].getBytes()); + if (i + 1 < clearTextSize) { + // https://tools.ietf.org/html/rfc4880#section-7 + // Convert all line endings to '\r\n' + sig.update((byte) '\r'); + sig.update((byte) '\n'); + } + } + return sig.verify(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + private String[] extractTextFromCleartextSignature(ArmoredInputStream in) throws FileNotFoundException, IOException { + // https://tools.ietf.org/html/rfc4880#section-7 + // ArmoredInputStream does unescape dash-escaped string in armored text and skips + // all headers. To calculate the signature we still need to: + // 1. handle different line endings \n or \n\r or \r\n + // 2. remove trailing whitespaces from each line (' ' and '\t') + // 3. remove the latest line ending + + String clearText = ""; + for (;;) { + int c = in.read(); + // in.isClearText() refers to the PREVIOUS byte read + if (c == -1 || !in.isClearText()) { + break; + } + // 1. convert all line endings to '\r\n' + if (c == '\r') { + continue; + } + clearText += (char) c; + } + + // 3. remove the latest line ending + if (clearText.endsWith("\n")) { + clearText = clearText.substring(0, clearText.length() - 1); + } + String[] lines = clearText.split("\n", -1); + for (int i = 0; i < lines.length; i++) { + // 2. remove trailing whitespaces from each line (' ' and '\t') + lines[i] = lines[i].replaceAll("[ \\t]+$", ""); + } + return lines; + } } From b0ebd7907084b4658e499cb828e9c865d41c16d0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 31 Mar 2020 13:58:59 +0200 Subject: [PATCH 14/15] When updating splash image check for valid signature --- app/src/processing/app/UpdateCheck.java | 51 ++++++++++++++++++------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index aed7ae42b76..49a74c4fee6 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -42,6 +42,8 @@ import org.apache.commons.compress.utils.IOUtils; +import cc.arduino.contributions.SignatureVerifier; +import cc.arduino.utils.FileHash; import processing.app.legacy.PApplet; @@ -125,27 +127,48 @@ public void run() { //System.err.println("Error while trying to check for an update."); } + File tmp = null; try { + tmp = File.createTempFile("arduino_splash_update", ".txt.asc"); // Check for updates of the splash screen - List lines = readFileFromURL("https://go.bug.st/latest_splash.txt"); - if (lines.size() > 0) { - // if the splash image has been changed download the new file - String newSplashUrl = lines.get(0); - String oldSplashUrl = PreferencesData.get("splash.imageurl"); - if (!newSplashUrl.equals(oldSplashUrl)) { - File tmpFile = BaseNoGui.getSettingsFile("splash.png.tmp"); - downloadFileFromURL(newSplashUrl, tmpFile); - File destFile = BaseNoGui.getSettingsFile("splash.png"); - Files.move(tmpFile.toPath(), destFile.toPath(), - StandardCopyOption.REPLACE_EXISTING); - PreferencesData.set("splash.imageurl", newSplashUrl); + downloadFileFromURL("https://go.bug.st/latest_splash.txt.asc", tmp); + SignatureVerifier verifier = new SignatureVerifier(); + if (!verifier.verifyCleartextSignature(tmp)) { + throw new Exception("Invalid signature"); + } + String[] lines = verifier.extractTextFromCleartextSignature(tmp); + if (lines.length < 2) { + throw new Exception("Invalid splash image update"); + } + String newSplashUrl = lines[0]; + String checksum = lines[1]; + + // if the splash image has been changed download the new file + String oldSplashUrl = PreferencesData.get("splash.imageurl"); + if (!newSplashUrl.equals(oldSplashUrl)) { + File tmpFile = BaseNoGui.getSettingsFile("splash.png.tmp"); + downloadFileFromURL(newSplashUrl, tmpFile); + + String algo = checksum.split(":")[0]; + String crc = FileHash.hash(tmpFile, algo); + if (!crc.equalsIgnoreCase(checksum)) { + throw new Exception("Invalid splash image checksum"); } - // extend expiration by 24h - PreferencesData.setLong("splash.expire", now + ONE_DAY); + File destFile = BaseNoGui.getSettingsFile("splash.png"); + Files.move(tmpFile.toPath(), destFile.toPath(), + StandardCopyOption.REPLACE_EXISTING); + PreferencesData.set("splash.imageurl", newSplashUrl); } + + // extend expiration by 24h + PreferencesData.setLong("splash.expire", now + ONE_DAY); } catch (Exception e) { // e.printStackTrace(); + } finally { + if (tmp != null) { + tmp.delete(); + } } } From d380b6ad8e8d404ae8e333136a29abff79c5939c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 31 Mar 2020 14:12:06 +0200 Subject: [PATCH 15/15] Slightly refactored update check method Makes linter happy and improves readability --- app/src/processing/app/UpdateCheck.java | 100 +++++++++++++----------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 49a74c4fee6..ec303d424ba 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -69,17 +69,71 @@ public UpdateCheck(Base base) { thread.start(); } + final long ONE_DAY = 24 * 60 * 60 * 1000; + public void run() { // Ensure updates-check are made only once per day Long when = PreferencesData.getLong("update.last"); long now = System.currentTimeMillis(); - final long ONE_DAY = 24 * 60 * 60 * 1000; if (when != null && (now - when) < ONE_DAY) { // don't annoy the shit outta people return; } PreferencesData.setLong("update.last", now); + checkForIDEUpdates(); + + checkForSplashImageUpdates(); + } + + private void checkForSplashImageUpdates() { + File tmp = null; + try { + tmp = File.createTempFile("arduino_splash_update", ".txt.asc"); + // Check for updates of the splash screen + downloadFileFromURL("https://go.bug.st/latest_splash.txt.asc", tmp); + SignatureVerifier verifier = new SignatureVerifier(); + if (!verifier.verifyCleartextSignature(tmp)) { + return; + } + String[] lines = verifier.extractTextFromCleartextSignature(tmp); + if (lines.length < 2) { + return; + } + String newSplashUrl = lines[0]; + String checksum = lines[1]; + + // if the splash image has been changed download the new file + String oldSplashUrl = PreferencesData.get("splash.imageurl"); + if (!newSplashUrl.equals(oldSplashUrl)) { + File tmpFile = BaseNoGui.getSettingsFile("splash.png.tmp"); + downloadFileFromURL(newSplashUrl, tmpFile); + + String algo = checksum.split(":")[0]; + String crc = FileHash.hash(tmpFile, algo); + if (!crc.equalsIgnoreCase(checksum)) { + return; + } + + File destFile = BaseNoGui.getSettingsFile("splash.png"); + Files.move(tmpFile.toPath(), destFile.toPath(), + StandardCopyOption.REPLACE_EXISTING); + PreferencesData.set("splash.imageurl", newSplashUrl); + } + + // extend expiration by 24h + long now = System.currentTimeMillis(); + PreferencesData.setLong("splash.expire", now + ONE_DAY); + } catch (Exception e) { + // e.printStackTrace(); + } finally { + if (tmp != null) { + tmp.delete(); + } + } + } + + private void checkForIDEUpdates() { // Set update id Long id = PreferencesData.getLong("update.id"); if (id == null) { @@ -126,50 +180,6 @@ public void run() { //e.printStackTrace(); //System.err.println("Error while trying to check for an update."); } - - File tmp = null; - try { - tmp = File.createTempFile("arduino_splash_update", ".txt.asc"); - // Check for updates of the splash screen - downloadFileFromURL("https://go.bug.st/latest_splash.txt.asc", tmp); - SignatureVerifier verifier = new SignatureVerifier(); - if (!verifier.verifyCleartextSignature(tmp)) { - throw new Exception("Invalid signature"); - } - String[] lines = verifier.extractTextFromCleartextSignature(tmp); - if (lines.length < 2) { - throw new Exception("Invalid splash image update"); - } - String newSplashUrl = lines[0]; - String checksum = lines[1]; - - // if the splash image has been changed download the new file - String oldSplashUrl = PreferencesData.get("splash.imageurl"); - if (!newSplashUrl.equals(oldSplashUrl)) { - File tmpFile = BaseNoGui.getSettingsFile("splash.png.tmp"); - downloadFileFromURL(newSplashUrl, tmpFile); - - String algo = checksum.split(":")[0]; - String crc = FileHash.hash(tmpFile, algo); - if (!crc.equalsIgnoreCase(checksum)) { - throw new Exception("Invalid splash image checksum"); - } - - File destFile = BaseNoGui.getSettingsFile("splash.png"); - Files.move(tmpFile.toPath(), destFile.toPath(), - StandardCopyOption.REPLACE_EXISTING); - PreferencesData.set("splash.imageurl", newSplashUrl); - } - - // extend expiration by 24h - PreferencesData.setLong("splash.expire", now + ONE_DAY); - } catch (Exception e) { - // e.printStackTrace(); - } finally { - if (tmp != null) { - tmp.delete(); - } - } } public static File getUpdatedSplashImageFile() {