blob: 71f931da1a926066b3c798d8ea5e25542823f55b [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
Songchun Fan38dfe9a2020-02-20 18:12:47 -080019import android.os.ParcelFileDescriptor;
20
Alex Buynytskyy17d9da02020-02-12 16:03:30 -080021import java.io.ByteArrayInputStream;
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -080022import java.io.ByteArrayOutputStream;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070023import java.io.EOFException;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080024import java.io.IOException;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070025import java.io.InputStream;
26import java.io.OutputStream;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080029
30/**
31 * V4 signature fields.
32 * Keep in sync with APKSig master copy.
33 * @hide
34 */
35public class V4Signature {
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -080036 public static final String EXT = ".idsig";
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070037 public static final int SUPPORTED_VERSION = 2;
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -080038
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -070039 public static final int HASHING_ALGORITHM_SHA256 = 1;
40 public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
41
42 /**
43 * IncFS hashing data.
44 */
45 public static class HashingInfo {
46 public final int hashAlgorithm; // only 1 == SHA256 supported
47 public final byte log2BlockSize; // only 12 (block size 4096) supported now
48 public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
49 public final byte[] rawRootHash; // salted digest of the first Merkle tree page
50
51 HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
52 this.hashAlgorithm = hashAlgorithm;
53 this.log2BlockSize = log2BlockSize;
54 this.salt = salt;
55 this.rawRootHash = rawRootHash;
56 }
57
58 /**
59 * Constructs HashingInfo from byte array.
60 */
61 public static HashingInfo fromByteArray(byte[] bytes) throws IOException {
62 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
63 final int hashAlgorithm = buffer.getInt();
64 final byte log2BlockSize = buffer.get();
65 byte[] salt = readBytes(buffer);
66 byte[] rawRootHash = readBytes(buffer);
67 return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
68 }
69 }
70
71 /**
72 * V4 signature data.
73 */
74 public static class SigningInfo {
75 public final byte[] v3Digest; // used to match with the corresponding APK
76 public final byte[] certificate; // ASN.1 DER form
77 public final byte[] additionalData; // a free-form binary data blob
78 public final byte[] publicKey; // ASN.1 DER, must match the certificate
79 public final int signatureAlgorithmId; // see the APK v2 doc for the list
80 public final byte[] signature;
81
82 SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
83 byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
84 this.v3Digest = v3Digest;
85 this.certificate = certificate;
86 this.additionalData = additionalData;
87 this.publicKey = publicKey;
88 this.signatureAlgorithmId = signatureAlgorithmId;
89 this.signature = signature;
90 }
91
92 /**
93 * Constructs SigningInfo from byte array.
94 */
95 public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
96 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
97 byte[] v3Digest = readBytes(buffer);
98 byte[] certificate = readBytes(buffer);
99 byte[] additionalData = readBytes(buffer);
100 byte[] publicKey = readBytes(buffer);
101 int signatureAlgorithmId = buffer.getInt();
102 byte[] signature = readBytes(buffer);
103 return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
104 signatureAlgorithmId, signature);
105 }
106 }
107
108 public final int version; // Always 2 for now.
109 public final byte[] hashingInfo;
110 public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later.
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800111
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800112 /**
113 * Construct a V4Signature from .idsig file.
114 */
Songchun Fan38dfe9a2020-02-20 18:12:47 -0800115 public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700116 try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) {
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800117 return readFrom(stream);
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800118 }
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800119 }
120
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800121 /**
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700122 * Construct a V4Signature from a byte array.
Alex Buynytskyy17d9da02020-02-12 16:03:30 -0800123 */
124 public static V4Signature readFrom(byte[] bytes) throws IOException {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700125 try (InputStream stream = new ByteArrayInputStream(bytes)) {
Alex Buynytskyy17d9da02020-02-12 16:03:30 -0800126 return readFrom(stream);
127 }
128 }
129
130 /**
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800131 * Store the V4Signature to a byte-array.
132 */
133 public byte[] toByteArray() {
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700134 try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
135 this.writeTo(stream);
136 return stream.toByteArray();
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800137 } catch (IOException e) {
138 return null;
139 }
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800140 }
141
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700142 /**
143 * Combines necessary data to a signed data blob.
144 * The blob can be validated against signingInfo.signature.
145 *
146 * @param fileSize - size of the signed file (APK)
147 */
148 public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
149 SigningInfo signingInfo) {
150 final int size =
151 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
152 hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
153 signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
154 signingInfo.additionalData);
155 ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
156 buffer.putInt(size);
157 buffer.putLong(fileSize);
158 buffer.putInt(hashingInfo.hashAlgorithm);
159 buffer.put(hashingInfo.log2BlockSize);
160 writeBytes(buffer, hashingInfo.salt);
161 writeBytes(buffer, hashingInfo.rawRootHash);
162 writeBytes(buffer, signingInfo.v3Digest);
163 writeBytes(buffer, signingInfo.certificate);
164 writeBytes(buffer, signingInfo.additionalData);
165 return buffer.array();
166 }
167
168 public boolean isVersionSupported() {
Yurii Zubrytskyidf7c0502020-02-19 14:02:14 -0800169 return this.version == SUPPORTED_VERSION;
170 }
171
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700172 private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) {
Yurii Zubrytskyidf7c0502020-02-19 14:02:14 -0800173 this.version = version;
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700174 this.hashingInfo = hashingInfo;
175 this.signingInfo = signingInfo;
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800176 }
177
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700178 private static V4Signature readFrom(InputStream stream) throws IOException {
179 final int version = readIntLE(stream);
180 final byte[] hashingInfo = readBytes(stream);
181 final byte[] signingInfo = readBytes(stream);
182 return new V4Signature(version, hashingInfo, signingInfo);
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800183 }
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800184
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700185 private void writeTo(OutputStream stream) throws IOException {
186 writeIntLE(stream, this.version);
187 writeBytes(stream, this.hashingInfo);
188 writeBytes(stream, this.signingInfo);
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800189 }
190
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700191 // Utility methods.
192 private static int bytesSize(byte[] bytes) {
193 return 4/*length*/ + (bytes == null ? 0 : bytes.length);
194 }
195
196 private static void readFully(InputStream stream, byte[] buffer) throws IOException {
197 int len = buffer.length;
198 int n = 0;
199 while (n < len) {
200 int count = stream.read(buffer, n, len - n);
201 if (count < 0) {
202 throw new EOFException();
203 }
204 n += count;
205 }
206 }
207
208 private static int readIntLE(InputStream stream) throws IOException {
209 final byte[] buffer = new byte[4];
210 readFully(stream, buffer);
211 return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
212 }
213
214 private static void writeIntLE(OutputStream stream, int v) throws IOException {
215 final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(
216 v).array();
217 stream.write(buffer);
218 }
219
220 private static byte[] readBytes(InputStream stream) throws IOException {
221 try {
222 final int size = readIntLE(stream);
223 final byte[] bytes = new byte[size];
224 readFully(stream, bytes);
225 return bytes;
226 } catch (EOFException ignored) {
227 return null;
228 }
229 }
230
231 private static byte[] readBytes(ByteBuffer buffer) throws IOException {
232 if (buffer.remaining() < 4) {
233 throw new EOFException();
234 }
235 final int size = buffer.getInt();
236 if (buffer.remaining() < size) {
237 throw new EOFException();
238 }
239 final byte[] bytes = new byte[size];
240 buffer.get(bytes);
241 return bytes;
242 }
243
244 private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException {
245 if (bytes == null) {
246 writeIntLE(stream, 0);
247 return;
248 }
249 writeIntLE(stream, bytes.length);
Alex Buynytskyy8e9e6a32020-02-08 14:26:45 -0800250 stream.write(bytes);
251 }
Alex Buynytskyyf5e605a2020-03-13 13:31:12 -0700252
253 private static void writeBytes(ByteBuffer buffer, byte[] bytes) {
254 if (bytes == null) {
255 buffer.putInt(0);
256 return;
257 }
258 buffer.putInt(bytes.length);
259 buffer.put(bytes);
260 }
Alex Buynytskyycd4d3872020-02-08 17:50:50 -0800261}