1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-01-31 20:52:13 +01:00

Split download and check signature, add check signature to library index

This commit is contained in:
Mattia Bertorello 2019-07-03 15:26:57 +02:00
parent 2596ecee28
commit 5157688590
No known key found for this signature in database
GPG Key ID: CE1FB2BE91770F24
3 changed files with 81 additions and 47 deletions

View File

@ -33,20 +33,22 @@ import cc.arduino.utils.FileHash;
import cc.arduino.utils.MultiStepProgress; import cc.arduino.utils.MultiStepProgress;
import cc.arduino.utils.Progress; import cc.arduino.utils.Progress;
import cc.arduino.utils.network.FileDownloader; import cc.arduino.utils.network.FileDownloader;
import org.slf4j.Logger; import org.apache.logging.log4j.LogManager;
import org.slf4j.LoggerFactory; import org.apache.logging.log4j.Logger;
import processing.app.BaseNoGui; import processing.app.BaseNoGui;
import processing.app.PreferencesData;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.nio.file.*; import java.nio.file.*;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import static processing.app.I18n.format; import static processing.app.I18n.format;
import static processing.app.I18n.tr; import static processing.app.I18n.tr;
public class DownloadableContributionsDownloader { public class DownloadableContributionsDownloader {
private static Logger log = LoggerFactory.getLogger(DownloadableContributionsDownloader.class); private static Logger log = LogManager.getLogger(DownloadableContributionsDownloader.class);
private final File stagingFolder; private final File stagingFolder;
@ -147,55 +149,71 @@ public class DownloadableContributionsDownloader {
// Extract the file name from the url // Extract the file name from the url
URL packageIndexUrl = new URL(packageIndexUrlString); URL packageIndexUrl = new URL(packageIndexUrlString);
URL packageIndexSignatureUrl = new URL(packageIndexUrlString + ".sig");
String[] urlPathParts = packageIndexUrl.getFile().split("/"); String[] urlPathParts = packageIndexUrl.getFile().split("/");
File packageIndex = BaseNoGui.indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]); File packageIndex = BaseNoGui.indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]);
// Signature file name
File packageIndexSignature = BaseNoGui.indexer.getIndexFile(urlPathParts[urlPathParts.length - 1] + ".sig");
final String statusText = tr("Downloading platforms index..."); final String statusText = tr("Downloading platforms index...");
downloadedFilesAccumulator.add(packageIndex.getName()); downloadedFilesAccumulator.add(packageIndex.getName());
// Create temp files // Create temp files
File packageIndexTemp = File.createTempFile(packageIndexUrl.getPath(), ".tmp"); File packageIndexTemp = File.createTempFile(packageIndexUrl.getPath(), ".tmp");
File packageIndexSignatureTemp = File.createTempFile(packageIndexSignatureUrl.getPath(), ".tmp");
try { try {
// Download package index // Download package index
download(packageIndexUrl, packageIndexTemp, progress, statusText, progressListener, true); download(packageIndexUrl, packageIndexTemp, progress, statusText, progressListener, true);
try { final List<String> domain = new LinkedList<>(PreferencesData.getCollection("http.signature_verify_domains"));
// Download signature // Default domain
download(packageIndexSignatureUrl, packageIndexSignatureTemp, progress, statusText, progressListener, true); domain.add("downloads.arduino.cc");
// Verify the signature before move the files if (domain.contains(packageIndexUrl.getHost())) {
boolean signatureVerified = signatureVerifier.isSigned(packageIndexTemp, packageIndexSignatureTemp); URL signatureUrl = new URL(packageIndexUrl.toString() + ".sig");
if (signatureVerified) {
// Move if the signature is ok if (checkSignature(progress, downloadedFilesAccumulator, signatureUrl, progressListener, signatureVerifier, statusText, packageIndexTemp)) {
Files.move(packageIndexTemp.toPath(), packageIndex.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.move(packageIndexTemp.toPath(), packageIndex.toPath(), StandardCopyOption.REPLACE_EXISTING);
Files.move(packageIndexSignatureTemp.toPath(), packageIndexSignature.toPath(), StandardCopyOption.REPLACE_EXISTING);
downloadedFilesAccumulator.add(packageIndexSignature.getName());
} else { } else {
downloadedFilesAccumulator.remove(packageIndex.getName()); downloadedFilesAccumulator.remove(packageIndex.getName());
log.error("{} file signature verification failed. File ignored.", packageIndexSignatureUrl);
System.err.println(format(tr("{0} file signature verification failed. File ignored."), packageIndexUrlString));
}
} catch (Exception e) {
log.error("Cannot download the signature from {} the package will be install in any case", packageIndexSignatureUrl, e);
if (packageIndexTemp.length() > 0) {
Files.move(packageIndexTemp.toPath(), packageIndex.toPath(), StandardCopyOption.REPLACE_EXISTING);
} else {
log.error("The temporarily package index file is empty (path:{},url:{}), It cannot be move there {} ",
packageIndexTemp.toPath(), packageIndexUrlString, packageIndex.toPath());
} }
} else {
log.info("The domain is not selected to verify the signature. domain list: {}, packageIndex: {}", domain, packageIndexUrl);
} }
} catch (Exception e) { } catch (Exception e) {
downloadedFilesAccumulator.remove(packageIndex.getName()); downloadedFilesAccumulator.remove(packageIndex.getName());
throw e; throw e;
} finally { } finally {
// Delete useless temp file // Delete useless temp file
Files.deleteIfExists(packageIndexTemp.toPath()); Files.deleteIfExists(packageIndexTemp.toPath());
Files.deleteIfExists(packageIndexSignatureTemp.toPath());
} }
} }
public boolean checkSignature(MultiStepProgress progress, List<String> downloadedFilesAccumulator, URL signatureUrl, ProgressListener progressListener, SignatureVerifier signatureVerifier, String statusText, File fileToVerify) throws Exception {
File packageIndexSignatureTemp = File.createTempFile(signatureUrl.getPath(), ".tmp");
// Signature file name
String[] urlPathParts = signatureUrl.getFile().split("/");
File packageIndexSignature = BaseNoGui.indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]);
try {
// Download signature
download(signatureUrl, packageIndexSignatureTemp, progress, statusText, progressListener, true);
// Verify the signature before move the files
boolean signatureVerified = signatureVerifier.isSigned(fileToVerify, packageIndexSignatureTemp);
if (signatureVerified) {
log.info("Signature verified. url={}, signature url={}, file to verify={}, signature file={}", signatureUrl, signatureUrl, fileToVerify, packageIndexSignatureTemp);
// Move if the signature is ok
Files.move(packageIndexSignatureTemp.toPath(), packageIndexSignature.toPath(), StandardCopyOption.REPLACE_EXISTING);
downloadedFilesAccumulator.add(packageIndexSignature.getName());
} else {
log.error("{} file signature verification failed. File ignored.", signatureUrl);
System.err.println(format(tr("{0} file signature verification failed. File ignored."), signatureUrl.toString()));
}
return signatureVerified;
} catch (Exception e) {
log.error("Cannot download the signature from {} the package will be discard", signatureUrl, e);
throw e;
} finally {
Files.deleteIfExists(packageIndexSignatureTemp.toPath());
}
}
} }

