OSDN Git Service

ANRdaemon: move trace result from /sdcard to /data
[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     }
153
154     public byte[] generateSignableImage(byte[] image) throws IOException {
155         byte[] attrs = getEncodedAuthenticatedAttributes();
156         byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
157         for (int i=0; i < attrs.length; i++) {
158             signable[i+image.length] = attrs[i];
159         }
160         return signable;
161     }
162
163     public byte[] sign(byte[] image, PrivateKey key) throws Exception {
164         byte[] signable = generateSignableImage(image);
165         return Utils.sign(key, signable);
166     }
167
168     public boolean verify(byte[] image) throws Exception {
169         if (length.getValue().intValue() != image.length) {
170             throw new IllegalArgumentException("Invalid image length");
171         }
172
173         byte[] signable = generateSignableImage(image);
174         return Utils.verify(publicKey, signable, signature.getOctets(),
175                     algorithmIdentifier);
176     }
177
178     public ASN1Primitive toASN1Primitive() {
179         ASN1EncodableVector v = new ASN1EncodableVector();
180         v.add(formatVersion);
181         v.add(certificate);
182         v.add(algorithmIdentifier);
183         v.add(getAuthenticatedAttributes());
184         v.add(signature);
185         return new DERSequence(v);
186     }
187
188     public static int getSignableImageSize(byte[] data) throws Exception {
189         if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
190                 "ANDROID!".getBytes("US-ASCII"))) {
191             throw new IllegalArgumentException("Invalid image header: missing magic");
192         }
193
194         ByteBuffer image = ByteBuffer.wrap(data);
195         image.order(ByteOrder.LITTLE_ENDIAN);
196
197         image.getLong(); // magic
198         int kernelSize = image.getInt();
199         image.getInt(); // kernel_addr
200         int ramdskSize = image.getInt();
201         image.getInt(); // ramdisk_addr
202         int secondSize = image.getInt();
203         image.getLong(); // second_addr + tags_addr
204         int pageSize = image.getInt();
205
206         int length = pageSize // include the page aligned image header
207                 + ((kernelSize + pageSize - 1) / pageSize) * pageSize
208                 + ((ramdskSize + pageSize - 1) / pageSize) * pageSize
209                 + ((secondSize + pageSize - 1) / pageSize) * pageSize;
210
211         length = ((length + pageSize - 1) / pageSize) * pageSize;
212
213         if (length <= 0) {
214             throw new IllegalArgumentException("Invalid image header: invalid length");
215         }
216
217         return length;
218     }
219
220     public static void doSignature( String target,
221                                     String imagePath,
222                                     String keyPath,
223                                     String certPath,
224                                     String outPath) throws Exception {
225
226         byte[] image = Utils.read(imagePath);
227         int signableSize = getSignableImageSize(image);
228
229         if (signableSize < image.length) {
230             System.err.println("NOTE: truncating file " + imagePath +
231                     " from " + image.length + " to " + signableSize + " bytes");
232             image = Arrays.copyOf(image, signableSize);
233         } else if (signableSize > image.length) {
234             throw new IllegalArgumentException("Invalid image: too short, expected " +
235                     signableSize + " bytes");
236         }
237
238         BootSignature bootsig = new BootSignature(target, image.length);
239
240         X509Certificate cert = Utils.loadPEMCertificate(certPath);
241         bootsig.setCertificate(cert);
242
243         PrivateKey key = Utils.loadDERPrivateKeyFromFile(keyPath);
244         bootsig.setSignature(bootsig.sign(image, key),
245             Utils.getSignatureAlgorithmIdentifier(key));
246
247         byte[] encoded_bootsig = bootsig.getEncoded();
248         byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length);
249
250         System.arraycopy(encoded_bootsig, 0, image_with_metadata,
251                 image.length, encoded_bootsig.length);
252
253         Utils.write(image_with_metadata, outPath);
254     }
255
256     public static void verifySignature(String imagePath) throws Exception {
257         byte[] image = Utils.read(imagePath);
258         int signableSize = getSignableImageSize(image);
259
260         if (signableSize >= image.length) {
261             throw new IllegalArgumentException("Invalid image: not signed");
262         }
263
264         byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
265         BootSignature bootsig = new BootSignature(signature);
266
267         try {
268             if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
269                 System.err.println("Signature is VALID");
270                 System.exit(0);
271             } else {
272                 System.err.println("Signature is INVALID");
273             }
274         } catch (Exception e) {
275             e.printStackTrace(System.err);
276         }
277         System.exit(1);
278     }
279
280     /* Example usage for signing a boot image using dev keys:
281         java -cp \
282             ../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/ \
283                 classes/com.android.verity.BootSignature \
284             /boot \
285             ../../../out/target/product/$PRODUCT/boot.img \
286             ../../../build/target/product/security/verity.pk8 \
287             ../../../build/target/product/security/verity.x509.pem \
288             /tmp/boot.img.signed
289     */
290     public static void main(String[] args) throws Exception {
291         Security.addProvider(new BouncyCastleProvider());
292
293         if ("-verify".equals(args[0])) {
294             /* args[1] is the path to a signed boot image */
295             verifySignature(args[1]);
296         } else {
297             /* args[0] is the target name, typically /boot
298                args[1] is the path to a boot image to sign
299                args[2] is the path to a private key
300                args[3] is the path to the matching public key certificate
301                args[4] is the path where to output the signed boot image
302             */
303             doSignature(args[0], args[1], args[2], args[3], args[4]);
304         }
305     }
306 }