1 /*
2  * Copyright (C) 2018 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 com.android.apksig.internal.apk;
18 
19 import static com.android.apksig.Constants.OID_RSA_ENCRYPTION;
20 import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA256;
21 import static com.android.apksig.internal.apk.ContentDigestAlgorithm.CHUNKED_SHA512;
22 import static com.android.apksig.internal.apk.ContentDigestAlgorithm.VERITY_CHUNKED_SHA256;
23 
24 import com.android.apksig.ApkVerifier;
25 import com.android.apksig.KeyConfig;
26 import com.android.apksig.SignerEngineFactory;
27 import com.android.apksig.SigningCertificateLineage;
28 import com.android.apksig.apk.ApkFormatException;
29 import com.android.apksig.apk.ApkUtils;
30 import com.android.apksig.internal.asn1.Asn1BerParser;
31 import com.android.apksig.internal.asn1.Asn1DecodingException;
32 import com.android.apksig.internal.asn1.Asn1DerEncoder;
33 import com.android.apksig.internal.asn1.Asn1EncodingException;
34 import com.android.apksig.internal.asn1.Asn1OpaqueObject;
35 import com.android.apksig.internal.pkcs7.AlgorithmIdentifier;
36 import com.android.apksig.internal.pkcs7.ContentInfo;
37 import com.android.apksig.internal.pkcs7.EncapsulatedContentInfo;
38 import com.android.apksig.internal.pkcs7.IssuerAndSerialNumber;
39 import com.android.apksig.internal.pkcs7.Pkcs7Constants;
40 import com.android.apksig.internal.pkcs7.SignedData;
41 import com.android.apksig.internal.pkcs7.SignerIdentifier;
42 import com.android.apksig.internal.pkcs7.SignerInfo;
43 import com.android.apksig.internal.util.ByteBufferDataSource;
44 import com.android.apksig.internal.util.ChainedDataSource;
45 import com.android.apksig.internal.util.GuaranteedEncodedFormX509Certificate;
46 import com.android.apksig.internal.util.Pair;
47 import com.android.apksig.internal.util.VerityTreeBuilder;
48 import com.android.apksig.internal.util.X509CertificateUtils;
49 import com.android.apksig.internal.x509.RSAPublicKey;
50 import com.android.apksig.internal.x509.SubjectPublicKeyInfo;
51 import com.android.apksig.internal.zip.ZipUtils;
52 import com.android.apksig.util.DataSink;
53 import com.android.apksig.util.DataSinks;
54 import com.android.apksig.util.DataSource;
55 import com.android.apksig.util.DataSources;
56 import com.android.apksig.util.RunnablesExecutor;
57 
58 import java.io.IOException;
59 import java.math.BigInteger;
60 import java.nio.ByteBuffer;
61 import java.nio.ByteOrder;
62 import java.security.DigestException;
63 import java.security.InvalidAlgorithmParameterException;
64 import java.security.InvalidKeyException;
65 import java.security.KeyFactory;
66 import java.security.MessageDigest;
67 import java.security.NoSuchAlgorithmException;
68 import java.security.PrivateKey;
69 import java.security.PublicKey;
70 import java.security.Signature;
71 import java.security.SignatureException;
72 import java.security.cert.CertificateEncodingException;
73 import java.security.cert.CertificateException;
74 import java.security.cert.X509Certificate;
75 import java.security.spec.AlgorithmParameterSpec;
76 import java.security.spec.InvalidKeySpecException;
77 import java.security.spec.X509EncodedKeySpec;
78 import java.util.ArrayList;
79 import java.util.Arrays;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.HashSet;
83 import java.util.List;
84 import java.util.Map;
85 import java.util.Set;
86 import java.util.concurrent.atomic.AtomicInteger;
87 import java.util.function.Supplier;
88 
89 import javax.security.auth.x500.X500Principal;
90 
91 public class ApkSigningBlockUtils {
92 
93     private static final long CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES = 1024 * 1024;
94     public static final int ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
95     private static final byte[] APK_SIGNING_BLOCK_MAGIC =
96           new byte[] {
97               0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20,
98               0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32,
99           };
100     public static final int VERITY_PADDING_BLOCK_ID = 0x42726577;
101 
102     private static final ContentDigestAlgorithm[] V4_CONTENT_DIGEST_ALGORITHMS =
103             {CHUNKED_SHA512, VERITY_CHUNKED_SHA256, CHUNKED_SHA256};
104 
105     public static final int VERSION_SOURCE_STAMP = 0;
106     public static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
107     public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
108     public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
109     public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31;
110     public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4;
111 
112     /**
113      * Returns positive number if {@code alg1} is preferred over {@code alg2}, {@code -1} if
114      * {@code alg2} is preferred over {@code alg1}, and {@code 0} if there is no preference.
115      */
compareSignatureAlgorithm(SignatureAlgorithm alg1, SignatureAlgorithm alg2)116     public static int compareSignatureAlgorithm(SignatureAlgorithm alg1, SignatureAlgorithm alg2) {
117         return ApkSigningBlockUtilsLite.compareSignatureAlgorithm(alg1, alg2);
118     }
119 
120     /**
121      * Verifies integrity of the APK outside of the APK Signing Block by computing digests of the
122      * APK and comparing them against the digests listed in APK Signing Block. The expected digests
123      * are taken from {@code SignerInfos} of the provided {@code result}.
124      *
125      * <p>This method adds one or more errors to the {@code result} if a verification error is
126      * expected to be encountered on Android. No errors are added to the {@code result} if the APK's
127      * integrity is expected to verify on Android for each algorithm in
128      * {@code contentDigestAlgorithms}.
129      *
130      * <p>The reason this method is currently not parameterized by a
131      * {@code [minSdkVersion, maxSdkVersion]} range is that up until now content digest algorithms
132      * exhibit the same behavior on all Android platform versions.
133      */
verifyIntegrity( RunnablesExecutor executor, DataSource beforeApkSigningBlock, DataSource centralDir, ByteBuffer eocd, Set<ContentDigestAlgorithm> contentDigestAlgorithms, Result result)134     public static void verifyIntegrity(
135             RunnablesExecutor executor,
136             DataSource beforeApkSigningBlock,
137             DataSource centralDir,
138             ByteBuffer eocd,
139             Set<ContentDigestAlgorithm> contentDigestAlgorithms,
140             Result result) throws IOException, NoSuchAlgorithmException {
141         if (contentDigestAlgorithms.isEmpty()) {
142             // This should never occur because this method is invoked once at least one signature
143             // is verified, meaning at least one content digest is known.
144             throw new RuntimeException("No content digests found");
145         }
146 
147         // For the purposes of verifying integrity, ZIP End of Central Directory (EoCD) must be
148         // treated as though its Central Directory offset points to the start of APK Signing Block.
149         // We thus modify the EoCD accordingly.
150         ByteBuffer modifiedEocd = ByteBuffer.allocate(eocd.remaining());
151         int eocdSavedPos = eocd.position();
152         modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
153         modifiedEocd.put(eocd);
154         modifiedEocd.flip();
155 
156         // restore eocd to position prior to modification in case it is to be used elsewhere
157         eocd.position(eocdSavedPos);
158         ZipUtils.setZipEocdCentralDirectoryOffset(modifiedEocd, beforeApkSigningBlock.size());
159         Map<ContentDigestAlgorithm, byte[]> actualContentDigests;
160         try {
161             actualContentDigests =
162                     computeContentDigests(
163                             executor,
164                             contentDigestAlgorithms,
165                             beforeApkSigningBlock,
166                             centralDir,
167                             new ByteBufferDataSource(modifiedEocd));
168             // Special checks for the verity algorithm requirements.
169             if (actualContentDigests.containsKey(VERITY_CHUNKED_SHA256)) {
170                 if ((beforeApkSigningBlock.size() % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0)) {
171                     throw new RuntimeException(
172                             "APK Signing Block is not aligned on 4k boundary: " +
173                             beforeApkSigningBlock.size());
174                 }
175 
176                 long centralDirOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
177                 long signingBlockSize = centralDirOffset - beforeApkSigningBlock.size();
178                 if (signingBlockSize % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0) {
179                     throw new RuntimeException(
180                             "APK Signing Block size is not multiple of page size: " +
181                             signingBlockSize);
182                 }
183             }
184         } catch (DigestException e) {
185             throw new RuntimeException("Failed to compute content digests", e);
186         }
187         if (!contentDigestAlgorithms.equals(actualContentDigests.keySet())) {
188             throw new RuntimeException(
189                     "Mismatch between sets of requested and computed content digests"
190                             + " . Requested: " + contentDigestAlgorithms
191                             + ", computed: " + actualContentDigests.keySet());
192         }
193 
194         // Compare digests computed over the rest of APK against the corresponding expected digests
195         // in signer blocks.
196         for (Result.SignerInfo signerInfo : result.signers) {
197             for (Result.SignerInfo.ContentDigest expected : signerInfo.contentDigests) {
198                 SignatureAlgorithm signatureAlgorithm =
199                         SignatureAlgorithm.findById(expected.getSignatureAlgorithmId());
200                 if (signatureAlgorithm == null) {
201                     continue;
202                 }
203                 ContentDigestAlgorithm contentDigestAlgorithm =
204                         signatureAlgorithm.getContentDigestAlgorithm();
205                 // if the current digest algorithm is not in the list provided by the caller then
206                 // ignore it; the signer may contain digests not recognized by the specified SDK
207                 // range.
208                 if (!contentDigestAlgorithms.contains(contentDigestAlgorithm)) {
209                     continue;
210                 }
211                 byte[] expectedDigest = expected.getValue();
212                 byte[] actualDigest = actualContentDigests.get(contentDigestAlgorithm);
213                 if (!Arrays.equals(expectedDigest, actualDigest)) {
214                     if (result.signatureSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V2) {
215                         signerInfo.addError(
216                                 ApkVerifier.Issue.V2_SIG_APK_DIGEST_DID_NOT_VERIFY,
217                                 contentDigestAlgorithm,
218                                 toHex(expectedDigest),
219                                 toHex(actualDigest));
220                     } else if (result.signatureSchemeVersion == VERSION_APK_SIGNATURE_SCHEME_V3) {
221                         signerInfo.addError(
222                                 ApkVerifier.Issue.V3_SIG_APK_DIGEST_DID_NOT_VERIFY,
223                                 contentDigestAlgorithm,
224                                 toHex(expectedDigest),
225                                 toHex(actualDigest));
226                     }
227                     continue;
228                 }
229                 signerInfo.verifiedContentDigests.put(contentDigestAlgorithm, actualDigest);
230             }
231         }
232     }
233 
findApkSignatureSchemeBlock( ByteBuffer apkSigningBlock, int blockId, Result result)234     public static ByteBuffer findApkSignatureSchemeBlock(
235             ByteBuffer apkSigningBlock,
236             int blockId,
237             Result result) throws SignatureNotFoundException {
238         try {
239             return ApkSigningBlockUtilsLite.findApkSignatureSchemeBlock(apkSigningBlock, blockId);
240         } catch (com.android.apksig.internal.apk.SignatureNotFoundException e) {
241             throw new SignatureNotFoundException(e.getMessage());
242         }
243     }
244 
checkByteOrderLittleEndian(ByteBuffer buffer)245     public static void checkByteOrderLittleEndian(ByteBuffer buffer) {
246         ApkSigningBlockUtilsLite.checkByteOrderLittleEndian(buffer);
247     }
248 
getLengthPrefixedSlice(ByteBuffer source)249     public static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws ApkFormatException {
250         return ApkSigningBlockUtilsLite.getLengthPrefixedSlice(source);
251     }
252 
readLengthPrefixedByteArray(ByteBuffer buf)253     public static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws ApkFormatException {
254         return ApkSigningBlockUtilsLite.readLengthPrefixedByteArray(buf);
255     }
256 
toHex(byte[] value)257     public static String toHex(byte[] value) {
258         return ApkSigningBlockUtilsLite.toHex(value);
259     }
260 
computeContentDigests( RunnablesExecutor executor, Set<ContentDigestAlgorithm> digestAlgorithms, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd)261     public static Map<ContentDigestAlgorithm, byte[]> computeContentDigests(
262             RunnablesExecutor executor,
263             Set<ContentDigestAlgorithm> digestAlgorithms,
264             DataSource beforeCentralDir,
265             DataSource centralDir,
266             DataSource eocd) throws IOException, NoSuchAlgorithmException, DigestException {
267         Map<ContentDigestAlgorithm, byte[]> contentDigests = new HashMap<>();
268         Set<ContentDigestAlgorithm> oneMbChunkBasedAlgorithm = new HashSet<>();
269         for (ContentDigestAlgorithm digestAlgorithm : digestAlgorithms) {
270             if (digestAlgorithm == ContentDigestAlgorithm.CHUNKED_SHA256
271                     || digestAlgorithm == ContentDigestAlgorithm.CHUNKED_SHA512) {
272                 oneMbChunkBasedAlgorithm.add(digestAlgorithm);
273             }
274         }
275         computeOneMbChunkContentDigests(
276                 executor,
277                 oneMbChunkBasedAlgorithm,
278                 new DataSource[] { beforeCentralDir, centralDir, eocd },
279                 contentDigests);
280 
281         if (digestAlgorithms.contains(VERITY_CHUNKED_SHA256)) {
282             computeApkVerityDigest(beforeCentralDir, centralDir, eocd, contentDigests);
283         }
284         return contentDigests;
285     }
286 
computeOneMbChunkContentDigests( Set<ContentDigestAlgorithm> digestAlgorithms, DataSource[] contents, Map<ContentDigestAlgorithm, byte[]> outputContentDigests)287     static void computeOneMbChunkContentDigests(
288             Set<ContentDigestAlgorithm> digestAlgorithms,
289             DataSource[] contents,
290             Map<ContentDigestAlgorithm, byte[]> outputContentDigests)
291             throws IOException, NoSuchAlgorithmException, DigestException {
292         // For each digest algorithm the result is computed as follows:
293         // 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
294         //    The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
295         //    No chunks are produced for empty (zero length) segments.
296         // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's
297         //    length in bytes (uint32 little-endian) and the chunk's contents.
298         // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of
299         //    chunks (uint32 little-endian) and the concatenation of digests of chunks of all
300         //    segments in-order.
301 
302         long chunkCountLong = 0;
303         for (DataSource input : contents) {
304             chunkCountLong +=
305                     getChunkCount(input.size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
306         }
307         if (chunkCountLong > Integer.MAX_VALUE) {
308             throw new DigestException("Input too long: " + chunkCountLong + " chunks");
309         }
310         int chunkCount = (int) chunkCountLong;
311 
312         ContentDigestAlgorithm[] digestAlgorithmsArray =
313                 digestAlgorithms.toArray(new ContentDigestAlgorithm[digestAlgorithms.size()]);
314         MessageDigest[] mds = new MessageDigest[digestAlgorithmsArray.length];
315         byte[][] digestsOfChunks = new byte[digestAlgorithmsArray.length][];
316         int[] digestOutputSizes = new int[digestAlgorithmsArray.length];
317         for (int i = 0; i < digestAlgorithmsArray.length; i++) {
318             ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i];
319             int digestOutputSizeBytes = digestAlgorithm.getChunkDigestOutputSizeBytes();
320             digestOutputSizes[i] = digestOutputSizeBytes;
321             byte[] concatenationOfChunkCountAndChunkDigests =
322                     new byte[5 + chunkCount * digestOutputSizeBytes];
323             concatenationOfChunkCountAndChunkDigests[0] = 0x5a;
324             setUnsignedInt32LittleEndian(
325                     chunkCount, concatenationOfChunkCountAndChunkDigests, 1);
326             digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
327             String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
328             mds[i] = MessageDigest.getInstance(jcaAlgorithm);
329         }
330 
331         DataSink mdSink = DataSinks.asDataSink(mds);
332         byte[] chunkContentPrefix = new byte[5];
333         chunkContentPrefix[0] = (byte) 0xa5;
334         int chunkIndex = 0;
335         // Optimization opportunity: digests of chunks can be computed in parallel. However,
336         // determining the number of computations to be performed in parallel is non-trivial. This
337         // depends on a wide range of factors, such as data source type (e.g., in-memory or fetched
338         // from file), CPU/memory/disk cache bandwidth and latency, interconnect architecture of CPU
339         // cores, load on the system from other threads of execution and other processes, size of
340         // input.
341         // For now, we compute these digests sequentially and thus have the luxury of improving
342         // performance by writing the digest of each chunk into a pre-allocated buffer at exactly
343         // the right position. This avoids unnecessary allocations, copying, and enables the final
344         // digest to be more efficient because it's presented with all of its input in one go.
345         for (DataSource input : contents) {
346             long inputOffset = 0;
347             long inputRemaining = input.size();
348             while (inputRemaining > 0) {
349                 int chunkSize =
350                         (int) Math.min(inputRemaining, CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
351                 setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1);
352                 for (int i = 0; i < mds.length; i++) {
353                     mds[i].update(chunkContentPrefix);
354                 }
355                 try {
356                     input.feed(inputOffset, chunkSize, mdSink);
357                 } catch (IOException e) {
358                     throw new IOException("Failed to read chunk #" + chunkIndex, e);
359                 }
360                 for (int i = 0; i < digestAlgorithmsArray.length; i++) {
361                     MessageDigest md = mds[i];
362                     byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
363                     int expectedDigestSizeBytes = digestOutputSizes[i];
364                     int actualDigestSizeBytes =
365                             md.digest(
366                                     concatenationOfChunkCountAndChunkDigests,
367                                     5 + chunkIndex * expectedDigestSizeBytes,
368                                     expectedDigestSizeBytes);
369                     if (actualDigestSizeBytes != expectedDigestSizeBytes) {
370                         throw new RuntimeException(
371                                 "Unexpected output size of " + md.getAlgorithm()
372                                         + " digest: " + actualDigestSizeBytes);
373                     }
374                 }
375                 inputOffset += chunkSize;
376                 inputRemaining -= chunkSize;
377                 chunkIndex++;
378             }
379         }
380 
381         for (int i = 0; i < digestAlgorithmsArray.length; i++) {
382             ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i];
383             byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
384             MessageDigest md = mds[i];
385             byte[] digest = md.digest(concatenationOfChunkCountAndChunkDigests);
386             outputContentDigests.put(digestAlgorithm, digest);
387         }
388     }
389 
computeOneMbChunkContentDigests( RunnablesExecutor executor, Set<ContentDigestAlgorithm> digestAlgorithms, DataSource[] contents, Map<ContentDigestAlgorithm, byte[]> outputContentDigests)390     static void computeOneMbChunkContentDigests(
391             RunnablesExecutor executor,
392             Set<ContentDigestAlgorithm> digestAlgorithms,
393             DataSource[] contents,
394             Map<ContentDigestAlgorithm, byte[]> outputContentDigests)
395             throws NoSuchAlgorithmException, DigestException {
396         long chunkCountLong = 0;
397         for (DataSource input : contents) {
398             chunkCountLong +=
399                     getChunkCount(input.size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
400         }
401         if (chunkCountLong > Integer.MAX_VALUE) {
402             throw new DigestException("Input too long: " + chunkCountLong + " chunks");
403         }
404         int chunkCount = (int) chunkCountLong;
405 
406         List<ChunkDigests> chunkDigestsList = new ArrayList<>(digestAlgorithms.size());
407         for (ContentDigestAlgorithm algorithms : digestAlgorithms) {
408             chunkDigestsList.add(new ChunkDigests(algorithms, chunkCount));
409         }
410 
411         ChunkSupplier chunkSupplier = new ChunkSupplier(contents);
412         executor.execute(() -> new ChunkDigester(chunkSupplier, chunkDigestsList));
413 
414         // Compute and write out final digest for each algorithm.
415         for (ChunkDigests chunkDigests : chunkDigestsList) {
416             MessageDigest messageDigest = chunkDigests.createMessageDigest();
417             outputContentDigests.put(
418                     chunkDigests.algorithm,
419                     messageDigest.digest(chunkDigests.concatOfDigestsOfChunks));
420         }
421     }
422 
423     private static class ChunkDigests {
424         private final ContentDigestAlgorithm algorithm;
425         private final int digestOutputSize;
426         private final byte[] concatOfDigestsOfChunks;
427 
ChunkDigests(ContentDigestAlgorithm algorithm, int chunkCount)428         private ChunkDigests(ContentDigestAlgorithm algorithm, int chunkCount) {
429             this.algorithm = algorithm;
430             digestOutputSize = this.algorithm.getChunkDigestOutputSizeBytes();
431             concatOfDigestsOfChunks = new byte[1 + 4 + chunkCount * digestOutputSize];
432 
433             // Fill the initial values of the concatenated digests of chunks, which is
434             // {0x5a, 4-bytes-of-little-endian-chunk-count, digests*...}.
435             concatOfDigestsOfChunks[0] = 0x5a;
436             setUnsignedInt32LittleEndian(chunkCount, concatOfDigestsOfChunks, 1);
437         }
438 
createMessageDigest()439         private MessageDigest createMessageDigest() throws NoSuchAlgorithmException {
440             return MessageDigest.getInstance(algorithm.getJcaMessageDigestAlgorithm());
441         }
442 
getOffset(int chunkIndex)443         private int getOffset(int chunkIndex) {
444             return 1 + 4 + chunkIndex * digestOutputSize;
445         }
446     }
447 
448     /**
449      * A per-thread digest worker.
450      */
451     private static class ChunkDigester implements Runnable {
452         private final ChunkSupplier dataSupplier;
453         private final List<ChunkDigests> chunkDigests;
454         private final List<MessageDigest> messageDigests;
455         private final DataSink mdSink;
456 
ChunkDigester(ChunkSupplier dataSupplier, List<ChunkDigests> chunkDigests)457         private ChunkDigester(ChunkSupplier dataSupplier, List<ChunkDigests> chunkDigests) {
458             this.dataSupplier = dataSupplier;
459             this.chunkDigests = chunkDigests;
460             messageDigests = new ArrayList<>(chunkDigests.size());
461             for (ChunkDigests chunkDigest : chunkDigests) {
462                 try {
463                     messageDigests.add(chunkDigest.createMessageDigest());
464                 } catch (NoSuchAlgorithmException ex) {
465                     throw new RuntimeException(ex);
466                 }
467             }
468             mdSink = DataSinks.asDataSink(messageDigests.toArray(new MessageDigest[0]));
469         }
470 
471         @Override
run()472         public void run() {
473             byte[] chunkContentPrefix = new byte[5];
474             chunkContentPrefix[0] = (byte) 0xa5;
475 
476             try {
477                 for (ChunkSupplier.Chunk chunk = dataSupplier.get();
478                      chunk != null;
479                      chunk = dataSupplier.get()) {
480                     int size = chunk.size;
481                     if (size > CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES) {
482                         throw new RuntimeException("Chunk size greater than expected: " + size);
483                     }
484 
485                     // First update with the chunk prefix.
486                     setUnsignedInt32LittleEndian(size, chunkContentPrefix, 1);
487                     mdSink.consume(chunkContentPrefix, 0, chunkContentPrefix.length);
488 
489                     // Then update with the chunk data.
490                     mdSink.consume(chunk.data);
491 
492                     // Now finalize chunk for all algorithms.
493                     for (int i = 0; i < chunkDigests.size(); i++) {
494                         ChunkDigests chunkDigest = chunkDigests.get(i);
495                         int actualDigestSize = messageDigests.get(i).digest(
496                                 chunkDigest.concatOfDigestsOfChunks,
497                                 chunkDigest.getOffset(chunk.chunkIndex),
498                                 chunkDigest.digestOutputSize);
499                         if (actualDigestSize != chunkDigest.digestOutputSize) {
500                             throw new RuntimeException(
501                                     "Unexpected output size of " + chunkDigest.algorithm
502                                             + " digest: " + actualDigestSize);
503                         }
504                     }
505                 }
506             } catch (IOException | DigestException e) {
507                 throw new RuntimeException(e);
508             }
509         }
510     }
511 
512     /**
513      * Thread-safe 1MB DataSource chunk supplier. When bounds are met in a
514      * supplied {@link DataSource}, the data from the next {@link DataSource}
515      * are NOT concatenated. Only the next call to get() will fetch from the
516      * next {@link DataSource} in the input {@link DataSource} array.
517      */
518     private static class ChunkSupplier implements Supplier<ChunkSupplier.Chunk> {
519         private final DataSource[] dataSources;
520         private final int[] chunkCounts;
521         private final int totalChunkCount;
522         private final AtomicInteger nextIndex;
523 
ChunkSupplier(DataSource[] dataSources)524         private ChunkSupplier(DataSource[] dataSources) {
525             this.dataSources = dataSources;
526             chunkCounts = new int[dataSources.length];
527             int totalChunkCount = 0;
528             for (int i = 0; i < dataSources.length; i++) {
529                 long chunkCount = getChunkCount(dataSources[i].size(),
530                         CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
531                 if (chunkCount > Integer.MAX_VALUE) {
532                     throw new RuntimeException(
533                             String.format(
534                                     "Number of chunks in dataSource[%d] is greater than max int.",
535                                     i));
536                 }
537                 chunkCounts[i] = (int)chunkCount;
538                 totalChunkCount = (int) (totalChunkCount + chunkCount);
539             }
540             this.totalChunkCount = totalChunkCount;
541             nextIndex = new AtomicInteger(0);
542         }
543 
544         /**
545          * We map an integer index to the termination-adjusted dataSources 1MB chunks.
546          * Note that {@link Chunk}s could be less than 1MB, namely the last 1MB-aligned
547          * blocks in each input {@link DataSource} (unless the DataSource itself is
548          * 1MB-aligned).
549          */
550         @Override
get()551         public ChunkSupplier.Chunk get() {
552             int index = nextIndex.getAndIncrement();
553             if (index < 0 || index >= totalChunkCount) {
554                 return null;
555             }
556 
557             int dataSourceIndex = 0;
558             long dataSourceChunkOffset = index;
559             for (; dataSourceIndex < dataSources.length; dataSourceIndex++) {
560                 if (dataSourceChunkOffset < chunkCounts[dataSourceIndex]) {
561                     break;
562                 }
563                 dataSourceChunkOffset -= chunkCounts[dataSourceIndex];
564             }
565 
566             long remainingSize = Math.min(
567                     dataSources[dataSourceIndex].size() -
568                             dataSourceChunkOffset * CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES,
569                     CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
570 
571             final int size = (int)remainingSize;
572             final ByteBuffer buffer = ByteBuffer.allocate(size);
573             try {
574                 dataSources[dataSourceIndex].copyTo(
575                         dataSourceChunkOffset * CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES, size,
576                         buffer);
577             } catch (IOException e) {
578                 throw new IllegalStateException("Failed to read chunk", e);
579             }
580             buffer.rewind();
581 
582             return new Chunk(index, buffer, size);
583         }
584 
585         static class Chunk {
586             private final int chunkIndex;
587             private final ByteBuffer data;
588             private final int size;
589 
Chunk(int chunkIndex, ByteBuffer data, int size)590             private Chunk(int chunkIndex, ByteBuffer data, int size) {
591                 this.chunkIndex = chunkIndex;
592                 this.data = data;
593                 this.size = size;
594             }
595         }
596     }
597 
598     @SuppressWarnings("ByteBufferBackingArray")
computeApkVerityDigest(DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, Map<ContentDigestAlgorithm, byte[]> outputContentDigests)599     private static void computeApkVerityDigest(DataSource beforeCentralDir, DataSource centralDir,
600             DataSource eocd, Map<ContentDigestAlgorithm, byte[]> outputContentDigests)
601             throws IOException, NoSuchAlgorithmException {
602         ByteBuffer encoded = createVerityDigestBuffer(true);
603         // Use 0s as salt for now.  This also needs to be consistent in the fsverify header for
604         // kernel to use.
605         try (VerityTreeBuilder builder = new VerityTreeBuilder(new byte[8])) {
606             byte[] rootHash = builder.generateVerityTreeRootHash(beforeCentralDir, centralDir,
607                     eocd);
608             encoded.put(rootHash);
609             encoded.putLong(beforeCentralDir.size() + centralDir.size() + eocd.size());
610             outputContentDigests.put(VERITY_CHUNKED_SHA256, encoded.array());
611         }
612     }
613 
createVerityDigestBuffer(boolean includeSourceDataSize)614     private static ByteBuffer createVerityDigestBuffer(boolean includeSourceDataSize) {
615         // FORMAT:
616         // OFFSET       DATA TYPE  DESCRIPTION
617         // * @+0  bytes uint8[32]  Merkle tree root hash of SHA-256
618         // * @+32 bytes int64      (optional) Length of source data
619         int backBufferSize =
620                 VERITY_CHUNKED_SHA256.getChunkDigestOutputSizeBytes();
621         if (includeSourceDataSize) {
622             backBufferSize += Long.SIZE / Byte.SIZE;
623         }
624         ByteBuffer encoded = ByteBuffer.allocate(backBufferSize);
625         encoded.order(ByteOrder.LITTLE_ENDIAN);
626         return encoded;
627     }
628 
629     public static class VerityTreeAndDigest {
630         public final ContentDigestAlgorithm contentDigestAlgorithm;
631         public final byte[] rootHash;
632         public final byte[] tree;
633 
VerityTreeAndDigest(ContentDigestAlgorithm contentDigestAlgorithm, byte[] rootHash, byte[] tree)634         VerityTreeAndDigest(ContentDigestAlgorithm contentDigestAlgorithm, byte[] rootHash,
635                 byte[] tree) {
636             this.contentDigestAlgorithm = contentDigestAlgorithm;
637             this.rootHash = rootHash;
638             this.tree = tree;
639         }
640     }
641 
642     @SuppressWarnings("ByteBufferBackingArray")
computeChunkVerityTreeAndDigest(DataSource dataSource)643     public static VerityTreeAndDigest computeChunkVerityTreeAndDigest(DataSource dataSource)
644             throws IOException, NoSuchAlgorithmException {
645         ByteBuffer encoded = createVerityDigestBuffer(false);
646         // Use 0s as salt for now.  This also needs to be consistent in the fsverify header for
647         // kernel to use.
648         try (VerityTreeBuilder builder = new VerityTreeBuilder(null)) {
649             ByteBuffer tree = builder.generateVerityTree(dataSource);
650             byte[] rootHash = builder.getRootHashFromTree(tree);
651             encoded.put(rootHash);
652             return new VerityTreeAndDigest(VERITY_CHUNKED_SHA256, encoded.array(), tree.array());
653         }
654     }
655 
getChunkCount(long inputSize, long chunkSize)656     private static long getChunkCount(long inputSize, long chunkSize) {
657         return (inputSize + chunkSize - 1) / chunkSize;
658     }
659 
setUnsignedInt32LittleEndian(int value, byte[] result, int offset)660     private static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) {
661         result[offset] = (byte) (value & 0xff);
662         result[offset + 1] = (byte) ((value >> 8) & 0xff);
663         result[offset + 2] = (byte) ((value >> 16) & 0xff);
664         result[offset + 3] = (byte) ((value >> 24) & 0xff);
665     }
666 
encodePublicKey(PublicKey publicKey)667     public static byte[] encodePublicKey(PublicKey publicKey)
668             throws InvalidKeyException, NoSuchAlgorithmException {
669         byte[] encodedPublicKey = null;
670         if ("X.509".equals(publicKey.getFormat())) {
671             encodedPublicKey = publicKey.getEncoded();
672             // if the key is an RSA key check for a negative modulus
673             String keyAlgorithm = publicKey.getAlgorithm();
674             if ("RSA".equals(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) {
675                 try {
676                     // Parse the encoded public key into the separate elements of the
677                     // SubjectPublicKeyInfo to obtain the SubjectPublicKey.
678                     ByteBuffer encodedPublicKeyBuffer = ByteBuffer.wrap(encodedPublicKey);
679                     SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse(
680                             encodedPublicKeyBuffer, SubjectPublicKeyInfo.class);
681                     // The SubjectPublicKey is encoded as a bit string within the
682                     // SubjectPublicKeyInfo. The first byte of the encoding is the number of padding
683                     // bits; store this and decode the rest of the bit string into the RSA modulus
684                     // and exponent.
685                     ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey;
686                     byte padding = subjectPublicKeyBuffer.get();
687                     RSAPublicKey rsaPublicKey = Asn1BerParser.parse(subjectPublicKeyBuffer,
688                             RSAPublicKey.class);
689                     // if the modulus is negative then attempt to reencode it with a leading 0 sign
690                     // byte.
691                     if (rsaPublicKey.modulus.compareTo(BigInteger.ZERO) < 0) {
692                         // A negative modulus indicates the leading bit in the integer is 1. Per
693                         // ASN.1 encoding rules to encode a positive integer with the leading bit
694                         // set to 1 a byte containing all zeros should precede the integer encoding.
695                         byte[] encodedModulus = rsaPublicKey.modulus.toByteArray();
696                         byte[] reencodedModulus = new byte[encodedModulus.length + 1];
697                         reencodedModulus[0] = 0;
698                         System.arraycopy(encodedModulus, 0, reencodedModulus, 1,
699                                 encodedModulus.length);
700                         rsaPublicKey.modulus = new BigInteger(reencodedModulus);
701                         // Once the modulus has been corrected reencode the RSAPublicKey, then
702                         // restore the padding value in the bit string and reencode the entire
703                         // SubjectPublicKeyInfo to be returned to the caller.
704                         byte[] reencodedRSAPublicKey = Asn1DerEncoder.encode(rsaPublicKey);
705                         byte[] reencodedSubjectPublicKey =
706                                 new byte[reencodedRSAPublicKey.length + 1];
707                         reencodedSubjectPublicKey[0] = padding;
708                         System.arraycopy(reencodedRSAPublicKey, 0, reencodedSubjectPublicKey, 1,
709                                 reencodedRSAPublicKey.length);
710                         subjectPublicKeyInfo.subjectPublicKey = ByteBuffer.wrap(
711                                 reencodedSubjectPublicKey);
712                         encodedPublicKey = Asn1DerEncoder.encode(subjectPublicKeyInfo);
713                     }
714                 } catch (Asn1DecodingException | Asn1EncodingException e) {
715                     System.out.println("Caught a exception encoding the public key: " + e);
716                     e.printStackTrace();
717                     encodedPublicKey = null;
718                 }
719             }
720         }
721         if (encodedPublicKey == null) {
722             try {
723                 encodedPublicKey =
724                         KeyFactory.getInstance(publicKey.getAlgorithm())
725                                 .getKeySpec(publicKey, X509EncodedKeySpec.class)
726                                 .getEncoded();
727             } catch (InvalidKeySpecException e) {
728                 throw new InvalidKeyException(
729                         "Failed to obtain X.509 encoded form of public key " + publicKey
730                                 + " of class " + publicKey.getClass().getName(),
731                         e);
732             }
733         }
734         if ((encodedPublicKey == null) || (encodedPublicKey.length == 0)) {
735             throw new InvalidKeyException(
736                     "Failed to obtain X.509 encoded form of public key " + publicKey
737                             + " of class " + publicKey.getClass().getName());
738         }
739         return encodedPublicKey;
740     }
741 
encodeCertificates(List<X509Certificate> certificates)742     public static List<byte[]> encodeCertificates(List<X509Certificate> certificates)
743             throws CertificateEncodingException {
744         List<byte[]> result = new ArrayList<>(certificates.size());
745         for (X509Certificate certificate : certificates) {
746             result.add(certificate.getEncoded());
747         }
748         return result;
749     }
750 
encodeAsLengthPrefixedElement(byte[] bytes)751     public static byte[] encodeAsLengthPrefixedElement(byte[] bytes) {
752         byte[][] adapterBytes = new byte[1][];
753         adapterBytes[0] = bytes;
754         return encodeAsSequenceOfLengthPrefixedElements(adapterBytes);
755     }
756 
encodeAsSequenceOfLengthPrefixedElements(List<byte[]> sequence)757     public static byte[] encodeAsSequenceOfLengthPrefixedElements(List<byte[]> sequence) {
758         return encodeAsSequenceOfLengthPrefixedElements(
759                 sequence.toArray(new byte[sequence.size()][]));
760     }
761 
encodeAsSequenceOfLengthPrefixedElements(byte[][] sequence)762     public static byte[] encodeAsSequenceOfLengthPrefixedElements(byte[][] sequence) {
763         int payloadSize = 0;
764         for (byte[] element : sequence) {
765             payloadSize += 4 + element.length;
766         }
767         ByteBuffer result = ByteBuffer.allocate(payloadSize);
768         result.order(ByteOrder.LITTLE_ENDIAN);
769         for (byte[] element : sequence) {
770             result.putInt(element.length);
771             result.put(element);
772         }
773         return result.array();
774       }
775 
encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( List<Pair<Integer, byte[]>> sequence)776     public static byte[] encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
777             List<Pair<Integer, byte[]>> sequence) {
778         return ApkSigningBlockUtilsLite
779                 .encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(sequence);
780       }
781 
782     /**
783      * Returns the APK Signature Scheme block contained in the provided APK file for the given ID
784      * and the additional information relevant for verifying the block against the file.
785      *
786      * @param blockId the ID value in the APK Signing Block's sequence of ID-value pairs
787      *                identifying the appropriate block to find, e.g. the APK Signature Scheme v2
788      *                block ID.
789      *
790      * @throws SignatureNotFoundException if the APK is not signed using given APK Signature Scheme
791      * @throws IOException if an I/O error occurs while reading the APK
792      */
findSignature( DataSource apk, ApkUtils.ZipSections zipSections, int blockId, Result result)793     public static SignatureInfo findSignature(
794             DataSource apk, ApkUtils.ZipSections zipSections, int blockId, Result result)
795                     throws IOException, SignatureNotFoundException {
796         try {
797             return ApkSigningBlockUtilsLite.findSignature(apk, zipSections, blockId);
798         } catch (com.android.apksig.internal.apk.SignatureNotFoundException e) {
799             throw new SignatureNotFoundException(e.getMessage());
800         }
801     }
802 
803     /**
804      * Generates a new DataSource representing the APK contents before the Central Directory with
805      * padding, if padding is requested.  If the existing data entries before the Central Directory
806      * are already aligned, or no padding is requested, the original DataSource is used.  This
807      * padding is used to allow for verity-based APK verification.
808      *
809      * @return {@code Pair} containing the potentially new {@code DataSource} and the amount of
810      *         padding used.
811      */
generateApkSigningBlockPadding( DataSource beforeCentralDir, boolean apkSigningBlockPaddingSupported)812     public static Pair<DataSource, Integer> generateApkSigningBlockPadding(
813             DataSource beforeCentralDir,
814             boolean apkSigningBlockPaddingSupported) {
815 
816         // Ensure APK Signing Block starts from page boundary.
817         int padSizeBeforeSigningBlock = 0;
818         if (apkSigningBlockPaddingSupported &&
819                 (beforeCentralDir.size() % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0)) {
820             padSizeBeforeSigningBlock = (int) (
821                     ANDROID_COMMON_PAGE_ALIGNMENT_BYTES -
822                             beforeCentralDir.size() % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES);
823             beforeCentralDir = new ChainedDataSource(
824                     beforeCentralDir,
825                     DataSources.asDataSource(
826                             ByteBuffer.allocate(padSizeBeforeSigningBlock)));
827         }
828         return Pair.of(beforeCentralDir, padSizeBeforeSigningBlock);
829     }
830 
copyWithModifiedCDOffset( DataSource beforeCentralDir, DataSource eocd)831     public static DataSource copyWithModifiedCDOffset(
832             DataSource beforeCentralDir, DataSource eocd) throws IOException {
833 
834         // Ensure that, when digesting, ZIP End of Central Directory record's Central Directory
835         // offset field is treated as pointing to the offset at which the APK Signing Block will
836         // start.
837         long centralDirOffsetForDigesting = beforeCentralDir.size();
838         ByteBuffer eocdBuf = ByteBuffer.allocate((int) eocd.size());
839         eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
840         eocd.copyTo(0, (int) eocd.size(), eocdBuf);
841         eocdBuf.flip();
842         ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting);
843         return DataSources.asDataSource(eocdBuf);
844     }
845 
generateApkSigningBlock( List<Pair<byte[], Integer>> apkSignatureSchemeBlockPairs)846     public static byte[] generateApkSigningBlock(
847             List<Pair<byte[], Integer>> apkSignatureSchemeBlockPairs) {
848         // FORMAT:
849         // uint64:  size (excluding this field)
850         // repeated ID-value pairs:
851         //     uint64:           size (excluding this field)
852         //     uint32:           ID
853         //     (size - 4) bytes: value
854         // (extra verity ID-value for padding to make block size a multiple of 4096 bytes)
855         // uint64:  size (same as the one above)
856         // uint128: magic
857 
858         int blocksSize = 0;
859         for (Pair<byte[], Integer> schemeBlockPair : apkSignatureSchemeBlockPairs) {
860             blocksSize += 8 + 4 + schemeBlockPair.getFirst().length; // size + id + value
861         }
862 
863         int resultSize =
864                 8 // size
865                 + blocksSize
866                 + 8 // size
867                 + 16 // magic
868                 ;
869         ByteBuffer paddingPair = null;
870         if (resultSize % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES != 0) {
871             int padding = ANDROID_COMMON_PAGE_ALIGNMENT_BYTES -
872                     (resultSize % ANDROID_COMMON_PAGE_ALIGNMENT_BYTES);
873             if (padding < 12) {  // minimum size of an ID-value pair
874                 padding += ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
875             }
876             paddingPair = ByteBuffer.allocate(padding).order(ByteOrder.LITTLE_ENDIAN);
877             paddingPair.putLong(padding - 8);
878             paddingPair.putInt(VERITY_PADDING_BLOCK_ID);
879             paddingPair.rewind();
880             resultSize += padding;
881         }
882 
883         ByteBuffer result = ByteBuffer.allocate(resultSize);
884         result.order(ByteOrder.LITTLE_ENDIAN);
885         long blockSizeFieldValue = resultSize - 8L;
886         result.putLong(blockSizeFieldValue);
887 
888         for (Pair<byte[], Integer> schemeBlockPair : apkSignatureSchemeBlockPairs) {
889             byte[] apkSignatureSchemeBlock = schemeBlockPair.getFirst();
890             int apkSignatureSchemeId = schemeBlockPair.getSecond();
891             long pairSizeFieldValue = 4L + apkSignatureSchemeBlock.length;
892             result.putLong(pairSizeFieldValue);
893             result.putInt(apkSignatureSchemeId);
894             result.put(apkSignatureSchemeBlock);
895         }
896 
897         if (paddingPair != null) {
898             result.put(paddingPair);
899         }
900 
901         result.putLong(blockSizeFieldValue);
902         result.put(APK_SIGNING_BLOCK_MAGIC);
903 
904         return result.array();
905     }
906 
907     /**
908      * Returns the individual APK signature blocks within the provided {@code apkSigningBlock} in a
909      * {@code List} of {@code Pair} instances where the first element in the {@code Pair} is the
910      * contents / value of the signature block and the second element is the ID of the block.
911      *
912      * @throws IOException if an error is encountered reading the provided {@code apkSigningBlock}
913      */
getApkSignatureBlocks( DataSource apkSigningBlock)914     public static List<Pair<byte[], Integer>> getApkSignatureBlocks(
915             DataSource apkSigningBlock) throws IOException {
916         // FORMAT:
917         // uint64:  size (excluding this field)
918         // repeated ID-value pairs:
919         //     uint64:           size (excluding this field)
920         //     uint32:           ID
921         //     (size - 4) bytes: value
922         // (extra verity ID-value for padding to make block size a multiple of 4096 bytes)
923         // uint64:  size (same as the one above)
924         // uint128: magic
925         long apkSigningBlockSize = apkSigningBlock.size();
926         if (apkSigningBlock.size() > Integer.MAX_VALUE || apkSigningBlockSize < 32) {
927             throw new IllegalArgumentException(
928                     "APK signing block size out of range: " + apkSigningBlockSize);
929         }
930         // Remove the header and footer from the signing block to iterate over only the repeated
931         // ID-value pairs.
932         ByteBuffer apkSigningBlockBuffer = apkSigningBlock.getByteBuffer(8,
933                 (int) apkSigningBlock.size() - 32);
934         apkSigningBlockBuffer.order(ByteOrder.LITTLE_ENDIAN);
935         List<Pair<byte[], Integer>> signatureBlocks = new ArrayList<>();
936         while (apkSigningBlockBuffer.hasRemaining()) {
937             long blockLength = apkSigningBlockBuffer.getLong();
938             if (blockLength > Integer.MAX_VALUE || blockLength < 4) {
939                 throw new IllegalArgumentException(
940                         "Block index " + (signatureBlocks.size() + 1) + " size out of range: "
941                                 + blockLength);
942             }
943             int blockId = apkSigningBlockBuffer.getInt();
944             // Since the block ID has already been read from the signature block read the next
945             // blockLength - 4 bytes as the value.
946             byte[] blockValue = new byte[(int) blockLength - 4];
947             apkSigningBlockBuffer.get(blockValue);
948             signatureBlocks.add(Pair.of(blockValue, blockId));
949         }
950         return signatureBlocks;
951     }
952 
953     /**
954      * Returns the individual APK signers within the provided {@code signatureBlock} in a {@code
955      * List} of {@code Pair} instances where the first element is a {@code List} of {@link
956      * X509Certificate}s and the second element is a byte array of the individual signer's block.
957      *
958      * <p>This method supports any signature block that adheres to the following format up to the
959      * signing certificate(s):
960      * <pre>
961      * * length-prefixed sequence of length-prefixed signers
962      *   * length-prefixed signed data
963      *     * length-prefixed sequence of length-prefixed digests:
964      *       * uint32: signature algorithm ID
965      *       * length-prefixed bytes: digest of contents
966      *     * length-prefixed sequence of certificates:
967      *       * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
968      * </pre>
969      *
970      * <p>Note, this is a convenience method to obtain any signers from an existing signature block;
971      * the signature of each signer will not be verified.
972      *
973      * @throws ApkFormatException if an error is encountered while parsing the provided {@code
974      * signatureBlock}
975      * @throws CertificateException if the signing certificate(s) within an individual signer block
976      * cannot be parsed
977      */
getApkSignatureBlockSigners( byte[] signatureBlock)978     public static List<Pair<List<X509Certificate>, byte[]>> getApkSignatureBlockSigners(
979             byte[] signatureBlock) throws ApkFormatException, CertificateException {
980         ByteBuffer signatureBlockBuffer = ByteBuffer.wrap(signatureBlock);
981         signatureBlockBuffer.order(ByteOrder.LITTLE_ENDIAN);
982         ByteBuffer signersBuffer = getLengthPrefixedSlice(signatureBlockBuffer);
983         List<Pair<List<X509Certificate>, byte[]>> signers = new ArrayList<>();
984         while (signersBuffer.hasRemaining()) {
985             // Parse the next signer block, save all of its bytes for the resulting List, and
986             // rewind the buffer to allow the signing certificate(s) to be parsed.
987             ByteBuffer signer = getLengthPrefixedSlice(signersBuffer);
988             byte[] signerBytes = new byte[signer.remaining()];
989             signer.get(signerBytes);
990             signer.rewind();
991 
992             ByteBuffer signedData = getLengthPrefixedSlice(signer);
993             // The first length prefixed slice is the sequence of digests which are not required
994             // when obtaining the signing certificate(s).
995             getLengthPrefixedSlice(signedData);
996             ByteBuffer certificatesBuffer = getLengthPrefixedSlice(signedData);
997             List<X509Certificate> certificates = new ArrayList<>();
998             while (certificatesBuffer.hasRemaining()) {
999                 int certLength = certificatesBuffer.getInt();
1000                 byte[] certBytes = new byte[certLength];
1001                 if (certLength > certificatesBuffer.remaining()) {
1002                     throw new IllegalArgumentException(
1003                             "Cert index " + (certificates.size() + 1) + " under signer index "
1004                                     + (signers.size() + 1) + " size out of range: " + certLength);
1005                 }
1006                 certificatesBuffer.get(certBytes);
1007                 GuaranteedEncodedFormX509Certificate signerCert =
1008                         new GuaranteedEncodedFormX509Certificate(
1009                                 X509CertificateUtils.generateCertificate(certBytes), certBytes);
1010                 certificates.add(signerCert);
1011             }
1012             signers.add(Pair.of(certificates, signerBytes));
1013         }
1014         return signers;
1015     }
1016 
1017     /**
1018      * Computes the digests of the given APK components according to the algorithms specified in the
1019      * given SignerConfigs.
1020      *
1021      * @param signerConfigs signer configurations, one for each signer At least one signer config
1022      *        must be provided.
1023      *
1024      * @throws IOException if an I/O error occurs
1025      * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is
1026      *         missing
1027      * @throws SignatureException if an error occurs when computing digests of generating
1028      *         signatures
1029      */
1030     public static Pair<List<SignerConfig>, Map<ContentDigestAlgorithm, byte[]>>
computeContentDigests( RunnablesExecutor executor, DataSource beforeCentralDir, DataSource centralDir, DataSource eocd, List<SignerConfig> signerConfigs)1031             computeContentDigests(
1032                     RunnablesExecutor executor,
1033                     DataSource beforeCentralDir,
1034                     DataSource centralDir,
1035                     DataSource eocd,
1036                     List<SignerConfig> signerConfigs)
1037                             throws IOException, NoSuchAlgorithmException, SignatureException {
1038         if (signerConfigs.isEmpty()) {
1039             throw new IllegalArgumentException(
1040                     "No signer configs provided. At least one is required");
1041         }
1042 
1043         // Figure out which digest(s) to use for APK contents.
1044         Set<ContentDigestAlgorithm> contentDigestAlgorithms = new HashSet<>(1);
1045         for (SignerConfig signerConfig : signerConfigs) {
1046             for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
1047                 contentDigestAlgorithms.add(signatureAlgorithm.getContentDigestAlgorithm());
1048             }
1049         }
1050 
1051         // Compute digests of APK contents.
1052         Map<ContentDigestAlgorithm, byte[]> contentDigests; // digest algorithm ID -> digest
1053         try {
1054             contentDigests =
1055                     computeContentDigests(
1056                             executor,
1057                             contentDigestAlgorithms,
1058                             beforeCentralDir,
1059                             centralDir,
1060                             eocd);
1061         } catch (IOException e) {
1062             throw new IOException("Failed to read APK being signed", e);
1063         } catch (DigestException e) {
1064             throw new SignatureException("Failed to compute digests of APK", e);
1065         }
1066 
1067         // Sign the digests and wrap the signatures and signer info into an APK Signing Block.
1068         return Pair.of(signerConfigs, contentDigests);
1069     }
1070 
1071     /**
1072      * Returns the subset of signatures which are expected to be verified by at least one Android
1073      * platform version in the {@code [minSdkVersion, maxSdkVersion]} range. The returned result is
1074      * guaranteed to contain at least one signature.
1075      *
1076      * <p>Each Android platform version typically verifies exactly one signature from the provided
1077      * {@code signatures} set. This method returns the set of these signatures collected over all
1078      * requested platform versions. As a result, the result may contain more than one signature.
1079      *
1080      * @throws NoSupportedSignaturesException if no supported signatures were
1081      *         found for an Android platform version in the range.
1082      */
getSignaturesToVerify( List<T> signatures, int minSdkVersion, int maxSdkVersion)1083     public static <T extends ApkSupportedSignature> List<T> getSignaturesToVerify(
1084             List<T> signatures, int minSdkVersion, int maxSdkVersion)
1085             throws NoSupportedSignaturesException {
1086         return getSignaturesToVerify(signatures, minSdkVersion, maxSdkVersion, false);
1087     }
1088 
1089     /**
1090      * Returns the subset of signatures which are expected to be verified by at least one Android
1091      * platform version in the {@code [minSdkVersion, maxSdkVersion]} range. The returned result is
1092      * guaranteed to contain at least one signature.
1093      *
1094      * <p>{@code onlyRequireJcaSupport} can be set to true for cases that only require verifying a
1095      * signature within the signing block using the standard JCA.
1096      *
1097      * <p>Each Android platform version typically verifies exactly one signature from the provided
1098      * {@code signatures} set. This method returns the set of these signatures collected over all
1099      * requested platform versions. As a result, the result may contain more than one signature.
1100      *
1101      * @throws NoSupportedSignaturesException if no supported signatures were
1102      *         found for an Android platform version in the range.
1103      */
getSignaturesToVerify( List<T> signatures, int minSdkVersion, int maxSdkVersion, boolean onlyRequireJcaSupport)1104     public static <T extends ApkSupportedSignature> List<T> getSignaturesToVerify(
1105             List<T> signatures, int minSdkVersion, int maxSdkVersion,
1106             boolean onlyRequireJcaSupport) throws NoSupportedSignaturesException {
1107         try {
1108             return ApkSigningBlockUtilsLite.getSignaturesToVerify(signatures, minSdkVersion,
1109                     maxSdkVersion, onlyRequireJcaSupport);
1110         } catch (NoApkSupportedSignaturesException e) {
1111             throw new NoSupportedSignaturesException(e.getMessage());
1112         }
1113     }
1114 
1115     public static class NoSupportedSignaturesException extends NoApkSupportedSignaturesException {
NoSupportedSignaturesException(String message)1116         public NoSupportedSignaturesException(String message) {
1117             super(message);
1118         }
1119     }
1120 
1121     public static class SignatureNotFoundException extends Exception {
1122         private static final long serialVersionUID = 1L;
1123 
SignatureNotFoundException(String message)1124         public SignatureNotFoundException(String message) {
1125             super(message);
1126         }
1127 
SignatureNotFoundException(String message, Throwable cause)1128         public SignatureNotFoundException(String message, Throwable cause) {
1129             super(message, cause);
1130         }
1131     }
1132 
1133     /**
1134      * uses the SignatureAlgorithms in the provided signerConfig to sign the provided data
1135      *
1136      * @return list of signature algorithm IDs and their corresponding signatures over the data.
1137      */
generateSignaturesOverData( SignerConfig signerConfig, byte[] data)1138     public static List<Pair<Integer, byte[]>> generateSignaturesOverData(
1139             SignerConfig signerConfig, byte[] data)
1140                     throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
1141         List<Pair<Integer, byte[]>> signatures =
1142                 new ArrayList<>(signerConfig.signatureAlgorithms.size());
1143         PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
1144         for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
1145             Pair<String, ? extends AlgorithmParameterSpec> sigAlgAndParams =
1146                     signatureAlgorithm.getJcaSignatureAlgorithmAndParams();
1147             String jcaSignatureAlgorithm = sigAlgAndParams.getFirst();
1148             AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgAndParams.getSecond();
1149 
1150             byte[] signatureBytes;
1151             try {
1152                 signatureBytes =
1153                         SignerEngineFactory.getImplementation(
1154                                         signerConfig.keyConfig,
1155                                         jcaSignatureAlgorithm,
1156                                         jcaSignatureAlgorithmParams)
1157                                 .sign(data);
1158             } catch (InvalidKeyException e) {
1159                 throw new InvalidKeyException("Failed to sign using " + jcaSignatureAlgorithm, e);
1160             } catch (InvalidAlgorithmParameterException | SignatureException e) {
1161                 throw new SignatureException("Failed to sign using " + jcaSignatureAlgorithm, e);
1162             }
1163 
1164             try {
1165                 Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
1166                 signature.initVerify(publicKey);
1167                 if (jcaSignatureAlgorithmParams != null) {
1168                     signature.setParameter(jcaSignatureAlgorithmParams);
1169                 }
1170                 signature.update(data);
1171                 if (!signature.verify(signatureBytes)) {
1172                     throw new SignatureException("Failed to verify generated "
1173                             + jcaSignatureAlgorithm
1174                             + " signature using public key from certificate");
1175                 }
1176             } catch (InvalidKeyException e) {
1177                 throw new InvalidKeyException(
1178                         "Failed to verify generated " + jcaSignatureAlgorithm + " signature using"
1179                                 + " public key from certificate", e);
1180             } catch (InvalidAlgorithmParameterException | SignatureException e) {
1181                 throw new SignatureException(
1182                         "Failed to verify generated " + jcaSignatureAlgorithm + " signature using"
1183                                 + " public key from certificate", e);
1184             }
1185 
1186             signatures.add(Pair.of(signatureAlgorithm.getId(), signatureBytes));
1187         }
1188         return signatures;
1189     }
1190 
1191     /**
1192      * Wrap the signature according to CMS PKCS #7 RFC 5652.
1193      * The high-level simplified structure is as follows:
1194      * // ContentInfo
1195      *     //   digestAlgorithm
1196      *     //   SignedData
1197      *     //     bag of certificates
1198      *     //     SignerInfo
1199      *     //       signing cert issuer and serial number (for locating the cert in the above bag)
1200      *     //       digestAlgorithm
1201      *     //       signatureAlgorithm
1202      *     //       signature
1203      *
1204      * @throws Asn1EncodingException if the ASN.1 structure could not be encoded
1205      */
generatePkcs7DerEncodedMessage( byte[] signatureBytes, ByteBuffer data, List<X509Certificate> signerCerts, AlgorithmIdentifier digestAlgorithmId, AlgorithmIdentifier signatureAlgorithmId)1206     public static byte[] generatePkcs7DerEncodedMessage(
1207             byte[] signatureBytes, ByteBuffer data, List<X509Certificate> signerCerts,
1208             AlgorithmIdentifier digestAlgorithmId, AlgorithmIdentifier signatureAlgorithmId)
1209             throws Asn1EncodingException, CertificateEncodingException {
1210         SignerInfo signerInfo = new SignerInfo();
1211         signerInfo.version = 1;
1212         X509Certificate signingCert = signerCerts.get(0);
1213         X500Principal signerCertIssuer = signingCert.getIssuerX500Principal();
1214         signerInfo.sid =
1215                 new SignerIdentifier(
1216                         new IssuerAndSerialNumber(
1217                                 new Asn1OpaqueObject(signerCertIssuer.getEncoded()),
1218                                 signingCert.getSerialNumber()));
1219 
1220         signerInfo.digestAlgorithm = digestAlgorithmId;
1221         signerInfo.signatureAlgorithm = signatureAlgorithmId;
1222         signerInfo.signature = ByteBuffer.wrap(signatureBytes);
1223 
1224         SignedData signedData = new SignedData();
1225         signedData.certificates = new ArrayList<>(signerCerts.size());
1226         for (X509Certificate cert : signerCerts) {
1227             signedData.certificates.add(new Asn1OpaqueObject(cert.getEncoded()));
1228         }
1229         signedData.version = 1;
1230         signedData.digestAlgorithms = Collections.singletonList(digestAlgorithmId);
1231         signedData.encapContentInfo = new EncapsulatedContentInfo(Pkcs7Constants.OID_DATA);
1232         // If data is not null, data will be embedded as is in the result -- an attached pcsk7
1233         signedData.encapContentInfo.content = data;
1234         signedData.signerInfos = Collections.singletonList(signerInfo);
1235         ContentInfo contentInfo = new ContentInfo();
1236         contentInfo.contentType = Pkcs7Constants.OID_SIGNED_DATA;
1237         contentInfo.content = new Asn1OpaqueObject(Asn1DerEncoder.encode(signedData));
1238         return Asn1DerEncoder.encode(contentInfo);
1239     }
1240 
1241     /**
1242      * Picks the correct v2/v3 digest for v4 signature verification.
1243      *
1244      * Keep in sync with pickBestDigestForV4 in framework's ApkSigningBlockUtils.
1245      */
pickBestDigestForV4(Map<ContentDigestAlgorithm, byte[]> contentDigests)1246     public static byte[] pickBestDigestForV4(Map<ContentDigestAlgorithm, byte[]> contentDigests) {
1247         for (ContentDigestAlgorithm algo : V4_CONTENT_DIGEST_ALGORITHMS) {
1248             if (contentDigests.containsKey(algo)) {
1249                 return contentDigests.get(algo);
1250             }
1251         }
1252         return null;
1253     }
1254 
1255     /** Signer configuration. */
1256     public static class SignerConfig {
1257         /**
1258          * Private key.
1259          *
1260          * @deprecated all internal usage has migrated to use {@link #keyConfig}. This field is not
1261          *     removed so that compilation is not broken for clients referencing it, but using this
1262          *     field may lead to unexpected errors.
1263          */
1264         @Deprecated public PrivateKey privateKey;
1265 
1266         /** Signing key config. */
1267         public KeyConfig keyConfig;
1268 
1269         /**
1270          * Certificates, with the first certificate containing the public key corresponding to
1271          * {@link #keyConfig}.
1272          */
1273         public List<X509Certificate> certificates;
1274 
1275         /**
1276          * List of signature algorithms with which to sign.
1277          */
1278         public List<SignatureAlgorithm> signatureAlgorithms;
1279 
1280         public int minSdkVersion;
1281         public int maxSdkVersion;
1282         public boolean signerTargetsDevRelease;
1283         public SigningCertificateLineage signingCertificateLineage;
1284     }
1285 
1286     public static class Result extends ApkSigResult {
1287         public SigningCertificateLineage signingCertificateLineage = null;
1288         public final List<Result.SignerInfo> signers = new ArrayList<>();
1289         private final List<ApkVerifier.IssueWithParams> mWarnings = new ArrayList<>();
1290         private final List<ApkVerifier.IssueWithParams> mErrors = new ArrayList<>();
1291 
Result(int signatureSchemeVersion)1292         public Result(int signatureSchemeVersion) {
1293             super(signatureSchemeVersion);
1294         }
1295 
containsErrors()1296         public boolean containsErrors() {
1297             if (!mErrors.isEmpty()) {
1298                 return true;
1299             }
1300             if (!signers.isEmpty()) {
1301                 for (Result.SignerInfo signer : signers) {
1302                     if (signer.containsErrors()) {
1303                         return true;
1304                     }
1305                 }
1306             }
1307             return false;
1308         }
1309 
containsWarnings()1310         public boolean containsWarnings() {
1311             if (!mWarnings.isEmpty()) {
1312                 return true;
1313             }
1314             if (!signers.isEmpty()) {
1315                 for (Result.SignerInfo signer : signers) {
1316                     if (signer.containsWarnings()) {
1317                         return true;
1318                     }
1319                 }
1320             }
1321             return false;
1322         }
1323 
addError(ApkVerifier.Issue msg, Object... parameters)1324         public void addError(ApkVerifier.Issue msg, Object... parameters) {
1325             mErrors.add(new ApkVerifier.IssueWithParams(msg, parameters));
1326         }
1327 
addWarning(ApkVerifier.Issue msg, Object... parameters)1328         public void addWarning(ApkVerifier.Issue msg, Object... parameters) {
1329             mWarnings.add(new ApkVerifier.IssueWithParams(msg, parameters));
1330         }
1331 
1332         @Override
getErrors()1333         public List<ApkVerifier.IssueWithParams> getErrors() {
1334             return mErrors;
1335         }
1336 
1337         @Override
getWarnings()1338         public List<ApkVerifier.IssueWithParams> getWarnings() {
1339             return mWarnings;
1340         }
1341 
1342         public static class SignerInfo extends ApkSignerInfo {
1343             public List<ContentDigest> contentDigests = new ArrayList<>();
1344             public Map<ContentDigestAlgorithm, byte[]> verifiedContentDigests = new HashMap<>();
1345             public List<Signature> signatures = new ArrayList<>();
1346             public Map<SignatureAlgorithm, byte[]> verifiedSignatures = new HashMap<>();
1347             public List<AdditionalAttribute> additionalAttributes = new ArrayList<>();
1348             public byte[] signedData;
1349             public int minSdkVersion;
1350             public int maxSdkVersion;
1351             public SigningCertificateLineage signingCertificateLineage;
1352 
1353             private final List<ApkVerifier.IssueWithParams> mWarnings = new ArrayList<>();
1354             private final List<ApkVerifier.IssueWithParams> mErrors = new ArrayList<>();
1355 
addError(ApkVerifier.Issue msg, Object... parameters)1356             public void addError(ApkVerifier.Issue msg, Object... parameters) {
1357                 mErrors.add(new ApkVerifier.IssueWithParams(msg, parameters));
1358             }
1359 
addWarning(ApkVerifier.Issue msg, Object... parameters)1360             public void addWarning(ApkVerifier.Issue msg, Object... parameters) {
1361                 mWarnings.add(new ApkVerifier.IssueWithParams(msg, parameters));
1362             }
1363 
containsErrors()1364             public boolean containsErrors() {
1365                 return !mErrors.isEmpty();
1366             }
1367 
containsWarnings()1368             public boolean containsWarnings() {
1369                 return !mWarnings.isEmpty();
1370             }
1371 
getErrors()1372             public List<ApkVerifier.IssueWithParams> getErrors() {
1373                 return mErrors;
1374             }
1375 
getWarnings()1376             public List<ApkVerifier.IssueWithParams> getWarnings() {
1377                 return mWarnings;
1378             }
1379 
1380             public static class ContentDigest {
1381                 private final int mSignatureAlgorithmId;
1382                 private final byte[] mValue;
1383 
ContentDigest(int signatureAlgorithmId, byte[] value)1384                 public ContentDigest(int signatureAlgorithmId, byte[] value) {
1385                     mSignatureAlgorithmId  = signatureAlgorithmId;
1386                     mValue = value;
1387                 }
1388 
getSignatureAlgorithmId()1389                 public int getSignatureAlgorithmId() {
1390                     return mSignatureAlgorithmId;
1391                 }
1392 
getValue()1393                 public byte[] getValue() {
1394                     return mValue;
1395                 }
1396             }
1397 
1398             public static class Signature {
1399                 private final int mAlgorithmId;
1400                 private final byte[] mValue;
1401 
Signature(int algorithmId, byte[] value)1402                 public Signature(int algorithmId, byte[] value) {
1403                     mAlgorithmId  = algorithmId;
1404                     mValue = value;
1405                 }
1406 
getAlgorithmId()1407                 public int getAlgorithmId() {
1408                     return mAlgorithmId;
1409                 }
1410 
getValue()1411                 public byte[] getValue() {
1412                     return mValue;
1413                 }
1414             }
1415 
1416             public static class AdditionalAttribute {
1417                 private final int mId;
1418                 private final byte[] mValue;
1419 
AdditionalAttribute(int id, byte[] value)1420                 public AdditionalAttribute(int id, byte[] value) {
1421                     mId  = id;
1422                     mValue = value.clone();
1423                 }
1424 
getId()1425                 public int getId() {
1426                     return mId;
1427                 }
1428 
getValue()1429                 public byte[] getValue() {
1430                     return mValue.clone();
1431                 }
1432             }
1433         }
1434     }
1435 
1436     public static class SupportedSignature extends ApkSupportedSignature {
SupportedSignature(SignatureAlgorithm algorithm, byte[] signature)1437         public SupportedSignature(SignatureAlgorithm algorithm, byte[] signature) {
1438             super(algorithm, signature);
1439         }
1440     }
1441 
1442     public static class SigningSchemeBlockAndDigests {
1443         public final Pair<byte[], Integer> signingSchemeBlock;
1444         public final Map<ContentDigestAlgorithm, byte[]> digestInfo;
1445 
SigningSchemeBlockAndDigests( Pair<byte[], Integer> signingSchemeBlock, Map<ContentDigestAlgorithm, byte[]> digestInfo)1446         public SigningSchemeBlockAndDigests(
1447                 Pair<byte[], Integer> signingSchemeBlock,
1448                 Map<ContentDigestAlgorithm, byte[]> digestInfo) {
1449             this.signingSchemeBlock = signingSchemeBlock;
1450             this.digestInfo = digestInfo;
1451         }
1452     }
1453 }
1454