xref: /aosp_15_r20/system/extras/verity/BootSignature.java (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2014 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker 
17*288bf522SAndroid Build Coastguard Worker package com.android.verity;
18*288bf522SAndroid Build Coastguard Worker 
19*288bf522SAndroid Build Coastguard Worker import java.io.ByteArrayInputStream;
20*288bf522SAndroid Build Coastguard Worker import java.io.IOException;
21*288bf522SAndroid Build Coastguard Worker import java.nio.ByteBuffer;
22*288bf522SAndroid Build Coastguard Worker import java.nio.ByteOrder;
23*288bf522SAndroid Build Coastguard Worker import java.security.PrivateKey;
24*288bf522SAndroid Build Coastguard Worker import java.security.PublicKey;
25*288bf522SAndroid Build Coastguard Worker import java.security.Security;
26*288bf522SAndroid Build Coastguard Worker import java.security.cert.X509Certificate;
27*288bf522SAndroid Build Coastguard Worker import java.security.cert.Certificate;
28*288bf522SAndroid Build Coastguard Worker import java.security.cert.CertificateFactory;
29*288bf522SAndroid Build Coastguard Worker import java.security.cert.CertificateEncodingException;
30*288bf522SAndroid Build Coastguard Worker import java.util.Arrays;
31*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1Encodable;
32*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1EncodableVector;
33*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1Integer;
34*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1Object;
35*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1ObjectIdentifier;
36*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1OctetString;
37*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1Primitive;
38*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1Sequence;
39*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.ASN1InputStream;
40*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.DEROctetString;
41*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.DERPrintableString;
42*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.DERSequence;
43*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.util.ASN1Dump;
44*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
45*288bf522SAndroid Build Coastguard Worker import org.bouncycastle.jce.provider.BouncyCastleProvider;
46*288bf522SAndroid Build Coastguard Worker 
47*288bf522SAndroid Build Coastguard Worker /**
48*288bf522SAndroid Build Coastguard Worker  *    AndroidVerifiedBootSignature DEFINITIONS ::=
49*288bf522SAndroid Build Coastguard Worker  *    BEGIN
50*288bf522SAndroid Build Coastguard Worker  *        formatVersion ::= INTEGER
51*288bf522SAndroid Build Coastguard Worker  *        certificate ::= Certificate
52*288bf522SAndroid Build Coastguard Worker  *        algorithmIdentifier ::= SEQUENCE {
53*288bf522SAndroid Build Coastguard Worker  *            algorithm OBJECT IDENTIFIER,
54*288bf522SAndroid Build Coastguard Worker  *            parameters ANY DEFINED BY algorithm OPTIONAL
55*288bf522SAndroid Build Coastguard Worker  *        }
56*288bf522SAndroid Build Coastguard Worker  *        authenticatedAttributes ::= SEQUENCE {
57*288bf522SAndroid Build Coastguard Worker  *            target CHARACTER STRING,
58*288bf522SAndroid Build Coastguard Worker  *            length INTEGER
59*288bf522SAndroid Build Coastguard Worker  *        }
60*288bf522SAndroid Build Coastguard Worker  *        signature ::= OCTET STRING
61*288bf522SAndroid Build Coastguard Worker  *     END
62*288bf522SAndroid Build Coastguard Worker  */
63*288bf522SAndroid Build Coastguard Worker 
64*288bf522SAndroid Build Coastguard Worker public class BootSignature extends ASN1Object
65*288bf522SAndroid Build Coastguard Worker {
66*288bf522SAndroid Build Coastguard Worker     private ASN1Integer             formatVersion;
67*288bf522SAndroid Build Coastguard Worker     private ASN1Encodable           certificate;
68*288bf522SAndroid Build Coastguard Worker     private AlgorithmIdentifier     algorithmIdentifier;
69*288bf522SAndroid Build Coastguard Worker     private DERPrintableString      target;
70*288bf522SAndroid Build Coastguard Worker     private ASN1Integer             length;
71*288bf522SAndroid Build Coastguard Worker     private DEROctetString          signature;
72*288bf522SAndroid Build Coastguard Worker     private PublicKey               publicKey;
73*288bf522SAndroid Build Coastguard Worker 
74*288bf522SAndroid Build Coastguard Worker     private static final int FORMAT_VERSION = 1;
75*288bf522SAndroid Build Coastguard Worker     /**
76*288bf522SAndroid Build Coastguard Worker      * Offset of recovery DTBO length in a boot image header of version greater than
77*288bf522SAndroid Build Coastguard Worker      * or equal to 1.
78*288bf522SAndroid Build Coastguard Worker      */
79*288bf522SAndroid Build Coastguard Worker     private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632;
80*288bf522SAndroid Build Coastguard Worker     /**
81*288bf522SAndroid Build Coastguard Worker      * Offset of DTB length in a boot image header of version greater than
82*288bf522SAndroid Build Coastguard Worker      * or equal to 2.
83*288bf522SAndroid Build Coastguard Worker      */
84*288bf522SAndroid Build Coastguard Worker     private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648;
85*288bf522SAndroid Build Coastguard Worker 
86*288bf522SAndroid Build Coastguard Worker 
87*288bf522SAndroid Build Coastguard Worker     /**
88*288bf522SAndroid Build Coastguard Worker      * Initializes the object for signing an image file
89*288bf522SAndroid Build Coastguard Worker      * @param target Target name, included in the signed data
90*288bf522SAndroid Build Coastguard Worker      * @param length Length of the image, included in the signed data
91*288bf522SAndroid Build Coastguard Worker      */
BootSignature(String target, int length)92*288bf522SAndroid Build Coastguard Worker     public BootSignature(String target, int length) {
93*288bf522SAndroid Build Coastguard Worker         this.formatVersion = new ASN1Integer(FORMAT_VERSION);
94*288bf522SAndroid Build Coastguard Worker         this.target = new DERPrintableString(target);
95*288bf522SAndroid Build Coastguard Worker         this.length = new ASN1Integer(length);
96*288bf522SAndroid Build Coastguard Worker     }
97*288bf522SAndroid Build Coastguard Worker 
98*288bf522SAndroid Build Coastguard Worker     /**
99*288bf522SAndroid Build Coastguard Worker      * Initializes the object for verifying a signed image file
100*288bf522SAndroid Build Coastguard Worker      * @param signature Signature footer
101*288bf522SAndroid Build Coastguard Worker      */
BootSignature(byte[] signature)102*288bf522SAndroid Build Coastguard Worker     public BootSignature(byte[] signature)
103*288bf522SAndroid Build Coastguard Worker             throws Exception {
104*288bf522SAndroid Build Coastguard Worker         ASN1InputStream stream = new ASN1InputStream(signature);
105*288bf522SAndroid Build Coastguard Worker         ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
106*288bf522SAndroid Build Coastguard Worker 
107*288bf522SAndroid Build Coastguard Worker         formatVersion = (ASN1Integer) sequence.getObjectAt(0);
108*288bf522SAndroid Build Coastguard Worker         if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
109*288bf522SAndroid Build Coastguard Worker             throw new IllegalArgumentException("Unsupported format version");
110*288bf522SAndroid Build Coastguard Worker         }
111*288bf522SAndroid Build Coastguard Worker 
112*288bf522SAndroid Build Coastguard Worker         certificate = sequence.getObjectAt(1);
113*288bf522SAndroid Build Coastguard Worker         byte[] encoded = ((ASN1Object) certificate).getEncoded();
114*288bf522SAndroid Build Coastguard Worker         ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
115*288bf522SAndroid Build Coastguard Worker 
116*288bf522SAndroid Build Coastguard Worker         CertificateFactory cf = CertificateFactory.getInstance("X.509");
117*288bf522SAndroid Build Coastguard Worker         X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
118*288bf522SAndroid Build Coastguard Worker         publicKey = c.getPublicKey();
119*288bf522SAndroid Build Coastguard Worker 
120*288bf522SAndroid Build Coastguard Worker         ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
121*288bf522SAndroid Build Coastguard Worker         algorithmIdentifier = new AlgorithmIdentifier(
122*288bf522SAndroid Build Coastguard Worker             (ASN1ObjectIdentifier) algId.getObjectAt(0));
123*288bf522SAndroid Build Coastguard Worker 
124*288bf522SAndroid Build Coastguard Worker         ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
125*288bf522SAndroid Build Coastguard Worker         target = (DERPrintableString) attrs.getObjectAt(0);
126*288bf522SAndroid Build Coastguard Worker         length = (ASN1Integer) attrs.getObjectAt(1);
127*288bf522SAndroid Build Coastguard Worker 
128*288bf522SAndroid Build Coastguard Worker         this.signature = (DEROctetString) sequence.getObjectAt(4);
129*288bf522SAndroid Build Coastguard Worker     }
130*288bf522SAndroid Build Coastguard Worker 
getAuthenticatedAttributes()131*288bf522SAndroid Build Coastguard Worker     public ASN1Object getAuthenticatedAttributes() {
132*288bf522SAndroid Build Coastguard Worker         ASN1EncodableVector attrs = new ASN1EncodableVector();
133*288bf522SAndroid Build Coastguard Worker         attrs.add(target);
134*288bf522SAndroid Build Coastguard Worker         attrs.add(length);
135*288bf522SAndroid Build Coastguard Worker         return new DERSequence(attrs);
136*288bf522SAndroid Build Coastguard Worker     }
137*288bf522SAndroid Build Coastguard Worker 
getEncodedAuthenticatedAttributes()138*288bf522SAndroid Build Coastguard Worker     public byte[] getEncodedAuthenticatedAttributes() throws IOException {
139*288bf522SAndroid Build Coastguard Worker         return getAuthenticatedAttributes().getEncoded();
140*288bf522SAndroid Build Coastguard Worker     }
141*288bf522SAndroid Build Coastguard Worker 
getAlgorithmIdentifier()142*288bf522SAndroid Build Coastguard Worker     public AlgorithmIdentifier getAlgorithmIdentifier() {
143*288bf522SAndroid Build Coastguard Worker         return algorithmIdentifier;
144*288bf522SAndroid Build Coastguard Worker     }
145*288bf522SAndroid Build Coastguard Worker 
getPublicKey()146*288bf522SAndroid Build Coastguard Worker     public PublicKey getPublicKey() {
147*288bf522SAndroid Build Coastguard Worker         return publicKey;
148*288bf522SAndroid Build Coastguard Worker     }
149*288bf522SAndroid Build Coastguard Worker 
getSignature()150*288bf522SAndroid Build Coastguard Worker     public byte[] getSignature() {
151*288bf522SAndroid Build Coastguard Worker         return signature.getOctets();
152*288bf522SAndroid Build Coastguard Worker     }
153*288bf522SAndroid Build Coastguard Worker 
setSignature(byte[] sig, AlgorithmIdentifier algId)154*288bf522SAndroid Build Coastguard Worker     public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
155*288bf522SAndroid Build Coastguard Worker         algorithmIdentifier = algId;
156*288bf522SAndroid Build Coastguard Worker         signature = new DEROctetString(sig);
157*288bf522SAndroid Build Coastguard Worker     }
158*288bf522SAndroid Build Coastguard Worker 
setCertificate(X509Certificate cert)159*288bf522SAndroid Build Coastguard Worker     public void setCertificate(X509Certificate cert)
160*288bf522SAndroid Build Coastguard Worker             throws Exception, IOException, CertificateEncodingException {
161*288bf522SAndroid Build Coastguard Worker         ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
162*288bf522SAndroid Build Coastguard Worker         certificate = s.readObject();
163*288bf522SAndroid Build Coastguard Worker         publicKey = cert.getPublicKey();
164*288bf522SAndroid Build Coastguard Worker     }
165*288bf522SAndroid Build Coastguard Worker 
generateSignableImage(byte[] image)166*288bf522SAndroid Build Coastguard Worker     public byte[] generateSignableImage(byte[] image) throws IOException {
167*288bf522SAndroid Build Coastguard Worker         byte[] attrs = getEncodedAuthenticatedAttributes();
168*288bf522SAndroid Build Coastguard Worker         byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
169*288bf522SAndroid Build Coastguard Worker         for (int i=0; i < attrs.length; i++) {
170*288bf522SAndroid Build Coastguard Worker             signable[i+image.length] = attrs[i];
171*288bf522SAndroid Build Coastguard Worker         }
172*288bf522SAndroid Build Coastguard Worker         return signable;
173*288bf522SAndroid Build Coastguard Worker     }
174*288bf522SAndroid Build Coastguard Worker 
sign(byte[] image, PrivateKey key)175*288bf522SAndroid Build Coastguard Worker     public byte[] sign(byte[] image, PrivateKey key) throws Exception {
176*288bf522SAndroid Build Coastguard Worker         byte[] signable = generateSignableImage(image);
177*288bf522SAndroid Build Coastguard Worker         return Utils.sign(key, signable);
178*288bf522SAndroid Build Coastguard Worker     }
179*288bf522SAndroid Build Coastguard Worker 
verify(byte[] image)180*288bf522SAndroid Build Coastguard Worker     public boolean verify(byte[] image) throws Exception {
181*288bf522SAndroid Build Coastguard Worker         if (length.getValue().intValue() != image.length) {
182*288bf522SAndroid Build Coastguard Worker             throw new IllegalArgumentException("Invalid image length");
183*288bf522SAndroid Build Coastguard Worker         }
184*288bf522SAndroid Build Coastguard Worker 
185*288bf522SAndroid Build Coastguard Worker         byte[] signable = generateSignableImage(image);
186*288bf522SAndroid Build Coastguard Worker         return Utils.verify(publicKey, signable, signature.getOctets(),
187*288bf522SAndroid Build Coastguard Worker                     algorithmIdentifier);
188*288bf522SAndroid Build Coastguard Worker     }
189*288bf522SAndroid Build Coastguard Worker 
toASN1Primitive()190*288bf522SAndroid Build Coastguard Worker     public ASN1Primitive toASN1Primitive() {
191*288bf522SAndroid Build Coastguard Worker         ASN1EncodableVector v = new ASN1EncodableVector();
192*288bf522SAndroid Build Coastguard Worker         v.add(formatVersion);
193*288bf522SAndroid Build Coastguard Worker         v.add(certificate);
194*288bf522SAndroid Build Coastguard Worker         v.add(algorithmIdentifier);
195*288bf522SAndroid Build Coastguard Worker         v.add(getAuthenticatedAttributes());
196*288bf522SAndroid Build Coastguard Worker         v.add(signature);
197*288bf522SAndroid Build Coastguard Worker         return new DERSequence(v);
198*288bf522SAndroid Build Coastguard Worker     }
199*288bf522SAndroid Build Coastguard Worker 
getSignableImageSize(byte[] data)200*288bf522SAndroid Build Coastguard Worker     public static int getSignableImageSize(byte[] data) throws Exception {
201*288bf522SAndroid Build Coastguard Worker         if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
202*288bf522SAndroid Build Coastguard Worker                 "ANDROID!".getBytes("US-ASCII"))) {
203*288bf522SAndroid Build Coastguard Worker             throw new IllegalArgumentException("Invalid image header: missing magic");
204*288bf522SAndroid Build Coastguard Worker         }
205*288bf522SAndroid Build Coastguard Worker 
206*288bf522SAndroid Build Coastguard Worker         ByteBuffer image = ByteBuffer.wrap(data);
207*288bf522SAndroid Build Coastguard Worker         image.order(ByteOrder.LITTLE_ENDIAN);
208*288bf522SAndroid Build Coastguard Worker 
209*288bf522SAndroid Build Coastguard Worker         image.getLong(); // magic
210*288bf522SAndroid Build Coastguard Worker         int kernelSize = image.getInt();
211*288bf522SAndroid Build Coastguard Worker         image.getInt(); // kernel_addr
212*288bf522SAndroid Build Coastguard Worker         int ramdskSize = image.getInt();
213*288bf522SAndroid Build Coastguard Worker         image.getInt(); // ramdisk_addr
214*288bf522SAndroid Build Coastguard Worker         int secondSize = image.getInt();
215*288bf522SAndroid Build Coastguard Worker         image.getLong(); // second_addr + tags_addr
216*288bf522SAndroid Build Coastguard Worker         int pageSize = image.getInt();
217*288bf522SAndroid Build Coastguard Worker 
218*288bf522SAndroid Build Coastguard Worker         int length = pageSize // include the page aligned image header
219*288bf522SAndroid Build Coastguard Worker                 + ((kernelSize + pageSize - 1) / pageSize) * pageSize
220*288bf522SAndroid Build Coastguard Worker                 + ((ramdskSize + pageSize - 1) / pageSize) * pageSize
221*288bf522SAndroid Build Coastguard Worker                 + ((secondSize + pageSize - 1) / pageSize) * pageSize;
222*288bf522SAndroid Build Coastguard Worker 
223*288bf522SAndroid Build Coastguard Worker         int headerVersion = image.getInt(); // boot image header version
224*288bf522SAndroid Build Coastguard Worker         if (headerVersion > 0) {
225*288bf522SAndroid Build Coastguard Worker             image.position(BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET);
226*288bf522SAndroid Build Coastguard Worker             int recoveryDtboLength = image.getInt();
227*288bf522SAndroid Build Coastguard Worker             length += ((recoveryDtboLength + pageSize - 1) / pageSize) * pageSize;
228*288bf522SAndroid Build Coastguard Worker 
229*288bf522SAndroid Build Coastguard Worker             image.getLong(); // recovery_dtbo address
230*288bf522SAndroid Build Coastguard Worker             int headerSize = image.getInt();
231*288bf522SAndroid Build Coastguard Worker             if (headerVersion == 2) {
232*288bf522SAndroid Build Coastguard Worker                 image.position(BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET);
233*288bf522SAndroid Build Coastguard Worker                 int dtbLength = image.getInt();
234*288bf522SAndroid Build Coastguard Worker                 length += ((dtbLength + pageSize - 1) / pageSize) * pageSize;
235*288bf522SAndroid Build Coastguard Worker                 image.getLong(); // dtb address
236*288bf522SAndroid Build Coastguard Worker             }
237*288bf522SAndroid Build Coastguard Worker             if (image.position() != headerSize) {
238*288bf522SAndroid Build Coastguard Worker                 throw new IllegalArgumentException(
239*288bf522SAndroid Build Coastguard Worker                         "Invalid image header: invalid header length");
240*288bf522SAndroid Build Coastguard Worker             }
241*288bf522SAndroid Build Coastguard Worker         }
242*288bf522SAndroid Build Coastguard Worker 
243*288bf522SAndroid Build Coastguard Worker         length = ((length + pageSize - 1) / pageSize) * pageSize;
244*288bf522SAndroid Build Coastguard Worker 
245*288bf522SAndroid Build Coastguard Worker         if (length <= 0) {
246*288bf522SAndroid Build Coastguard Worker             throw new IllegalArgumentException("Invalid image header: invalid length");
247*288bf522SAndroid Build Coastguard Worker         }
248*288bf522SAndroid Build Coastguard Worker 
249*288bf522SAndroid Build Coastguard Worker         return length;
250*288bf522SAndroid Build Coastguard Worker     }
251*288bf522SAndroid Build Coastguard Worker 
doSignature( String target, String imagePath, String keyPath, String certPath, String outPath)252*288bf522SAndroid Build Coastguard Worker     public static void doSignature( String target,
253*288bf522SAndroid Build Coastguard Worker                                     String imagePath,
254*288bf522SAndroid Build Coastguard Worker                                     String keyPath,
255*288bf522SAndroid Build Coastguard Worker                                     String certPath,
256*288bf522SAndroid Build Coastguard Worker                                     String outPath) throws Exception {
257*288bf522SAndroid Build Coastguard Worker 
258*288bf522SAndroid Build Coastguard Worker         byte[] image = Utils.read(imagePath);
259*288bf522SAndroid Build Coastguard Worker         int signableSize = getSignableImageSize(image);
260*288bf522SAndroid Build Coastguard Worker 
261*288bf522SAndroid Build Coastguard Worker         if (signableSize < image.length) {
262*288bf522SAndroid Build Coastguard Worker             System.err.println("NOTE: truncating file " + imagePath +
263*288bf522SAndroid Build Coastguard Worker                     " from " + image.length + " to " + signableSize + " bytes");
264*288bf522SAndroid Build Coastguard Worker             image = Arrays.copyOf(image, signableSize);
265*288bf522SAndroid Build Coastguard Worker         } else if (signableSize > image.length) {
266*288bf522SAndroid Build Coastguard Worker             throw new IllegalArgumentException("Invalid image: too short, expected " +
267*288bf522SAndroid Build Coastguard Worker                     signableSize + " bytes");
268*288bf522SAndroid Build Coastguard Worker         }
269*288bf522SAndroid Build Coastguard Worker 
270*288bf522SAndroid Build Coastguard Worker         BootSignature bootsig = new BootSignature(target, image.length);
271*288bf522SAndroid Build Coastguard Worker 
272*288bf522SAndroid Build Coastguard Worker         X509Certificate cert = Utils.loadPEMCertificate(certPath);
273*288bf522SAndroid Build Coastguard Worker         bootsig.setCertificate(cert);
274*288bf522SAndroid Build Coastguard Worker 
275*288bf522SAndroid Build Coastguard Worker         PrivateKey key = Utils.loadDERPrivateKeyFromFile(keyPath);
276*288bf522SAndroid Build Coastguard Worker         bootsig.setSignature(bootsig.sign(image, key),
277*288bf522SAndroid Build Coastguard Worker             Utils.getSignatureAlgorithmIdentifier(key));
278*288bf522SAndroid Build Coastguard Worker 
279*288bf522SAndroid Build Coastguard Worker         byte[] encoded_bootsig = bootsig.getEncoded();
280*288bf522SAndroid Build Coastguard Worker         byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length);
281*288bf522SAndroid Build Coastguard Worker 
282*288bf522SAndroid Build Coastguard Worker         System.arraycopy(encoded_bootsig, 0, image_with_metadata,
283*288bf522SAndroid Build Coastguard Worker                 image.length, encoded_bootsig.length);
284*288bf522SAndroid Build Coastguard Worker 
285*288bf522SAndroid Build Coastguard Worker         Utils.write(image_with_metadata, outPath);
286*288bf522SAndroid Build Coastguard Worker     }
287*288bf522SAndroid Build Coastguard Worker 
verifySignature(String imagePath, String certPath)288*288bf522SAndroid Build Coastguard Worker     public static void verifySignature(String imagePath, String certPath) throws Exception {
289*288bf522SAndroid Build Coastguard Worker         byte[] image = Utils.read(imagePath);
290*288bf522SAndroid Build Coastguard Worker         int signableSize = getSignableImageSize(image);
291*288bf522SAndroid Build Coastguard Worker 
292*288bf522SAndroid Build Coastguard Worker         if (signableSize >= image.length) {
293*288bf522SAndroid Build Coastguard Worker             throw new IllegalArgumentException("Invalid image: not signed");
294*288bf522SAndroid Build Coastguard Worker         }
295*288bf522SAndroid Build Coastguard Worker 
296*288bf522SAndroid Build Coastguard Worker         byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
297*288bf522SAndroid Build Coastguard Worker         BootSignature bootsig = new BootSignature(signature);
298*288bf522SAndroid Build Coastguard Worker 
299*288bf522SAndroid Build Coastguard Worker         if (!certPath.isEmpty()) {
300*288bf522SAndroid Build Coastguard Worker             System.err.println("NOTE: verifying using public key from " + certPath);
301*288bf522SAndroid Build Coastguard Worker             bootsig.setCertificate(Utils.loadPEMCertificate(certPath));
302*288bf522SAndroid Build Coastguard Worker         }
303*288bf522SAndroid Build Coastguard Worker 
304*288bf522SAndroid Build Coastguard Worker         try {
305*288bf522SAndroid Build Coastguard Worker             if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
306*288bf522SAndroid Build Coastguard Worker                 System.err.println("Signature is VALID");
307*288bf522SAndroid Build Coastguard Worker                 System.exit(0);
308*288bf522SAndroid Build Coastguard Worker             } else {
309*288bf522SAndroid Build Coastguard Worker                 System.err.println("Signature is INVALID");
310*288bf522SAndroid Build Coastguard Worker             }
311*288bf522SAndroid Build Coastguard Worker         } catch (Exception e) {
312*288bf522SAndroid Build Coastguard Worker             e.printStackTrace(System.err);
313*288bf522SAndroid Build Coastguard Worker         }
314*288bf522SAndroid Build Coastguard Worker         System.exit(1);
315*288bf522SAndroid Build Coastguard Worker     }
316*288bf522SAndroid Build Coastguard Worker 
317*288bf522SAndroid Build Coastguard Worker     /* Example usage for signing a boot image using dev keys:
318*288bf522SAndroid Build Coastguard Worker         java -cp \
319*288bf522SAndroid Build Coastguard Worker             ../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/ \
320*288bf522SAndroid Build Coastguard Worker                 classes/com.android.verity.BootSignature \
321*288bf522SAndroid Build Coastguard Worker             /boot \
322*288bf522SAndroid Build Coastguard Worker             ../../../out/target/product/$PRODUCT/boot.img \
323*288bf522SAndroid Build Coastguard Worker             ../../../build/target/product/security/verity.pk8 \
324*288bf522SAndroid Build Coastguard Worker             ../../../build/target/product/security/verity.x509.pem \
325*288bf522SAndroid Build Coastguard Worker             /tmp/boot.img.signed
326*288bf522SAndroid Build Coastguard Worker     */
main(String[] args)327*288bf522SAndroid Build Coastguard Worker     public static void main(String[] args) throws Exception {
328*288bf522SAndroid Build Coastguard Worker         Security.addProvider(new BouncyCastleProvider());
329*288bf522SAndroid Build Coastguard Worker 
330*288bf522SAndroid Build Coastguard Worker         if ("-verify".equals(args[0])) {
331*288bf522SAndroid Build Coastguard Worker             String certPath = "";
332*288bf522SAndroid Build Coastguard Worker 
333*288bf522SAndroid Build Coastguard Worker             if (args.length >= 4 && "-certificate".equals(args[2])) {
334*288bf522SAndroid Build Coastguard Worker                 /* args[3] is the path to a public key certificate */
335*288bf522SAndroid Build Coastguard Worker                 certPath = args[3];
336*288bf522SAndroid Build Coastguard Worker             }
337*288bf522SAndroid Build Coastguard Worker 
338*288bf522SAndroid Build Coastguard Worker             /* args[1] is the path to a signed boot image */
339*288bf522SAndroid Build Coastguard Worker             verifySignature(args[1], certPath);
340*288bf522SAndroid Build Coastguard Worker         } else {
341*288bf522SAndroid Build Coastguard Worker             /* args[0] is the target name, typically /boot
342*288bf522SAndroid Build Coastguard Worker                args[1] is the path to a boot image to sign
343*288bf522SAndroid Build Coastguard Worker                args[2] is the path to a private key
344*288bf522SAndroid Build Coastguard Worker                args[3] is the path to the matching public key certificate
345*288bf522SAndroid Build Coastguard Worker                args[4] is the path where to output the signed boot image
346*288bf522SAndroid Build Coastguard Worker             */
347*288bf522SAndroid Build Coastguard Worker             doSignature(args[0], args[1], args[2], args[3], args[4]);
348*288bf522SAndroid Build Coastguard Worker         }
349*288bf522SAndroid Build Coastguard Worker     }
350*288bf522SAndroid Build Coastguard Worker }
351