View File

@ -31,10 +31,13 @@ package cc.arduino.contributions.libraries;
import cc.arduino.Constants; import cc.arduino.Constants;
import cc.arduino.contributions.DownloadableContributionsDownloader; import cc.arduino.contributions.DownloadableContributionsDownloader;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.GZippedJsonDownloader; import cc.arduino.contributions.GZippedJsonDownloader;
import cc.arduino.contributions.ProgressListener; import cc.arduino.contributions.ProgressListener;
import cc.arduino.utils.ArchiveExtractor; import cc.arduino.utils.ArchiveExtractor;
import cc.arduino.utils.MultiStepProgress; import cc.arduino.utils.MultiStepProgress;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import processing.app.BaseNoGui; import processing.app.BaseNoGui;
import processing.app.I18n; import processing.app.I18n;
import processing.app.Platform; import processing.app.Platform;
@ -45,47 +48,59 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import static processing.app.I18n.tr; import static processing.app.I18n.tr;
public class LibraryInstaller { public class LibraryInstaller {
private static Logger log = LogManager.getLogger(LibraryInstaller.class);
private final Platform platform; private final Platform platform;
private final GPGDetachedSignatureVerifier signatureVerifier;
public LibraryInstaller(Platform platform) { public LibraryInstaller(Platform platform, GPGDetachedSignatureVerifier signatureVerifier) {
this.platform = platform; this.platform = platform;
this.signatureVerifier = signatureVerifier;
} }
public synchronized void updateIndex(ProgressListener progressListener) throws Exception { public synchronized void updateIndex(ProgressListener progressListener) throws Exception {
final MultiStepProgress progress = new MultiStepProgress(3); final MultiStepProgress progress = new MultiStepProgress(3);
List<String> downloadedFilesAccumulator = new LinkedList<>();
DownloadableContributionsDownloader downloader = new DownloadableContributionsDownloader(BaseNoGui.librariesIndexer.getStagingFolder()); DownloadableContributionsDownloader downloader = new DownloadableContributionsDownloader(BaseNoGui.librariesIndexer.getStagingFolder());
// Step 1: Download index // Step 1: Download index
File outputFile = BaseNoGui.librariesIndexer.getIndexFile(); File outputFile = BaseNoGui.librariesIndexer.getIndexFile();
// Create temp files // Create temp files
File libraryIndexTemp = File.createTempFile(new URL(Constants.LIBRARY_INDEX_URL).getPath(), ".tmp"); File libraryIndexTemp = File.createTempFile(new URL(Constants.LIBRARY_INDEX_URL).getPath(), ".tmp");
final URL libraryURL = new URL(Constants.LIBRARY_INDEX_URL);
final String statusText = tr("Downloading libraries index...");
try { try {
GZippedJsonDownloader gZippedJsonDownloader = new GZippedJsonDownloader(downloader, new URL(Constants.LIBRARY_INDEX_URL), new URL(Constants.LIBRARY_INDEX_URL_GZ)); GZippedJsonDownloader gZippedJsonDownloader = new GZippedJsonDownloader(downloader, libraryURL, new URL(Constants.LIBRARY_INDEX_URL_GZ));
gZippedJsonDownloader.download(libraryIndexTemp, progress, tr("Downloading libraries index..."), progressListener); gZippedJsonDownloader.download(libraryIndexTemp, progress, statusText, progressListener);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// Download interrupted... just exit // Download interrupted... just exit
return; return;
} }
progress.stepDone(); progress.stepDone();
// TODO: Check downloaded index URL signatureUrl = new URL(libraryURL.toString() + ".sig");
if (downloader.checkSignature(progress, downloadedFilesAccumulator, signatureUrl, progressListener, signatureVerifier, statusText, libraryIndexTemp)) {
// Replace old index with the updated one
if (libraryIndexTemp.length() > 0) {
Files.move(libraryIndexTemp.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
// Replace old index with the updated one // Step 2: Parse index
if (libraryIndexTemp.length() > 0) { BaseNoGui.librariesIndexer.parseIndex();
Files.move(libraryIndexTemp.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
// Step 3: Rescan index
rescanLibraryIndex(progress, progressListener);
} else {
log.error("Fail to verify the signature of {}", libraryURL);
} }
// Step 2: Parse index
BaseNoGui.librariesIndexer.parseIndex();
// Step 3: Rescan index
rescanLibraryIndex(progress, progressListener);
} }
public synchronized void install(ContributedLibrary lib, Optional<ContributedLibrary> mayReplacedLib, ProgressListener progressListener) throws Exception { public synchronized void install(ContributedLibrary lib, Optional<ContributedLibrary> mayReplacedLib, ProgressListener progressListener) throws Exception {

View File

@ -41,8 +41,8 @@ import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor; import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.exec.PumpStreamHandler;
import org.slf4j.Logger; import org.apache.logging.log4j.LogManager;
import org.slf4j.LoggerFactory; import org.apache.logging.log4j.Logger;
import processing.app.BaseNoGui; import processing.app.BaseNoGui;
import processing.app.I18n; import processing.app.I18n;
import processing.app.Platform; import processing.app.Platform;
@ -63,7 +63,7 @@ import static processing.app.I18n.format;
import static processing.app.I18n.tr; import static processing.app.I18n.tr;
public class ContributionInstaller { public class ContributionInstaller {
private static Logger log = LoggerFactory.getLogger(ContributionInstaller.class); private static Logger log = LogManager.getLogger(ContributionInstaller.class);
private final Platform platform; private final Platform platform;
private final SignatureVerifier signatureVerifier; private final SignatureVerifier signatureVerifier;
@ -267,10 +267,11 @@ public class ContributionInstaller {
// now try to remove the containing TOOL_NAME folder // now try to remove the containing TOOL_NAME folder
// (and silently fail if another version of the tool is installed) // (and silently fail if another version of the tool is installed)
try { try {
Files.deleteIfExists(destFolder.getParentFile().toPath()); Files.delete(destFolder.getParentFile().toPath());
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
log.error(e.getMessage(), e); log.info("The directory is not empty there is another version installed. directory {}",
destFolder.getParentFile().toPath(), e);
} }
} }