OSDN Git Service

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