blob: 4ee3309de56b888114fb145eb73361a9201688b1 [file] [log] [blame]
Geremy Condracee5bfd2014-06-11 13:38:45 -07001/*
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
17package com.android.verity;
18
19import java.io.IOException;
Paul Lawrence29131b92014-11-13 22:15:30 +000020import java.nio.ByteBuffer;
21import java.nio.ByteOrder;
Geremy Condracee5bfd2014-06-11 13:38:45 -070022import java.security.PrivateKey;
Paul Lawrence29131b92014-11-13 22:15:30 +000023import java.security.Security;
24import java.security.cert.X509Certificate;
25import java.security.cert.CertificateEncodingException;
Geremy Condracee5bfd2014-06-11 13:38:45 -070026import java.util.Arrays;
27import org.bouncycastle.asn1.ASN1Encodable;
28import org.bouncycastle.asn1.ASN1EncodableVector;
29import org.bouncycastle.asn1.ASN1Integer;
30import org.bouncycastle.asn1.ASN1Object;
31import org.bouncycastle.asn1.ASN1Primitive;
Paul Lawrence29131b92014-11-13 22:15:30 +000032import org.bouncycastle.asn1.ASN1InputStream;
Geremy Condracee5bfd2014-06-11 13:38:45 -070033import org.bouncycastle.asn1.DEROctetString;
34import org.bouncycastle.asn1.DERPrintableString;
35import org.bouncycastle.asn1.DERSequence;
Geremy Condracee5bfd2014-06-11 13:38:45 -070036import org.bouncycastle.asn1.util.ASN1Dump;
37import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
Paul Lawrence29131b92014-11-13 22:15:30 +000038import org.bouncycastle.jce.provider.BouncyCastleProvider;
Geremy Condracee5bfd2014-06-11 13:38:45 -070039
40/**
41 * AndroidVerifiedBootSignature DEFINITIONS ::=
42 * BEGIN
Paul Lawrence29131b92014-11-13 22:15:30 +000043 * formatVersion ::= INTEGER
44 * certificate ::= Certificate
45 * algorithmIdentifier ::= SEQUENCE {
Geremy Condracee5bfd2014-06-11 13:38:45 -070046 * algorithm OBJECT IDENTIFIER,
47 * parameters ANY DEFINED BY algorithm OPTIONAL
48 * }
Paul Lawrence29131b92014-11-13 22:15:30 +000049 * authenticatedAttributes ::= SEQUENCE {
Geremy Condracee5bfd2014-06-11 13:38:45 -070050 * target CHARACTER STRING,
51 * length INTEGER
52 * }
Paul Lawrence29131b92014-11-13 22:15:30 +000053 * signature ::= OCTET STRING
Geremy Condracee5bfd2014-06-11 13:38:45 -070054 * END
55 */
56
57public class BootSignature extends ASN1Object
58{
59 private ASN1Integer formatVersion;
Paul Lawrence29131b92014-11-13 22:15:30 +000060 private ASN1Encodable certificate;
Geremy Condracee5bfd2014-06-11 13:38:45 -070061 private AlgorithmIdentifier algorithmIdentifier;
62 private DERPrintableString target;
63 private ASN1Integer length;
64 private DEROctetString signature;
65
Paul Lawrence29131b92014-11-13 22:15:30 +000066 private static final int FORMAT_VERSION = 1;
67
Geremy Condracee5bfd2014-06-11 13:38:45 -070068 public BootSignature(String target, int length) {
Paul Lawrence29131b92014-11-13 22:15:30 +000069 this.formatVersion = new ASN1Integer(FORMAT_VERSION);
Geremy Condracee5bfd2014-06-11 13:38:45 -070070 this.target = new DERPrintableString(target);
71 this.length = new ASN1Integer(length);
Geremy Condracee5bfd2014-06-11 13:38:45 -070072 }
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
Paul Lawrence29131b92014-11-13 22:15:30 +000085 public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
86 algorithmIdentifier = algId;
Geremy Condracee5bfd2014-06-11 13:38:45 -070087 signature = new DEROctetString(sig);
88 }
89
Paul Lawrence29131b92014-11-13 22:15:30 +000090 public void setCertificate(X509Certificate cert)
91 throws Exception, IOException, CertificateEncodingException {
92 ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
93 certificate = s.readObject();
94 }
95
Geremy Condracee5bfd2014-06-11 13:38:45 -070096 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);
Geremy Condrad66cefd2014-08-14 16:44:31 -0700107 return Utils.sign(key, signable);
Geremy Condracee5bfd2014-06-11 13:38:45 -0700108 }
109
110 public ASN1Primitive toASN1Primitive() {
111 ASN1EncodableVector v = new ASN1EncodableVector();
112 v.add(formatVersion);
Paul Lawrence29131b92014-11-13 22:15:30 +0000113 v.add(certificate);
Geremy Condracee5bfd2014-06-11 13:38:45 -0700114 v.add(algorithmIdentifier);
115 v.add(getAuthenticatedAttributes());
116 v.add(signature);
117 return new DERSequence(v);
118 }
119
Paul Lawrence29131b92014-11-13 22:15:30 +0000120 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
Geremy Condracee5bfd2014-06-11 13:38:45 -0700152 public static void doSignature( String target,
153 String imagePath,
154 String keyPath,
Paul Lawrence29131b92014-11-13 22:15:30 +0000155 String certPath,
Geremy Condracee5bfd2014-06-11 13:38:45 -0700156 String outPath) throws Exception {
Paul Lawrence29131b92014-11-13 22:15:30 +0000157
Geremy Condracee5bfd2014-06-11 13:38:45 -0700158 byte[] image = Utils.read(imagePath);
Paul Lawrence29131b92014-11-13 22:15:30 +0000159 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
Geremy Condracee5bfd2014-06-11 13:38:45 -0700170 BootSignature bootsig = new BootSignature(target, image.length);
Paul Lawrence29131b92014-11-13 22:15:30 +0000171
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
Geremy Condrad66cefd2014-08-14 16:44:31 -0700179 byte[] encoded_bootsig = bootsig.getEncoded();
180 byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length);
Paul Lawrence29131b92014-11-13 22:15:30 +0000181
182 System.arraycopy(encoded_bootsig, 0, image_with_metadata,
183 image.length, encoded_bootsig.length);
184
Geremy Condrad66cefd2014-08-14 16:44:31 -0700185 Utils.write(image_with_metadata, outPath);
Geremy Condracee5bfd2014-06-11 13:38:45 -0700186 }
187
Paul Lawrence29131b92014-11-13 22:15:30 +0000188 /* 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 */
Geremy Condracee5bfd2014-06-11 13:38:45 -0700198 public static void main(String[] args) throws Exception {
Paul Lawrence29131b92014-11-13 22:15:30 +0000199 Security.addProvider(new BouncyCastleProvider());
200 doSignature(args[0], args[1], args[2], args[3], args[4]);
Geremy Condracee5bfd2014-06-11 13:38:45 -0700201 }
Paul Lawrence29131b92014-11-13 22:15:30 +0000202}