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:
parent
2596ecee28
commit
5157688590
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user