1cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/* 2cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Copyright (C) 2014 The Android Open Source Project 3cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 4cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Licensed under the Apache License, Version 2.0 (the "License"); 5cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * you may not use this file except in compliance with the License. 6cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * You may obtain a copy of the License at 7cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 8cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * http://www.apache.org/licenses/LICENSE-2.0 9cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * 10cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Unless required by applicable law or agreed to in writing, software 11cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * distributed under the License is distributed on an "AS IS" BASIS, 12cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * See the License for the specific language governing permissions and 14cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * limitations under the License. 15cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */ 16cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 17cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapackage com.android.verity; 18cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 193380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.io.ByteArrayInputStream; 20cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.IOException; 2129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.nio.ByteBuffer; 2229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.nio.ByteOrder; 23cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PrivateKey; 243380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.security.PublicKey; 2529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.security.Security; 2629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.security.cert.X509Certificate; 273380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.security.cert.Certificate; 283380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.security.cert.CertificateFactory; 2929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.security.cert.CertificateEncodingException; 30cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.util.Arrays; 31cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Encodable; 32cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1EncodableVector; 33cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Integer; 34cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Object; 353380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport org.bouncycastle.asn1.ASN1ObjectIdentifier; 363380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport org.bouncycastle.asn1.ASN1OctetString; 37cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Primitive; 383380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport org.bouncycastle.asn1.ASN1Sequence; 3929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport org.bouncycastle.asn1.ASN1InputStream; 40cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DEROctetString; 41cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERPrintableString; 42cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERSequence; 43cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.util.ASN1Dump; 44cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.x509.AlgorithmIdentifier; 4529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport org.bouncycastle.jce.provider.BouncyCastleProvider; 46cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 47cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/** 48cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * AndroidVerifiedBootSignature DEFINITIONS ::= 49cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * BEGIN 5029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence * formatVersion ::= INTEGER 5129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence * certificate ::= Certificate 5229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence * algorithmIdentifier ::= SEQUENCE { 53cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * algorithm OBJECT IDENTIFIER, 54cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * parameters ANY DEFINED BY algorithm OPTIONAL 55cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * } 5629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence * authenticatedAttributes ::= SEQUENCE { 57cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * target CHARACTER STRING, 58cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * length INTEGER 59cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * } 6029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence * signature ::= OCTET STRING 61cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * END 62cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */ 63cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 64cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapublic class BootSignature extends ASN1Object 65cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra{ 66cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private ASN1Integer formatVersion; 6729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence private ASN1Encodable certificate; 68cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private AlgorithmIdentifier algorithmIdentifier; 69cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private DERPrintableString target; 70cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private ASN1Integer length; 71cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra private DEROctetString signature; 723380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen private PublicKey publicKey; 73cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 7429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence private static final int FORMAT_VERSION = 1; 7529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 763380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen /** 773380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen * Initializes the object for signing an image file 783380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen * @param target Target name, included in the signed data 793380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen * @param length Length of the image, included in the signed data 803380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen */ 81cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public BootSignature(String target, int length) { 8229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence this.formatVersion = new ASN1Integer(FORMAT_VERSION); 83cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra this.target = new DERPrintableString(target); 84cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra this.length = new ASN1Integer(length); 85cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 86cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 873380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen /** 883380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen * Initializes the object for verifying a signed image file 893380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen * @param signature Signature footer 903380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen */ 913380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen public BootSignature(byte[] signature) 923380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen throws Exception { 933380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen ASN1InputStream stream = new ASN1InputStream(signature); 943380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen ASN1Sequence sequence = (ASN1Sequence) stream.readObject(); 953380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 963380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen formatVersion = (ASN1Integer) sequence.getObjectAt(0); 973380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen if (formatVersion.getValue().intValue() != FORMAT_VERSION) { 983380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen throw new IllegalArgumentException("Unsupported format version"); 993380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 1003380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 1013380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen certificate = sequence.getObjectAt(1); 1023380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen byte[] encoded = ((ASN1Object) certificate).getEncoded(); 1033380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen ByteArrayInputStream bis = new ByteArrayInputStream(encoded); 1043380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 1053380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen CertificateFactory cf = CertificateFactory.getInstance("X.509"); 1063380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen X509Certificate c = (X509Certificate) cf.generateCertificate(bis); 1073380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen publicKey = c.getPublicKey(); 1083380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 1093380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2); 1103380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen algorithmIdentifier = new AlgorithmIdentifier( 1113380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen (ASN1ObjectIdentifier) algId.getObjectAt(0)); 1123380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 1133380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3); 1143380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen target = (DERPrintableString) attrs.getObjectAt(0); 1153380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen length = (ASN1Integer) attrs.getObjectAt(1); 1163380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 1173380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen this.signature = (DEROctetString) sequence.getObjectAt(4); 1183380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 1193380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 120cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public ASN1Object getAuthenticatedAttributes() { 121cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra ASN1EncodableVector attrs = new ASN1EncodableVector(); 122cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra attrs.add(target); 123cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra attrs.add(length); 124cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return new DERSequence(attrs); 125cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 126cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 127cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public byte[] getEncodedAuthenticatedAttributes() throws IOException { 128cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return getAuthenticatedAttributes().getEncoded(); 129cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 130cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 131241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen public AlgorithmIdentifier getAlgorithmIdentifier() { 132241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen return algorithmIdentifier; 133241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 134241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 135241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen public PublicKey getPublicKey() { 136241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen return publicKey; 137241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 138241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 139241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen public byte[] getSignature() { 140241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen return signature.getOctets(); 141241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen } 142241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen 14329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence public void setSignature(byte[] sig, AlgorithmIdentifier algId) { 14429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence algorithmIdentifier = algId; 145cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra signature = new DEROctetString(sig); 146cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 147cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 14829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence public void setCertificate(X509Certificate cert) 14929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence throws Exception, IOException, CertificateEncodingException { 15029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence ASN1InputStream s = new ASN1InputStream(cert.getEncoded()); 15129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence certificate = s.readObject(); 15253e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen publicKey = cert.getPublicKey(); 15329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence } 15429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 155cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public byte[] generateSignableImage(byte[] image) throws IOException { 156cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] attrs = getEncodedAuthenticatedAttributes(); 157cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] signable = Arrays.copyOf(image, image.length + attrs.length); 158cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra for (int i=0; i < attrs.length; i++) { 159cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra signable[i+image.length] = attrs[i]; 160cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 161cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return signable; 162cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 163cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 164cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public byte[] sign(byte[] image, PrivateKey key) throws Exception { 165cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] signable = generateSignableImage(image); 166d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra return Utils.sign(key, signable); 167cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 168cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 1693380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen public boolean verify(byte[] image) throws Exception { 1703380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen if (length.getValue().intValue() != image.length) { 1713380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen throw new IllegalArgumentException("Invalid image length"); 1723380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 1733380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 1743380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen byte[] signable = generateSignableImage(image); 1753380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen return Utils.verify(publicKey, signable, signature.getOctets(), 1763380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen algorithmIdentifier); 1773380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 1783380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 179cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public ASN1Primitive toASN1Primitive() { 180cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra ASN1EncodableVector v = new ASN1EncodableVector(); 181cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(formatVersion); 18229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence v.add(certificate); 183cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(algorithmIdentifier); 184cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(getAuthenticatedAttributes()); 185cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra v.add(signature); 186cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra return new DERSequence(v); 187cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 188cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 18929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence public static int getSignableImageSize(byte[] data) throws Exception { 19029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8), 19129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence "ANDROID!".getBytes("US-ASCII"))) { 19229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence throw new IllegalArgumentException("Invalid image header: missing magic"); 19329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence } 19429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 19529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence ByteBuffer image = ByteBuffer.wrap(data); 19629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image.order(ByteOrder.LITTLE_ENDIAN); 19729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 19829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image.getLong(); // magic 19929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence int kernelSize = image.getInt(); 20029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image.getInt(); // kernel_addr 20129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence int ramdskSize = image.getInt(); 20229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image.getInt(); // ramdisk_addr 20329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence int secondSize = image.getInt(); 20429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image.getLong(); // second_addr + tags_addr 20529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence int pageSize = image.getInt(); 20629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 20729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence int length = pageSize // include the page aligned image header 20829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence + ((kernelSize + pageSize - 1) / pageSize) * pageSize 20929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence + ((ramdskSize + pageSize - 1) / pageSize) * pageSize 21029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence + ((secondSize + pageSize - 1) / pageSize) * pageSize; 21129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 21229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence length = ((length + pageSize - 1) / pageSize) * pageSize; 21329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 21429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence if (length <= 0) { 21529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence throw new IllegalArgumentException("Invalid image header: invalid length"); 21629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence } 21729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 21829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence return length; 21929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence } 22029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 221cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public static void doSignature( String target, 222cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra String imagePath, 223cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra String keyPath, 22429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence String certPath, 225cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra String outPath) throws Exception { 22629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 227cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra byte[] image = Utils.read(imagePath); 22829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence int signableSize = getSignableImageSize(image); 22929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 23029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence if (signableSize < image.length) { 23129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence System.err.println("NOTE: truncating file " + imagePath + 23229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence " from " + image.length + " to " + signableSize + " bytes"); 23329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image = Arrays.copyOf(image, signableSize); 23429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence } else if (signableSize > image.length) { 23529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence throw new IllegalArgumentException("Invalid image: too short, expected " + 23629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence signableSize + " bytes"); 23729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence } 23829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 239cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra BootSignature bootsig = new BootSignature(target, image.length); 24029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 24129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence X509Certificate cert = Utils.loadPEMCertificate(certPath); 24229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence bootsig.setCertificate(cert); 24329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 24429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence PrivateKey key = Utils.loadDERPrivateKeyFromFile(keyPath); 24529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence bootsig.setSignature(bootsig.sign(image, key), 24629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence Utils.getSignatureAlgorithmIdentifier(key)); 24729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 248d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra byte[] encoded_bootsig = bootsig.getEncoded(); 249d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length); 25029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 25129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence System.arraycopy(encoded_bootsig, 0, image_with_metadata, 25229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence image.length, encoded_bootsig.length); 25329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence 254d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra Utils.write(image_with_metadata, outPath); 255cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 256cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra 25753e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen public static void verifySignature(String imagePath, String certPath) throws Exception { 2583380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen byte[] image = Utils.read(imagePath); 2593380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen int signableSize = getSignableImageSize(image); 2603380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 2613380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen if (signableSize >= image.length) { 2623380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen throw new IllegalArgumentException("Invalid image: not signed"); 2633380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 2643380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 2653380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen byte[] signature = Arrays.copyOfRange(image, signableSize, image.length); 2663380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen BootSignature bootsig = new BootSignature(signature); 2673380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 26853e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen if (!certPath.isEmpty()) { 26953e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen System.err.println("NOTE: verifying using public key from " + certPath); 27053e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen bootsig.setCertificate(Utils.loadPEMCertificate(certPath)); 27153e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen } 27253e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen 2733380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen try { 2743380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen if (bootsig.verify(Arrays.copyOf(image, signableSize))) { 2753380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen System.err.println("Signature is VALID"); 2763380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen System.exit(0); 2773380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } else { 2783380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen System.err.println("Signature is INVALID"); 2793380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 2803380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } catch (Exception e) { 2813380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen e.printStackTrace(System.err); 2823380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 2833380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen System.exit(1); 2843380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 2853380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 28640193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen /* Example usage for signing a boot image using dev keys: 28740193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen java -cp \ 28840193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen ../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/ \ 28940193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen classes/com.android.verity.BootSignature \ 29040193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen /boot \ 29140193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen ../../../out/target/product/$PRODUCT/boot.img \ 29240193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen ../../../build/target/product/security/verity.pk8 \ 29340193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen ../../../build/target/product/security/verity.x509.pem \ 29440193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen /tmp/boot.img.signed 29529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence */ 296cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra public static void main(String[] args) throws Exception { 29729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence Security.addProvider(new BouncyCastleProvider()); 2983380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen 2993380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen if ("-verify".equals(args[0])) { 30053e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen String certPath = ""; 30153e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen 30253e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen if (args.length >= 4 && "-certificate".equals(args[2])) { 30353e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen /* args[3] is the path to a public key certificate */ 30453e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen certPath = args[3]; 30553e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen } 30653e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen 30740193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen /* args[1] is the path to a signed boot image */ 30853e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen verifySignature(args[1], certPath); 3093380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } else { 31040193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen /* args[0] is the target name, typically /boot 31140193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen args[1] is the path to a boot image to sign 31240193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen args[2] is the path to a private key 31340193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen args[3] is the path to the matching public key certificate 31440193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen args[4] is the path where to output the signed boot image 31540193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen */ 3163380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen doSignature(args[0], args[1], args[2], args[3], args[4]); 3173380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen } 318cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra } 31929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence} 320