1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-08-08 11:22:40 +03:00

Introducing GPG signature verification when loading main package_index file

This commit is contained in:
Federico Fissore
2015-04-30 11:17:27 +02:00
parent 804480ce53
commit cf1879f948
31 changed files with 1039 additions and 286 deletions

View File

@@ -17,10 +17,10 @@
<classpathentry kind="lib" path="../app/lib/jackson-core-2.2.3.jar"/>
<classpathentry kind="lib" path="../app/lib/jackson-databind-2.2.3.jar"/>
<classpathentry kind="lib" path="../app/lib/jackson-module-mrbean-2.2.3.jar"/>
<classpathentry kind="lib" path="../app/lib/bcpg-jdk15on-149.jar"/>
<classpathentry kind="lib" path="../app/lib/bcprov-jdk15on-149.jar"/>
<classpathentry kind="lib" path="lib/bcpg-jdk15on-149.jar"/>
<classpathentry kind="lib" path="lib/bcprov-jdk15on-149.jar"/>
<classpathentry kind="lib" path="../app/lib/bcpg-jdk15on-152.jar"/>
<classpathentry kind="lib" path="../app/lib/bcprov-jdk15on-152.jar"/>
<classpathentry kind="lib" path="lib/bcpg-jdk15on-152.jar"/>
<classpathentry kind="lib" path="lib/bcprov-jdk15on-152.jar"/>
<classpathentry kind="lib" path="lib/commons-codec-1.7.jar"/>
<classpathentry kind="lib" path="lib/commons-compress-1.8.jar"/>
<classpathentry kind="lib" path="lib/commons-lang3-3.3.2.jar"/>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,94 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.contributions;
import org.apache.commons.compress.utils.IOUtils;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import java.io.*;
import java.util.Iterator;
public class GPGDetachedSignatureVerifier {
private String keyId;
public GPGDetachedSignatureVerifier() {
this("7F294291");
}
public GPGDetachedSignatureVerifier(String keyId) {
this.keyId = keyId;
}
public boolean verify(File signedFile, File signature, File publicKey) throws IOException, PGPException {
PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId);
PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(new FileInputStream(signature), new BcKeyFingerprintCalculator());
PGPSignatureList pgpSignatureList = (PGPSignatureList) pgpObjectFactory.nextObject();
assert pgpSignatureList.size() == 1;
PGPSignature pgpSignature = pgpSignatureList.get(0);
pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey);
pgpSignature.update(IOUtils.toByteArray(new FileInputStream(signedFile)));
return pgpSignature.verify();
}
private PGPPublicKey readPublicKey(File file, String keyId) throws IOException, PGPException {
InputStream keyIn = new BufferedInputStream(new FileInputStream(file));
PGPPublicKey pubKey = readPublicKey(keyIn, keyId);
keyIn.close();
return pubKey;
}
private PGPPublicKey readPublicKey(InputStream input, String keyId) throws IOException, PGPException {
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator());
Iterator keyRingIter = pgpPub.getKeyRings();
while (keyRingIter.hasNext()) {
PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next();
Iterator keyIter = keyRing.getPublicKeys();
while (keyIter.hasNext()) {
PGPPublicKey key = (PGPPublicKey) keyIter.next();
if (Long.toHexString(key.getKeyID()).toUpperCase().endsWith(keyId)) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
}

View File

@@ -0,0 +1,16 @@
package cc.arduino.contributions;
import processing.app.I18n;
import static processing.app.I18n._;
public class SignatureVerificationFailedException extends Exception {
public SignatureVerificationFailedException(String filename) {
super(I18n.format(_("{0} file signature verification failed"), filename));
}
public SignatureVerificationFailedException(String filename, Throwable cause) {
super(I18n.format(_("{0} file signature verification failed"), filename), cause);
}
}

View File

@@ -241,16 +241,26 @@ public class ContributionInstaller {
public List<String> updateIndex() throws Exception {
List<String> errors = new LinkedList<String>();
MultiStepProgress progress = new MultiStepProgress(1);
String statusText = _("Downloading platforms index...");
URL url = new URL(PACKAGE_INDEX_URL);
downloadIndex(progress, PACKAGE_INDEX_URL);
try {
downloadIndex(progress, PACKAGE_INDEX_URL + ".sig");
} catch (Exception e) {
//ignore errors
}
progress.stepDone();
return errors;
}
private void downloadIndex(MultiStepProgress progress, String packageIndexUrl) throws Exception {
String statusText = _("Downloading platforms index...");
URL url = new URL(packageIndexUrl);
String[] urlPathParts = url.getFile().split("/");
File outputFile = indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]);
File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp");
downloader.download(url, tmpFile, progress, statusText);
progress.stepDone();
// TODO: Check downloaded index
// Replace old index with the updated one
if (outputFile.exists()) {
@@ -259,7 +269,6 @@ public class ContributionInstaller {
if (!tmpFile.renameTo(outputFile)) {
throw new Exception("An error occurred while updating platforms index!");
}
return errors;
}
protected void onProgress(Progress progress) {

View File

@@ -29,6 +29,8 @@
package cc.arduino.contributions.packages;
import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.SignatureVerificationFailedException;
import cc.arduino.contributions.filters.BuiltInPredicate;
import cc.arduino.contributions.filters.InstalledPredicate;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -39,6 +41,7 @@ import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import processing.app.BaseNoGui;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.debug.TargetPlatformException;
@@ -65,22 +68,12 @@ public class ContributionsIndexer {
stagingFolder = new File(preferencesFolder, "staging" + File.separator + "packages");
}
// public static void main(String args[]) throws Exception {
// File indexFile = new File(args[0]);
//
// // VerifyResult verify = ClearSignedVerifier.verify(indexFile,
// // new PackagersPublicKeys());
// // if (!verify.verified)
// // throw new Exception("Invalid index file!");
//
// ContributionsIndexer indexer = new ContributionsIndexer(null);
// // indexer.parse(new ByteArrayInputStream(verify.clearText));
// indexer.parseIndex(indexFile);
// indexer.syncWithFilesystem();
// }
public void parseIndex() throws IOException {
index = parseIndex(getIndexFile(DEFAULT_INDEX_FILE_NAME));
public void parseIndex() throws Exception {
File defaultIndexFile = getIndexFile(DEFAULT_INDEX_FILE_NAME);
if (!isSigned(defaultIndexFile)) {
throw new SignatureVerificationFailedException(DEFAULT_INDEX_FILE_NAME);
}
index = parseIndex(defaultIndexFile);
File[] indexFiles = preferencesFolder.listFiles(new FilenameFilter() {
@Override
@@ -147,9 +140,18 @@ public class ContributionsIndexer {
return !PROTECTED_PACKAGE_NAMES.contains(contributedPackage.getName()) || isSigned(indexFile);
}
//TODO stub implementation
private boolean isSigned(File indexFile) {
return true;
File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig");
if (!signature.exists()) {
return false;
}
try {
return new GPGDetachedSignatureVerifier().verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key"));
} catch (Exception e) {
BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e);
return false;
}
}
private ContributionsIndex parseIndex(File indexFile) throws IOException {
@@ -169,6 +171,9 @@ public class ContributionsIndexer {
}
public void syncBuiltInHardwareFolder(File hardwareFolder) throws IOException {
if (index == null) {
return;
}
for (File folder : hardwareFolder.listFiles(ONLY_DIRS)) {
ContributedPackage pack = index.findPackage(folder.getName());
if (pack != null) {
@@ -203,8 +208,13 @@ public class ContributionsIndexer {
}
public void syncLocalPackagesFolder() {
if (!packagesFolder.isDirectory())
if (!packagesFolder.isDirectory()) {
return;
}
if (index == null) {
return;
}
// Scan all hardware folders and mark as installed all the
// platforms found.
@@ -270,6 +280,10 @@ public class ContributionsIndexer {
public List<TargetPackage> createTargetPackages() throws TargetPlatformException {
List<TargetPackage> packages = new ArrayList<TargetPackage>();
if (index == null) {
return packages;
}
for (ContributedPackage aPackage : index.getPackages()) {
ContributedTargetPackage targetPackage = new ContributedTargetPackage(aPackage.getName());
@@ -315,6 +329,9 @@ public class ContributionsIndexer {
public Set<ContributedTool> getInstalledTools() {
Set<ContributedTool> tools = new HashSet<ContributedTool>();
if (index == null) {
return tools;
}
for (ContributedPackage pack : index.getPackages()) {
Collection<ContributedPlatform> platforms = Collections2.filter(pack.getPlatforms(), new InstalledPredicate());
ImmutableListMultimap<String, ContributedPlatform> platformsByName = Multimaps.index(platforms, new Function<ContributedPlatform, String>() {
@@ -353,4 +370,24 @@ public class ContributionsIndexer {
return new File(preferencesFolder, name);
}
public List<ContributedPackage> getPackages() {
if (index == null) {
return new LinkedList<ContributedPackage>();
}
return index.getPackages();
}
public List<String> getCategories() {
if (index == null) {
return new LinkedList<String>();
}
return index.getCategories();
}
public ContributedPlatform getInstalled(String packageName, String platformArch) {
if (index == null) {
return null;
}
return index.getInstalled(packageName, platformArch);
}
}

View File

@@ -1,135 +0,0 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.security;
import cc.arduino.packages.security.keys.PackagersPublicKeys;
import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import processing.app.helpers.StringUtils;
import java.io.*;
import java.security.Security;
public class ClearSignedVerifier {
public static class VerifyResult {
public byte clearText[];
public boolean verified;
public Exception error;
}
/**
* Verify a PGP clearText-signature.
*
* @param signedTextFile A File containing the clearText signature
* @param pubKeyRing A public key-ring containing the public key needed for the
* signature verification
* @return A VerifyResult class with the clearText and the signature
* verification status
* @throws FileNotFoundException
*/
public static VerifyResult verify(File signedTextFile,
PGPPublicKeyRingCollection pubKeyRing) {
// Create the result object
VerifyResult result = new VerifyResult();
result.clearText = null;
result.verified = false;
result.error = null;
ArmoredInputStream in = null;
try {
// Extract clear text.
// Dash-encoding is removed by ArmoredInputStream.
in = new ArmoredInputStream(new FileInputStream(signedTextFile));
ByteArrayOutputStream temp = new ByteArrayOutputStream(in.available());
while (true) {
int c = in.read();
if (c == -1)
throw new IOException("Unexpected end of file");
if (!in.isClearText())
break;
temp.write(c);
}
byte clearText[] = temp.toByteArray();
result.clearText = clearText;
// Extract signature from clear-signed text
PGPObjectFactory pgpFact = new PGPObjectFactory(in);
PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
PGPSignature sig = p3.get(0);
// Decode public key
PGPPublicKey publicKey = pubKeyRing.getPublicKey(sig.getKeyID());
// Verify signature
Security.addProvider(new BouncyCastleProvider());
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"),
publicKey);
// RFC 4880, section 7: http://tools.ietf.org/html/rfc4880#section-7
// The signature must be validated using clear text:
// - without trailing white spaces on every line
// - using CR LF line endings, no matter what the original line ending is
// - without the latest line ending
BufferedReader textIn = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(clearText)));
while (true) {
// remove trailing whitespace and line endings
String line = StringUtils.rtrim(textIn.readLine());
sig.update(line.getBytes());
if (!textIn.ready()) // skip latest line ending
break;
// always use CR LF
sig.update((byte) '\r');
sig.update((byte) '\n');
}
// Prepare the result
result.verified = sig.verify();
} catch (Exception e) {
result.error = e;
} finally {
if (in != null)
try {
in.close();
} catch (IOException e) {
// ignored
}
}
return result;
}
public static void main(String[] args) throws Exception {
VerifyResult verify = verify(new File(
"/home/megabug/git/arduino/test.txt.asc"), new PackagersPublicKeys());
System.out.println(verify.verified);
}
}

View File

@@ -1,99 +0,0 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.security.keys;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
public class PackagersPublicKeys extends PGPPublicKeyRingCollection {
public static final String ARDUINO_PK = "" //
+ "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
+ "Version: GnuPG v1.4.11 (GNU/Linux)\n"
+ "\n"
+ "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\n"
+ "KCcKZZVUQLJhOIwgHQMy7ck=\n" //
+ "=u0/X\n" //
+ "-----END PGP PUBLIC KEY BLOCK-----\n";
public PackagersPublicKeys() throws IOException, PGPException {
super(PGPUtil.getDecoderStream(new ByteArrayInputStream(ARDUINO_PK.getBytes())));
}
}

View File

@@ -1,5 +1,6 @@
package processing.app;
import cc.arduino.contributions.SignatureVerificationFailedException;
import cc.arduino.contributions.libraries.LibrariesIndexer;
import cc.arduino.contributions.packages.ContributedTool;
import cc.arduino.contributions.packages.ContributionsIndexer;
@@ -581,6 +582,7 @@ public class BaseNoGui {
File defaultPackageJsonFile = new File(getContentFile("dist"), "package_index.json");
if (!indexFile.isFile() || (defaultPackageJsonFile.isFile() && defaultPackageJsonFile.lastModified() > indexFile.lastModified())) {
FileUtils.copyFile(defaultPackageJsonFile, indexFile);
FileUtils.copyFile(new File(getContentFile("dist"), "package_index.json.sig"), new File(indexFile.getParent(), "package_index.json.sig"));
} else if (!indexFile.isFile()) {
// Otherwise create an empty packages index
FileOutputStream out = null;
@@ -594,7 +596,13 @@ public class BaseNoGui {
}
}
}
indexer.parseIndex();
try {
indexer.parseIndex();
} catch (SignatureVerificationFailedException e) {
indexFile.delete();
throw e;
}
indexer.syncWithFilesystem(getHardwareFolder());
packages = new HashMap<String, TargetPackage>();