Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 183c386

Browse files
cmaglieFederico Fissore
authored and
Federico Fissore
committedMar 27, 2015
PGP Digital signature verifier class
1 parent 8c49ee4 commit 183c386

File tree

3 files changed

+263
-0
lines changed

3 files changed

+263
-0
lines changed
 
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Arduino is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
28+
*/
29+
30+
package cc.arduino.packages.security;
31+
32+
import java.io.BufferedReader;
33+
import java.io.ByteArrayInputStream;
34+
import java.io.ByteArrayOutputStream;
35+
import java.io.File;
36+
import java.io.FileInputStream;
37+
import java.io.FileNotFoundException;
38+
import java.io.IOException;
39+
import java.io.InputStreamReader;
40+
import java.security.Security;
41+
42+
import org.bouncycastle.bcpg.ArmoredInputStream;
43+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
44+
import org.bouncycastle.openpgp.PGPObjectFactory;
45+
import org.bouncycastle.openpgp.PGPPublicKey;
46+
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
47+
import org.bouncycastle.openpgp.PGPSignature;
48+
import org.bouncycastle.openpgp.PGPSignatureList;
49+
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
50+
51+
import processing.app.helpers.StringUtils;
52+
import cc.arduino.packages.security.keys.PackagersPublicKeys;
53+
54+
public class ClearSignedVerifier {
55+
56+
public static class VerifyResult {
57+
public byte clearText[];
58+
public boolean verified;
59+
public Exception error;
60+
}
61+
62+
/**
63+
* Verify a PGP clearText-signature.
64+
*
65+
* @param signedTextFile
66+
* A File containing the clearText signature
67+
* @param pubKeyRing
68+
* A public key-ring containing the public key needed for the
69+
* signature verification
70+
* @return A VerifyResult class with the clearText and the signature
71+
* verification status
72+
* @throws FileNotFoundException
73+
*/
74+
public static VerifyResult verify(File signedTextFile,
75+
PGPPublicKeyRingCollection pubKeyRing)
76+
throws FileNotFoundException {
77+
// Create the result object
78+
VerifyResult result = new VerifyResult();
79+
result.clearText = null;
80+
result.verified = false;
81+
result.error = null;
82+
83+
ArmoredInputStream in = null;
84+
try {
85+
// Extract clear text.
86+
// Dash-encoding is removed by ArmoredInputStream.
87+
in = new ArmoredInputStream(new FileInputStream(signedTextFile));
88+
ByteArrayOutputStream temp = new ByteArrayOutputStream(in.available());
89+
while (true) {
90+
int c = in.read();
91+
if (c == -1)
92+
throw new IOException("Unexpected end of file");
93+
if (!in.isClearText())
94+
break;
95+
temp.write(c);
96+
}
97+
byte clearText[] = temp.toByteArray();
98+
result.clearText = clearText;
99+
100+
// Extract signature from clear-signed text
101+
PGPObjectFactory pgpFact = new PGPObjectFactory(in);
102+
PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
103+
PGPSignature sig = p3.get(0);
104+
105+
// Decode public key
106+
PGPPublicKey publicKey = pubKeyRing.getPublicKey(sig.getKeyID());
107+
108+
// Verify signature
109+
Security.addProvider(new BouncyCastleProvider());
110+
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"),
111+
publicKey);
112+
// RFC 4880, section 7: http://tools.ietf.org/html/rfc4880#section-7
113+
// The signature must be validated using clear text:
114+
// - without trailing white spaces on every line
115+
// - using CR LF line endings, no matter what the original line ending is
116+
// - without the latest line ending
117+
BufferedReader textIn = new BufferedReader(new InputStreamReader(
118+
new ByteArrayInputStream(clearText)));
119+
while (true) {
120+
// remove trailing whitespace and line endings
121+
String line = StringUtils.rtrim(textIn.readLine());
122+
sig.update(line.getBytes());
123+
if (!textIn.ready()) // skip latest line ending
124+
break;
125+
// always use CR LF
126+
sig.update((byte) '\r');
127+
sig.update((byte) '\n');
128+
}
129+
130+
// Prepare the result
131+
result.verified = sig.verify();
132+
} catch (Exception e) {
133+
result.error = e;
134+
} finally {
135+
if (in != null)
136+
try {
137+
in.close();
138+
} catch (IOException e) {
139+
e.printStackTrace();
140+
}
141+
}
142+
return result;
143+
}
144+
145+
public static void main(String[] args) throws Exception {
146+
VerifyResult verify = verify(new File(
147+
"/home/megabug/git/arduino/test.txt.asc"), new PackagersPublicKeys());
148+
System.out.println(verify.verified);
149+
}
150+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Arduino is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
28+
*/
29+
30+
package cc.arduino.packages.security.keys;
31+
32+
import java.io.ByteArrayInputStream;
33+
import java.io.IOException;
34+
35+
import org.bouncycastle.openpgp.PGPException;
36+
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
37+
import org.bouncycastle.openpgp.PGPUtil;
38+
39+
public class PackagersPublicKeys extends PGPPublicKeyRingCollection {
40+
41+
public static final String ARDUINO_PK = "" //
42+
+ "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
43+
+ "Version: GnuPG v1.4.11 (GNU/Linux)\n"
44+
+ "\n"
45+
+ "mQINBFJ9IskBEAD3vGGYFl+gib5WURZwcW7e1Z2+ZAd48LP+KsZ2RVHv7FhzsH1s\n"
46+
+ "eSRNsuLUXV0DHTCGUUqvQYV/+HLnv4hQvRFogql5zapQldS4mhWO0jcVuee3lDun\n"
47+
+ "nmQEN3Ikn0Zf2+sQD0iMXk8eRz3MJx2xDs7yK3ZHkdkie7pqNKg6yrJ64x5H3HJn\n"
48+
+ "y7aOSN3ClNgTbxdNlfCQfhex12c+fiOqVO4f5KyYvRo3zBMv8TV4JL0KG1L+uuKU\n"
49+
+ "uuXyG4jUhldpf+1fazX3bW0432rfnxNI2JsPCQ5h95nQNrW0LRS1Nrts8UTePt/9\n"
50+
+ "trJ1sIlSMRyG7mkq3gzTf4a2BaUejHHNGLwXBBMyGNQqei+hawwnimlD7egXKpa3\n"
51+
+ "uy8La2rB37RK672CjsN2KSOU7B6UpTQY6VCjkC0yQY6u9Kp8P9XY5M6HIZhBwVpk\n"
52+
+ "kPfJ93b73leMcSDSU6cCcCdWpkCUDQNpBLa4k0vr4nEC5hs8Q6RjpgVgGDulY2Xf\n"
53+
+ "hWkrh430r+a50wbEmSZwPg05wnC0n2pu+hpSF7mNx4FhnfXQ3eaJHvW/4hCdwxAg\n"
54+
+ "tbC+yXPmEJ01h3cK53xI8Usve4pizaxb2FuMf5FmOTt/B/H+KmHAOLcY3xCMxF9t\n"
55+
+ "wcXVHdfkWfZk4LK2RUo+oe3Z2SXSGuOj61pP5wnvRYojtURwjrehArTrpwARAQAB\n"
56+
+ "tCZBcmR1aW5vIFBhY2thZ2VzIDxwYWNrYWdlc0BhcmR1aW5vLmNjPokCPgQTAQIA\n"
57+
+ "KAUCUn0iyQIbAwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQPrLD\n"
58+
+ "cgsG1iRL7A/8Cf/S6xgksnSk1GD+6tSLEZHku7tLEhiCX38pS8a6UBAj1UGrbxPn\n"
59+
+ "kS0iPLcfJG7AblI4EjrYTMaLHUL0UtcOvq8+F9/NrZAVW6+xOpm9LTQhUMh+ddCx\n"
60+
+ "6igY3BRr9WtNkrowzMUGpGrJzIw7v3hiJbXDNIpTwoLF4aFEgOJXyJZawp8rjBOw\n"
61+
+ "bnRlq9MTC/7+nm/d7i7nsRxDyGcZllVDIFI38sVVmLL8eBd7z9Vn1RsZDkPzhGzM\n"
62+
+ "aKdFCU7hD15H3hPkeidkbLwmd5jihZCDDd2LhGtMFo3cwqouKo/UTaYomNP2iGlU\n"
63+
+ "2BBpuQTPcJy2d1d8lCr19yARcJCVC9H6sISkXPHO0EMgGrpdFgxDVGeEkvn1scHV\n"
64+
+ "x+k4BCOMl1WfsD4JkAJ5RyyZNjP0oDlY8SROfT479Ypb6qLMGCOujxjHapv/t2PH\n"
65+
+ "2cjadiMP62AN3rIiMTZWpCgL+bu3j8hsbAnlaINxoiab72+8uuA53o2SKWIA4J24\n"
66+
+ "0wu7ETW0QQkxym/ammX/nXgap/R9u/A8kGx+QKPyjptO+hnc7vgGAMEIrVDsKeTp\n"
67+
+ "AmIwtxvK3AIKGkup+E+ee2kzBhJHhsoDpmJZgaIxoiCdOZglaA+V53I16Vv+fiC1\n"
68+
+ "SW9cN5UQvlNycu8QFQwwz/Eg7M8abQDXBgf6znAKt0wSn6tI/b/NBmG5Ag0EUn0i\n"
69+
+ "yQEQAK8ZvoX51FizIt49nfwWR6w7CCG35B92oVTKn1oLPSF9fU75dmBd57sFAle0\n"
70+
+ "Zm5DzfzCQ1d6voo8HhmUQHIE1NamC1YE6c2AKvc4xx4ltjjPqi8+KJ1y5gNz1M5Q\n"
71+
+ "ZRnzjxjkCePSRxQXoEDNINryPvNQLzrFbtm5R2tsygwqaVxhJok4hny1srhxd8IZ\n"
72+
+ "rz5MBlRtRr31D494GRD4iSKyvpAC+zh2ZL1+zUtg7qQU0FybUJ/hIJ2DRHNwuutp\n"
73+
+ "2EzbDwJJNNDjjNC8NKdJ4GgOJJnKGU52OfdmeEeI1+wDm3/FvI4lSS9O/ay4ty3/\n"
74+
+ "wSwhGKOWNXowGFVXdzxYyCOf1NVDHn8Vj8sU2lHw5Fn2RA41xAs33aLPjLmdv7xa\n"
75+
+ "7mJSp0cfiudPyVWP0D+/269kMq6W3V9IFeRQMxObNXWACKzuaaLi60ncGaXvM/q1\n"
76+
+ "2O0HvQ9+BlLE7DSQWGb8NTngSAFYUYdWZ1GhiyTvFKkSDIAhkQfYLc0Kky6y1D2J\n"
77+
+ "w0alVMdroHwf67V+ya2+Ac8EGv0oQvAF7ug1Ymnjx59kqV6IxdsPdRAmfZT63yJS\n"
78+
+ "C6ZDEbuqP3SUCehSwO/GW0Echwuga87At4RJ6OQ8HxdhjFMGjQANp+He6L7O2dav\n"
79+
+ "+JbH1687fc65VO8sTbhfW6Ntzr/MIVdS6rc1RzHMfMeVcuFJABEBAAGJAiUEGAEC\n"
80+
+ "AA8FAlJ9IskCGwwFCQlmAYAACgkQPrLDcgsG1iRQwg//VhFjyz1q/jxB7HbUEGhT\n"
81+
+ "wNsT5lOVXIJy8Y6CyAQLjI5LatZxMdIqZOlkPgHiMpMqJqvDgBgR/44UKL4yzvmv\n"
82+
+ "/6DIeMngej2oD794Q4j4LlnQopolvZy7dSyQqWX3kqEABAPMYnLhR9r/PQPiienR\n"
83+
+ "E7p1+zC/czcpL7wHKChjPgegPDrJ7yOug9IgwFSfokF5BFR3aNJNOxEHd+YSXfS4\n"
84+
+ "i4Eef3YovQfUvMfq7jux7Qsi8igzvm782pPsylPwysd/d0emlkGqMLGHWh+r1eIy\n"
85+
+ "UzOXgfhwgN38RB/p1joVSZGpmTu6y9e50HME9FrYEmCrNwYTOi1fQB/IHr7lg1qT\n"
86+
+ "/Bap938b6vm08pEDnVLSahsgdJdG8YYwJEg2BJnpThIGHnle9Ahmk3OMI7Wl9VsQ\n"
87+
+ "1MJ+va/rWjKvq6z7k0YzQbrJStrlrErbi4DN0YTmNV2M6IDcySjhCSAww7nqHiIx\n"
88+
+ "sJGggMBQS0/KQCsyXHeLpJwoSTv16c9UajV7/wJA8r7yNZV9a/5LrC2hRoN4RnU5\n"
89+
+ "kN//5xNON5L5Qd40XslUaFv4J/f21xuLgCkDb9N/jqwq7gLkkP/1WX8UkmWLvGM0\n"
90+
+ "J5DkabHzgusefEG9pNsFwExzAg4IFYKgG2qbS0zNQV8uMUD9vF7K/6YZgcBjH5gc\n"
91+
+ "KCcKZZVUQLJhOIwgHQMy7ck=\n" //
92+
+ "=u0/X\n" //
93+
+ "-----END PGP PUBLIC KEY BLOCK-----\n";
94+
95+
public PackagersPublicKeys() throws IOException, PGPException {
96+
super(PGPUtil.getDecoderStream(new ByteArrayInputStream(ARDUINO_PK.getBytes())));
97+
}
98+
99+
}

‎arduino-core/src/processing/app/helpers/StringUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,18 @@ public static boolean wildcardMatch(String input, String pattern) {
2626
String regex = pattern.replace("?", ".?").replace("*", ".*?");
2727
return input.matches(regex);
2828
}
29+
30+
/**
31+
* Returns the string without trailing whitespace characters
32+
*
33+
* @param s
34+
* @return
35+
*/
36+
public static String rtrim(String s) {
37+
int i = s.length() - 1;
38+
while (i >= 0 && Character.isWhitespace(s.charAt(i))) {
39+
i--;
40+
}
41+
return s.substring(0, i + 1);
42+
}
2943
}

0 commit comments

Comments
 (0)
Please sign in to comment.