2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.verity;
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;
41 * AndroidVerifiedBootSignature DEFINITIONS ::=
43 * formatVersion ::= INTEGER
44 * certificate ::= Certificate
45 * algorithmIdentifier ::= SEQUENCE {
46 * algorithm OBJECT IDENTIFIER,
47 * parameters ANY DEFINED BY algorithm OPTIONAL
49 * authenticatedAttributes ::= SEQUENCE {
50 * target CHARACTER STRING,
53 * signature ::= OCTET STRING
57 public class BootSignature extends ASN1Object
59 private ASN1Integer formatVersion;
60 private ASN1Encodable certificate;
61 private AlgorithmIdentifier algorithmIdentifier;
62 private DERPrintableString target;
63 private ASN1Integer length;
64 private DEROctetString signature;
66 private static final int FORMAT_VERSION = 1;
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);
74 public ASN1Object getAuthenticatedAttributes() {
75 ASN1EncodableVector attrs = new ASN1EncodableVector();
78 return new DERSequence(attrs);
81 public byte[] getEncodedAuthenticatedAttributes() throws IOException {
82 return getAuthenticatedAttributes().getEncoded();
85 public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
86 algorithmIdentifier = algId;
87 signature = new DEROctetString(sig);
90 public void setCertificate(X509Certificate cert)
91 throws Exception, IOException, CertificateEncodingException {
92 ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
93 certificate = s.readObject();
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];
105 public byte[] sign(byte[] image, PrivateKey key) throws Exception {
106 byte[] signable = generateSignableImage(image);
107 return Utils.sign(key, signable);
110 public ASN1Primitive toASN1Primitive() {
111 ASN1EncodableVector v = new ASN1EncodableVector();
112 v.add(formatVersion);
114 v.add(algorithmIdentifier);
115 v.add(getAuthenticatedAttributes());
117 return new DERSequence(v);
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");
126 ByteBuffer image = ByteBuffer.wrap(data);
127 image.order(ByteOrder.LITTLE_ENDIAN);
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();
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;
143 length = ((length + pageSize - 1) / pageSize) * pageSize;
146 throw new IllegalArgumentException("Invalid image header: invalid length");
152 public static void doSignature( String target,
156 String outPath) throws Exception {
158 byte[] image = Utils.read(imagePath);
159 int signableSize = getSignableImageSize(image);
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");
170 BootSignature bootsig = new BootSignature(target, image.length);
172 X509Certificate cert = Utils.loadPEMCertificate(certPath);
173 bootsig.setCertificate(cert);
175 PrivateKey key = Utils.loadDERPrivateKeyFromFile(keyPath);
176 bootsig.setSignature(bootsig.sign(image, key),
177 Utils.getSignatureAlgorithmIdentifier(key));
179 byte[] encoded_bootsig = bootsig.getEncoded();
180 byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length);
182 System.arraycopy(encoded_bootsig, 0, image_with_metadata,
183 image.length, encoded_bootsig.length);
185 Utils.write(image_with_metadata, outPath);
189 ../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/\
190 classes/com.android.verity.BootSignature \
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 \
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]);