diff options
author | Sami Tolvanen <samitolvanen@google.com> | 2014-10-31 17:18:23 -0700 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2014-11-13 15:40:07 -0800 |
commit | 3380f2fea1b9a18f26ae95b60a01eeb55565eb1b (patch) | |
tree | 44a4b9e18737674c5edaa8947bef25ebeee3a73c | |
parent | f0f33adb7ce6557459306ce03576af4d79c0c9ef (diff) | |
download | extras-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.java | 87 | ||||
-rw-r--r-- | verity/Utils.java | 15 |
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); |