blob: d35ce5b2c3f8e1168bea9d2f895abba144c6e570 [file] [log] [blame]
Alex Buynytskyycd4d3872020-02-08 17:50:50 -08001/*
2 * Copyright (C) 2020 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 android.os.incremental;
18
Todd Kennedy75dd0e62020-04-01 10:48:57 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
Songchun Fan38dfe9a2020-02-20 18:12:47 -080021import android.os.ParcelFileDescriptor;
22
Alex Buynytskyy17d9da02020-02-12 16:03:30 -080023import java.io.ByteArrayInputStream;
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -080024import java.io.ByteArrayOutputStream;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070025import java.io.EOFException;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080026import java.io.IOException;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070027import java.io.InputStream;
28import java.io.OutputStream;
29import java.nio.ByteBuffer;
30import java.nio.ByteOrder;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080031
32/**
33 * V4 signature fields.
34 * Keep in sync with APKSig master copy.
35 * @hide
36 */
37public class V4Signature {
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -080038 public static final String EXT = ".idsig";
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070039 public static final int SUPPORTED_VERSION = 2;
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -080040
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070041 public static final int HASHING_ALGORITHM_SHA256 = 1;
42 public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
43
44 /**
45 * IncFS hashing data.
46 */
47 public static class HashingInfo {
48 public final int hashAlgorithm; // only 1 == SHA256 supported
49 public final byte log2BlockSize; // only 12 (block size 4096) supported now
Todd Kennedy75dd0e62020-04-01 10:48:57 -070050 @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
51 @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070052
53 HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
54 this.hashAlgorithm = hashAlgorithm;
55 this.log2BlockSize = log2BlockSize;
56 this.salt = salt;
57 this.rawRootHash = rawRootHash;
58 }
59
60 /**
61 * Constructs HashingInfo from byte array.
62 */
Todd Kennedy75dd0e62020-04-01 10:48:57 -070063 @NonNull
64 public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070065 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
66 final int hashAlgorithm = buffer.getInt();
67 final byte log2BlockSize = buffer.get();
68 byte[] salt = readBytes(buffer);
69 byte[] rawRootHash = readBytes(buffer);
70 return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
71 }
72 }
73
74 /**
75 * V4 signature data.
76 */
77 public static class SigningInfo {
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -070078 public final byte[] apkDigest; // used to match with the corresponding APK
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070079 public final byte[] certificate; // ASN.1 DER form
80 public final byte[] additionalData; // a free-form binary data blob
81 public final byte[] publicKey; // ASN.1 DER, must match the certificate
82 public final int signatureAlgorithmId; // see the APK v2 doc for the list
83 public final byte[] signature;
84
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -070085 SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070086 byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -070087 this.apkDigest = apkDigest;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070088 this.certificate = certificate;
89 this.additionalData = additionalData;
90 this.publicKey = publicKey;
91 this.signatureAlgorithmId = signatureAlgorithmId;
92 this.signature = signature;
93 }
94
95 /**
96 * Constructs SigningInfo from byte array.
97 */
98 public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
99 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -0700100 byte[] apkDigest = readBytes(buffer);
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700101 byte[] certificate = readBytes(buffer);
102 byte[] additionalData = readBytes(buffer);
103 byte[] publicKey = readBytes(buffer);
104 int signatureAlgorithmId = buffer.getInt();
105 byte[] signature = readBytes(buffer);
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -0700106 return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700107 signatureAlgorithmId, signature);
108 }
109 }
110
111 public final int version; // Always 2 for now.
Todd Kennedy75dd0e62020-04-01 10:48:57 -0700112 /**
113 * Raw byte array containing the IncFS hashing data.
114 * @see HashingInfo#fromByteArray(byte[])
115 */
116 @Nullable public final byte[] hashingInfo;
117
118 /**
119 * Raw byte array containing the V4 signature data.
120 * <p>Passed as-is to the kernel. Can be retrieved later.
121 * @see SigningInfo#fromByteArray(byte[])
122 */
123 @Nullable public final byte[] signingInfo;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800124
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800125 /**
126 * Construct a V4Signature from .idsig file.
127 */
Songchun Fan38dfe9a2020-02-20 18:12:47 -0800128 public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700129 try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) {
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800130 return readFrom(stream);
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800131 }
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800132 }
133
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800134 /**
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700135 * Construct a V4Signature from a byte array.
Alex Buynytskyy17d9da02020-02-12 16:03:30 -0800136 */
Todd Kennedy75dd0e62020-04-01 10:48:57 -0700137 @NonNull
138 public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700139 try (InputStream stream = new ByteArrayInputStream(bytes)) {
Alex Buynytskyy17d9da02020-02-12 16:03:30 -0800140 return readFrom(stream);
141 }
142 }
143
144 /**
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800145 * Store the V4Signature to a byte-array.
146 */
147 public byte[] toByteArray() {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700148 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
149 this.writeTo(stream);
150 return stream.toByteArray();
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800151 } catch (IOException e) {
152 return null;
153 }
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800154 }
155
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700156 /**
157 * Combines necessary data to a signed data blob.
158 * The blob can be validated against signingInfo.signature.
159 *
160 * @param fileSize - size of the signed file (APK)
161 */
162 public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
163 SigningInfo signingInfo) {
164 final int size =
165 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
166 hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -0700167 signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700168 signingInfo.additionalData);
169 ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
170 buffer.putInt(size);
171 buffer.putLong(fileSize);
172 buffer.putInt(hashingInfo.hashAlgorithm);
173 buffer.put(hashingInfo.log2BlockSize);
174 writeBytes(buffer, hashingInfo.salt);
175 writeBytes(buffer, hashingInfo.rawRootHash);
Alex Buynytskyy6b7efbc2020-03-23 18:23:15 -0700176 writeBytes(buffer, signingInfo.apkDigest);
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700177 writeBytes(buffer, signingInfo.certificate);
178 writeBytes(buffer, signingInfo.additionalData);
179 return buffer.array();
180 }
181
182 public boolean isVersionSupported() {
Yurii Zubrytskyidf7c0502020-02-19 14:02:14 -0800183 return this.version == SUPPORTED_VERSION;
184 }
185
Todd Kennedy75dd0e62020-04-01 10:48:57 -0700186 private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) {
Yurii Zubrytskyidf7c0502020-02-19 14:02:14 -0800187 this.version = version;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700188 this.hashingInfo = hashingInfo;
189 this.signingInfo = signingInfo;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800190 }
191
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700192 private static V4Signature readFrom(InputStream stream) throws IOException {
193 final int version = readIntLE(stream);
194 final byte[] hashingInfo = readBytes(stream);
195 final byte[] signingInfo = readBytes(stream);
196 return new V4Signature(version, hashingInfo, signingInfo);
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800197 }
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800198
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700199 private void writeTo(OutputStream stream) throws IOException {
200 writeIntLE(stream, this.version);
201 writeBytes(stream, this.hashingInfo);
202 writeBytes(stream, this.signingInfo);
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800203 }
204
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700205 // Utility methods.
206 private static int bytesSize(byte[] bytes) {
207 return 4/*length*/ + (bytes == null ? 0 : bytes.length);
208 }
209
210 private static void readFully(InputStream stream, byte[] buffer) throws IOException {
211 int len = buffer.length;
212 int n = 0;
213 while (n < len) {
214 int count = stream.read(buffer, n, len - n);
215 if (count < 0) {
216 throw new EOFException();
217 }
218 n += count;
219 }
220 }
221
222 private static int readIntLE(InputStream stream) throws IOException {
223 final byte[] buffer = new byte[4];
224 readFully(stream, buffer);
225 return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
226 }
227
228 private static void writeIntLE(OutputStream stream, int v) throws IOException {
229 final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(
230 v).array();
231 stream.write(buffer);
232 }
233
234 private static byte[] readBytes(InputStream stream) throws IOException {
235 try {
236 final int size = readIntLE(stream);
237 final byte[] bytes = new byte[size];
238 readFully(stream, bytes);
239 return bytes;
240 } catch (EOFException ignored) {
241 return null;
242 }
243 }
244
245 private static byte[] readBytes(ByteBuffer buffer) throws IOException {
246 if (buffer.remaining() < 4) {
247 throw new EOFException();
248 }
249 final int size = buffer.getInt();
250 if (buffer.remaining() < size) {
251 throw new EOFException();
252 }
253 final byte[] bytes = new byte[size];
254 buffer.get(bytes);
255 return bytes;
256 }
257
258 private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException {
259 if (bytes == null) {
260 writeIntLE(stream, 0);
261 return;
262 }
263 writeIntLE(stream, bytes.length);
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800264 stream.write(bytes);
265 }
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700266
267 private static void writeBytes(ByteBuffer buffer, byte[] bytes) {
268 if (bytes == null) {
269 buffer.putInt(0);
270 return;
271 }
272 buffer.putInt(bytes.length);
273 buffer.put(bytes);
274 }
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800275}