OSDN Git Service

am 29131b97: Reinstate "Update boot image signature format to version 1"
[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.IOException;
20 import java.nio.ByteBuffer;
21 import java.nio.ByteOrder;
22 import java.security.PrivateKey;
23 import java.security.Security;
24 import java.security.cert.X509Certificate;
25 import java.security.cert.CertificateEncodingException;
26 import java.util.Arrays;
27 import org.bouncycastle.asn1.ASN1Encodable;
28 import org.bouncycastle.asn1.ASN1EncodableVector;
29 import org.bouncycastle.asn1.ASN1Integer;
30 import org.bouncycastle.asn1.ASN1Object;
31 import org.bouncycastle.asn1.ASN1Primitive;
32 import org.bouncycastle.asn1.ASN1InputStream;
33 import org.bouncycastle.asn1.DEROctetString;
34 import org.bouncycastle.asn1.DERPrintableString;
35 import org.bouncycastle.asn1.DERSequence;
36 import org.bouncycastle.asn1.util.ASN1Dump;
37 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
38 import org.bouncycastle.jce.provider.BouncyCastleProvider;
39
40 /**
41  *    AndroidVerifiedBootSignature DEFINITIONS ::=
42  *    BEGIN
43  *        formatVersion ::= INTEGER
44  *        certificate ::= Certificate
45  *        algorithmIdentifier ::= SEQUENCE {
46  *            algorithm OBJECT IDENTIFIER,
47  *            parameters ANY DEFINED BY algorithm OPTIONAL
48  *        }
49  *        authenticatedAttributes ::= SEQUENCE {
50  *            target CHARACTER STRING,
51  *            length INTEGER
52  *        }
53  *        signature ::= OCTET STRING
54  *     END
55  */
56
57 public class BootSignature extends ASN1Object
58 {
59     private ASN1Integer             formatVersion;
60     private ASN1Encodable           certificate;
61     private AlgorithmIdentifier     algorithmIdentifier;
62     private DERPrintableString      target;
63     private ASN1Integer             length;
64     private DEROctetString          signature;
65
66     private static final int FORMAT_VERSION = 1;
67
68     public BootSignature(String target, int length) {
69         this.formatVersion = new ASN1Integer(FORMAT_VERSION);
70         this.target = new DERPrintableString(target);
71         this.length = new ASN1Integer(length);
72     }
73
74     public ASN1Object getAuthenticatedAttributes() {
75         ASN1EncodableVector attrs = new ASN1EncodableVector();
76         attrs.add(target);
77         attrs.add(length);
78         return new DERSequence(attrs);
79     }
80
81     public byte[] getEncodedAuthenticatedAttributes() throws IOException {
82         return getAuthenticatedAttributes().getEncoded();
83     }
84
85     public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
86         algorithmIdentifier = algId;
87         signature = new DEROctetString(sig);
88     }
89
90     public void setCertificate(X509Certificate cert)
91             throws Exception, IOException, CertificateEncodingException {
92         ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
93         certificate = s.readObject();
94     }
95
96     public byte[] generateSignableImage(byte[] image) throws IOException {
97         byte[] attrs = getEncodedAuthenticatedAttributes();
98         byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
99         for (int i=0; i < attrs.length; i++) {
100             signable[i+image.length] = attrs[i];
101         }
102         return signable;
103     }
104
105     public byte[] sign(byte[] image, PrivateKey key) throws Exception {
106         byte[] signable = generateSignableImage(image);
107         return Utils.sign(key, signable);
108     }
109
110     public ASN1Primitive toASN1Primitive() {
111         ASN1EncodableVector v = new ASN1EncodableVector();
112         v.add(formatVersion);
113         v.add(certificate);
114         v.add(algorithmIdentifier);
115         v.add(getAuthenticatedAttributes());
116         v.add(signature);
117         return new DERSequence(v);
118     }
119
120     public static int getSignableImageSize(byte[] data) throws Exception {
121         if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
122                 "ANDROID!".getBytes("US-ASCII"))) {
123             throw new IllegalArgumentException("Invalid image header: missing magic");
124         }
125
126         ByteBuffer image = ByteBuffer.wrap(data);
127         image.order(ByteOrder.LITTLE_ENDIAN);
128
129         image.getLong(); // magic
130         int kernelSize = image.getInt();
131         image.getInt(); // kernel_addr
132         int ramdskSize = image.getInt();
133         image.getInt(); // ramdisk_addr
134         int secondSize = image.getInt();
135         image.getLong(); // second_addr + tags_addr
136         int pageSize = image.getInt();
137
138         int length = pageSize // include the page aligned image header
139                 + ((kernelSize + pageSize - 1) / pageSize) * pageSize
140                 + ((ramdskSize + pageSize - 1) / pageSize) * pageSize
141                 + ((secondSize + pageSize - 1) / pageSize) * pageSize;
142
143         length = ((length + pageSize - 1) / pageSize) * pageSize;
144
145         if (length <= 0) {
146             throw new IllegalArgumentException("Invalid image header: invalid length");
147         }
148
149         return length;
150     }
151
152     public static void doSignature( String target,
153                                     String imagePath,
154                                     String keyPath,
155                                     String certPath,
156                                     String outPath) throws Exception {
157
158         byte[] image = Utils.read(imagePath);
159         int signableSize = getSignableImageSize(image);
160
161         if (signableSize < image.length) {
162             System.err.println("NOTE: truncating file " + imagePath +
163                     " from " + image.length + " to " + signableSize + " bytes");
164             image = Arrays.copyOf(image, signableSize);
165         } else if (signableSize > image.length) {
166             throw new IllegalArgumentException("Invalid image: too short, expected " +
167                     signableSize + " bytes");
168         }
169
170         BootSignature bootsig = new BootSignature(target, image.length);
171
172         X509Certificate cert = Utils.loadPEMCertificate(certPath);
173         bootsig.setCertificate(cert);
174
175         PrivateKey key = Utils.loadDERPrivateKeyFromFile(keyPath);
176         bootsig.setSignature(bootsig.sign(image, key),
177             Utils.getSignatureAlgorithmIdentifier(key));
178
179         byte[] encoded_bootsig = bootsig.getEncoded();
180         byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length);
181
182         System.arraycopy(encoded_bootsig, 0, image_with_metadata,
183                 image.length, encoded_bootsig.length);
184
185         Utils.write(image_with_metadata, outPath);
186     }
187
188     /* java -cp
189         ../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/\
190             classes/com.android.verity.BootSignature \
191         boot \
192         ../../../out/target/product/flounder/boot.img \
193         ../../../build/target/product/security/verity_private_dev_key \
194         ../../../build/target/product/security/verity.pk8 \
195         ../../../build/target/product/security/verity.x509.pem \
196         /tmp/boot.img.signed
197     */
198     public static void main(String[] args) throws Exception {
199         Security.addProvider(new BouncyCastleProvider());
200         doSignature(args[0], args[1], args[2], args[3], args[4]);
201     }
202 }