summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSami Tolvanen <samitolvanen@google.com>2014-10-31 17:18:23 -0700
committerIliyan Malchev <malchev@google.com>2014-11-13 15:40:07 -0800
commit3380f2fea1b9a18f26ae95b60a01eeb55565eb1b (patch)
tree44a4b9e18737674c5edaa8947bef25ebeee3a73c
parentf0f33adb7ce6557459306ce03576af4d79c0c9ef (diff)
downloadextras-3380f2fea1b9a18f26ae95b60a01eeb55565eb1b.tar.gz
DO NOT MERGE: Implement boot signature verification
Adds signature verification support to BootSignature.java. Bug: 15984840 Bug: 18120110 Change-Id: Ib1a67573bdcd0f41e2d920f518de15993c8750e0 (cherry picked from commit 7999c089cfa82d63d4a28606c78b381b74509175)
-rw-r--r--verity/BootSignature.java87
-rw-r--r--verity/Utils.java15
2 files changed, 101 insertions, 1 deletions
diff --git a/verity/BootSignature.java b/verity/BootSignature.java
index 4ee3309d..3e23a1ec 100644
--- a/verity/BootSignature.java
+++ b/verity/BootSignature.java
@@ -16,19 +16,26 @@
package com.android.verity;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.PrivateKey;
+import java.security.PublicKey;
import java.security.Security;
import java.security.cert.X509Certificate;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
import java.security.cert.CertificateEncodingException;
import java.util.Arrays;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
@@ -62,15 +69,54 @@ public class BootSignature extends ASN1Object
private DERPrintableString target;
private ASN1Integer length;
private DEROctetString signature;
+ private PublicKey publicKey;
private static final int FORMAT_VERSION = 1;
+ /**
+ * Initializes the object for signing an image file
+ * @param target Target name, included in the signed data
+ * @param length Length of the image, included in the signed data
+ */
public BootSignature(String target, int length) {
this.formatVersion = new ASN1Integer(FORMAT_VERSION);
this.target = new DERPrintableString(target);
this.length = new ASN1Integer(length);
}
+ /**
+ * Initializes the object for verifying a signed image file
+ * @param signature Signature footer
+ */
+ public BootSignature(byte[] signature)
+ throws Exception {
+ ASN1InputStream stream = new ASN1InputStream(signature);
+ ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
+
+ formatVersion = (ASN1Integer) sequence.getObjectAt(0);
+ if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
+ throw new IllegalArgumentException("Unsupported format version");
+ }
+
+ certificate = sequence.getObjectAt(1);
+ byte[] encoded = ((ASN1Object) certificate).getEncoded();
+ ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
+
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
+ publicKey = c.getPublicKey();
+
+ ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
+ algorithmIdentifier = new AlgorithmIdentifier(
+ (ASN1ObjectIdentifier) algId.getObjectAt(0));
+
+ ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
+ target = (DERPrintableString) attrs.getObjectAt(0);
+ length = (ASN1Integer) attrs.getObjectAt(1);
+
+ this.signature = (DEROctetString) sequence.getObjectAt(4);
+ }
+
public ASN1Object getAuthenticatedAttributes() {
ASN1EncodableVector attrs = new ASN1EncodableVector();
attrs.add(target);
@@ -107,6 +153,16 @@ public class BootSignature extends ASN1Object
return Utils.sign(key, signable);
}
+ public boolean verify(byte[] image) throws Exception {
+ if (length.getValue().intValue() != image.length) {
+ throw new IllegalArgumentException("Invalid image length");
+ }
+
+ byte[] signable = generateSignableImage(image);
+ return Utils.verify(publicKey, signable, signature.getOctets(),
+ algorithmIdentifier);
+ }
+
public ASN1Primitive toASN1Primitive() {
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(formatVersion);
@@ -185,6 +241,30 @@ public class BootSignature extends ASN1Object
Utils.write(image_with_metadata, outPath);
}
+ public static void verifySignature(String imagePath) throws Exception {
+ byte[] image = Utils.read(imagePath);
+ int signableSize = getSignableImageSize(image);
+
+ if (signableSize >= image.length) {
+ throw new IllegalArgumentException("Invalid image: not signed");
+ }
+
+ byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
+ BootSignature bootsig = new BootSignature(signature);
+
+ try {
+ if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
+ System.err.println("Signature is VALID");
+ System.exit(0);
+ } else {
+ System.err.println("Signature is INVALID");
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ }
+ System.exit(1);
+ }
+
/* java -cp
../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/\
classes/com.android.verity.BootSignature \
@@ -197,6 +277,11 @@ public class BootSignature extends ASN1Object
*/
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
- doSignature(args[0], args[1], args[2], args[3], args[4]);
+
+ if ("-verify".equals(args[0])) {
+ verifySignature(args[1]);
+ } else {
+ doSignature(args[0], args[1], args[2], args[3], args[4]);
+ }
}
}
diff --git a/verity/Utils.java b/verity/Utils.java
index 4eba5527..3576e3b0 100644
--- a/verity/Utils.java
+++ b/verity/Utils.java
@@ -226,6 +226,21 @@ public class Utils {
return new AlgorithmIdentifier(new ASN1ObjectIdentifier(id));
}
+ static boolean verify(PublicKey key, byte[] input, byte[] signature,
+ AlgorithmIdentifier algId) throws Exception {
+ String algName = ID_TO_ALG.get(algId.getObjectId().getId());
+
+ if (algName == null) {
+ throw new IllegalArgumentException("Unsupported algorithm " + algId.getObjectId());
+ }
+
+ Signature verifier = Signature.getInstance(algName);
+ verifier.initVerify(key);
+ verifier.update(input);
+
+ return verifier.verify(signature);
+ }
+
static byte[] sign(PrivateKey privateKey, byte[] input) throws Exception {
Signature signer = Signature.getInstance(getSignatureAlgorithm(privateKey));
signer.initSign(privateKey);