• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.os.incremental;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.ParcelFileDescriptor;
22 
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.EOFException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.nio.ByteBuffer;
30 import java.nio.ByteOrder;
31 
32 /**
33  * V4 signature fields.
34  * Keep in sync with APKSig authoritative copy.
35  * @hide
36  */
37 public class V4Signature {
38     public static final String EXT = ".idsig";
39     public static final int SUPPORTED_VERSION = 2;
40 
41     public static final int HASHING_ALGORITHM_SHA256 = 1;
42     public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
43 
44     public static final int INCFS_MAX_SIGNATURE_SIZE = 8096;  // incrementalfs.h
45 
46     /**
47      * IncFS hashing data.
48      */
49     public static class HashingInfo {
50         public final int hashAlgorithm; // only 1 == SHA256 supported
51         public final byte log2BlockSize; // only 12 (block size 4096) supported now
52         @Nullable public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
53         @Nullable public final byte[] rawRootHash; // salted digest of the first Merkle tree page
54 
HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash)55         HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
56             this.hashAlgorithm = hashAlgorithm;
57             this.log2BlockSize = log2BlockSize;
58             this.salt = salt;
59             this.rawRootHash = rawRootHash;
60         }
61 
62         /**
63          * Constructs HashingInfo from byte array.
64          */
65         @NonNull
fromByteArray(@onNull byte[] bytes)66         public static HashingInfo fromByteArray(@NonNull byte[] bytes) throws IOException {
67             ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
68             final int hashAlgorithm = buffer.getInt();
69             final byte log2BlockSize = buffer.get();
70             byte[] salt = readBytes(buffer);
71             byte[] rawRootHash = readBytes(buffer);
72             return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
73         }
74     }
75 
76     /**
77      * V4 signature data.
78      */
79     public static class SigningInfo {
80         public final byte[] apkDigest;  // used to match with the corresponding APK
81         public final byte[] certificate; // ASN.1 DER form
82         public final byte[] additionalData; // a free-form binary data blob
83         public final byte[] publicKey; // ASN.1 DER, must match the certificate
84         public final int signatureAlgorithmId; // see the APK v2 doc for the list
85         public final byte[] signature;
86 
SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, byte[] publicKey, int signatureAlgorithmId, byte[] signature)87         SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
88                 byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
89             this.apkDigest = apkDigest;
90             this.certificate = certificate;
91             this.additionalData = additionalData;
92             this.publicKey = publicKey;
93             this.signatureAlgorithmId = signatureAlgorithmId;
94             this.signature = signature;
95         }
96 
97         /**
98          * Constructs SigningInfo from byte array.
99          */
fromByteArray(byte[] bytes)100         public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
101             ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
102             byte[] apkDigest = readBytes(buffer);
103             byte[] certificate = readBytes(buffer);
104             byte[] additionalData = readBytes(buffer);
105             byte[] publicKey = readBytes(buffer);
106             int signatureAlgorithmId = buffer.getInt();
107             byte[] signature = readBytes(buffer);
108             return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
109                     signatureAlgorithmId, signature);
110         }
111     }
112 
113     public final int version; // Always 2 for now.
114     /**
115      * Raw byte array containing the IncFS hashing data.
116      * @see HashingInfo#fromByteArray(byte[])
117      */
118     @Nullable public final byte[] hashingInfo;
119 
120     /**
121      * Raw byte array containing the V4 signature data.
122      * <p>Passed as-is to the kernel. Can be retrieved later.
123      * @see SigningInfo#fromByteArray(byte[])
124      */
125     @Nullable public final byte[] signingInfo;
126 
127     /**
128      * Construct a V4Signature from .idsig file.
129      */
readFrom(ParcelFileDescriptor pfd)130     public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException {
131         try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) {
132             return readFrom(stream);
133         }
134     }
135 
136     /**
137      * Construct a V4Signature from a byte array.
138      */
139     @NonNull
readFrom(@onNull byte[] bytes)140     public static V4Signature readFrom(@NonNull byte[] bytes) throws IOException {
141         try (InputStream stream = new ByteArrayInputStream(bytes)) {
142             return readFrom(stream);
143         }
144     }
145 
146     /**
147      * Store the V4Signature to a byte-array.
148      */
toByteArray()149     public byte[] toByteArray() {
150         try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
151             this.writeTo(stream);
152             return stream.toByteArray();
153         } catch (IOException e) {
154             return null;
155         }
156     }
157 
158     /**
159      * Combines necessary data to a signed data blob.
160      * The blob can be validated against signingInfo.signature.
161      *
162      * @param fileSize - size of the signed file (APK)
163      */
getSignedData(long fileSize, HashingInfo hashingInfo, SigningInfo signingInfo)164     public static byte[] getSignedData(long fileSize, HashingInfo hashingInfo,
165             SigningInfo signingInfo) {
166         final int size =
167                 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
168                         hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
169                         signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
170                         signingInfo.additionalData);
171         ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
172         buffer.putInt(size);
173         buffer.putLong(fileSize);
174         buffer.putInt(hashingInfo.hashAlgorithm);
175         buffer.put(hashingInfo.log2BlockSize);
176         writeBytes(buffer, hashingInfo.salt);
177         writeBytes(buffer, hashingInfo.rawRootHash);
178         writeBytes(buffer, signingInfo.apkDigest);
179         writeBytes(buffer, signingInfo.certificate);
180         writeBytes(buffer, signingInfo.additionalData);
181         return buffer.array();
182     }
183 
isVersionSupported()184     public boolean isVersionSupported() {
185         return this.version == SUPPORTED_VERSION;
186     }
187 
V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo)188     private V4Signature(int version, @Nullable byte[] hashingInfo, @Nullable byte[] signingInfo) {
189         this.version = version;
190         this.hashingInfo = hashingInfo;
191         this.signingInfo = signingInfo;
192     }
193 
readFrom(InputStream stream)194     private static V4Signature readFrom(InputStream stream) throws IOException {
195         final int version = readIntLE(stream);
196         int maxSize = INCFS_MAX_SIGNATURE_SIZE;
197         final byte[] hashingInfo = readBytes(stream, maxSize);
198         if (hashingInfo != null) {
199             maxSize -= hashingInfo.length;
200         }
201         final byte[] signingInfo = readBytes(stream, maxSize);
202         return new V4Signature(version, hashingInfo, signingInfo);
203     }
204 
writeTo(OutputStream stream)205     private void writeTo(OutputStream stream) throws IOException {
206         writeIntLE(stream, this.version);
207         writeBytes(stream, this.hashingInfo);
208         writeBytes(stream, this.signingInfo);
209     }
210 
211     // Utility methods.
bytesSize(byte[] bytes)212     private static int bytesSize(byte[] bytes) {
213         return 4/*length*/ + (bytes == null ? 0 : bytes.length);
214     }
215 
readFully(InputStream stream, byte[] buffer)216     private static void readFully(InputStream stream, byte[] buffer) throws IOException {
217         int len = buffer.length;
218         int n = 0;
219         while (n < len) {
220             int count = stream.read(buffer, n, len - n);
221             if (count < 0) {
222                 throw new EOFException();
223             }
224             n += count;
225         }
226     }
227 
readIntLE(InputStream stream)228     private static int readIntLE(InputStream stream) throws IOException {
229         final byte[] buffer = new byte[4];
230         readFully(stream, buffer);
231         return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
232     }
233 
writeIntLE(OutputStream stream, int v)234     private static void writeIntLE(OutputStream stream, int v) throws IOException {
235         final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(
236                 v).array();
237         stream.write(buffer);
238     }
239 
readBytes(InputStream stream, int maxSize)240     private static byte[] readBytes(InputStream stream, int maxSize) throws IOException {
241         try {
242             final int size = readIntLE(stream);
243             if (size > maxSize) {
244                 throw new IOException(
245                         "Signature is too long. Max allowed is " + INCFS_MAX_SIGNATURE_SIZE);
246             }
247             final byte[] bytes = new byte[size];
248             readFully(stream, bytes);
249             return bytes;
250         } catch (EOFException ignored) {
251             return null;
252         }
253     }
254 
readBytes(ByteBuffer buffer)255     private static byte[] readBytes(ByteBuffer buffer) throws IOException {
256         if (buffer.remaining() < 4) {
257             throw new EOFException();
258         }
259         final int size = buffer.getInt();
260         if (buffer.remaining() < size) {
261             throw new EOFException();
262         }
263         final byte[] bytes = new byte[size];
264         buffer.get(bytes);
265         return bytes;
266     }
267 
writeBytes(OutputStream stream, byte[] bytes)268     private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException {
269         if (bytes == null) {
270             writeIntLE(stream, 0);
271             return;
272         }
273         writeIntLE(stream, bytes.length);
274         stream.write(bytes);
275     }
276 
writeBytes(ByteBuffer buffer, byte[] bytes)277     private static void writeBytes(ByteBuffer buffer, byte[] bytes) {
278         if (bytes == null) {
279             buffer.putInt(0);
280             return;
281         }
282         buffer.putInt(bytes.length);
283         buffer.put(bytes);
284     }
285 }
286