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;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import com.android.apksig.apk.ApkFormatException;
25 import com.android.apksig.internal.apk.ApkSigningBlockUtils;
26 import com.android.apksig.internal.apk.v3.V3SchemeSigner;
27 import com.android.apksig.internal.util.ByteBufferDataSource;
28 import com.android.apksig.internal.util.ByteBufferUtils;
29 import com.android.apksig.internal.util.Resources;
30 
31 import com.android.apksig.SigningCertificateLineage.SignerConfig;
32 import com.android.apksig.SigningCertificateLineage.SignerCapabilities;
33 
34 import com.android.apksig.util.DataSource;
35 
36 import java.io.IOException;
37 import java.nio.ByteBuffer;
38 
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 
44 import java.io.File;
45 import java.nio.ByteOrder;
46 import java.security.PrivateKey;
47 import java.security.cert.X509Certificate;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.Collections;
51 import java.util.HashSet;
52 import java.util.List;
53 import java.util.Set;
54 
55 @RunWith(JUnit4.class)
56 public class SigningCertificateLineageTest {
57 
58     // createLineageWithSignersFromResources and updateLineageWithSignerFromResources will add the
59     // SignerConfig for the signers added to the Lineage to this list.
60     private List<SignerConfig> mSigners;
61 
62     // All signers with the same prefix and an _X suffix were signed with the private key of the
63     // (X-1) signer.
64     private static final String FIRST_RSA_1024_SIGNER_RESOURCE_NAME = "rsa-1024";
65     private static final String SECOND_RSA_1024_SIGNER_RESOURCE_NAME = "rsa-1024_2";
66 
67     private static final String FIRST_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048";
68     private static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2";
69     private static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3";
70 
71     @Before
setUp()72     public void setUp() {
73         mSigners = new ArrayList<>();
74     }
75 
76     @Test
testFirstRotationContainsExpectedSigners()77     public void testFirstRotationContainsExpectedSigners() throws Exception {
78         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
79                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
80         assertLineageContainsExpectedSigners(lineage, mSigners);
81         SignerConfig unknownSigner = Resources.toLineageSignerConfig(getClass(),
82                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
83         assertFalse("The signer " + unknownSigner.getCertificate().getSubjectDN()
84                 + " should not be in the lineage", lineage.isSignerInLineage(unknownSigner));
85     }
86 
87     @Test
testRotationWithExistingLineageContainsExpectedSigners()88     public void testRotationWithExistingLineageContainsExpectedSigners() throws Exception {
89         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
90                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
91         lineage = updateLineageWithSignerFromResources(lineage,
92                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
93         assertLineageContainsExpectedSigners(lineage, mSigners);
94     }
95 
96     @Test
testLineageFromFileContainsExpectedSigners()97     public void testLineageFromFileContainsExpectedSigners() throws Exception {
98         // This file contains the lineage with the three rsa-2048 signers
99         DataSource lineageDataSource = Resources.toDataSource(getClass(),
100                 "rsa-2048-lineage-3-signers");
101         SigningCertificateLineage lineage = SigningCertificateLineage.readFromDataSource(
102                 lineageDataSource);
103         List<SignerConfig> signers = new ArrayList<>(3);
104         signers.add(
105                 Resources.toLineageSignerConfig(getClass(), FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
106         signers.add(
107                 Resources.toLineageSignerConfig(getClass(), SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
108         signers.add(
109                 Resources.toLineageSignerConfig(getClass(), THIRD_RSA_2048_SIGNER_RESOURCE_NAME));
110         assertLineageContainsExpectedSigners(lineage, signers);
111     }
112 
113     @Test
testLineageFromFileDoesNotContainUnknownSigner()114     public void testLineageFromFileDoesNotContainUnknownSigner() throws Exception {
115         // This file contains the lineage with the first two rsa-2048 signers
116         SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(),
117                 "rsa-2048-lineage-2-signers");
118         SignerConfig unknownSigner = Resources.toLineageSignerConfig(getClass(),
119                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
120         assertFalse("The signer " + unknownSigner.getCertificate().getSubjectDN()
121                 + " should not be in the lineage", lineage.isSignerInLineage(unknownSigner));
122     }
123 
124     @Test(expected = IllegalArgumentException.class)
testLineageFromFileWithInvalidMagicFails()125     public void testLineageFromFileWithInvalidMagicFails() throws Exception {
126         // This file contains the lineage with two rsa-2048 signers and a modified MAGIC value
127         Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-invalid-magic");
128     }
129 
130     @Test(expected = IllegalArgumentException.class)
testLineageFromFileWithInvalidVersionFails()131     public void testLineageFromFileWithInvalidVersionFails() throws Exception {
132         // This file contains the lineage with two rsa-2048 signers and an invalid value of FF for
133         // the version
134         Resources.toSigningCertificateLineage(getClass(), "rsa-2048-lineage-invalid-version");
135     }
136 
137     @Test
testLineageWrittenToFileContainsExpectedSigners()138     public void testLineageWrittenToFileContainsExpectedSigners() throws Exception {
139         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
140                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
141         lineage = updateLineageWithSignerFromResources(lineage,
142                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
143         File lineageFile = File.createTempFile(getClass().getSimpleName(), ".bin");
144         lineageFile.deleteOnExit();
145         lineage.writeToFile(lineageFile);
146         lineage = SigningCertificateLineage.readFromFile(lineageFile);
147         assertLineageContainsExpectedSigners(lineage, mSigners);
148     }
149 
150     @Test
testUpdatedCapabilitiesInLineage()151     public void testUpdatedCapabilitiesInLineage() throws Exception {
152         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
153                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
154         SignerConfig oldSignerConfig = mSigners.get(0);
155         List<Boolean> expectedCapabilityValues = Arrays.asList(false, false, false, false, false);
156         SignerCapabilities newCapabilities = buildSignerCapabilities(expectedCapabilityValues);
157         lineage.updateSignerCapabilities(oldSignerConfig, newCapabilities);
158         SignerCapabilities updatedCapabilities = lineage.getSignerCapabilities(oldSignerConfig);
159         assertExpectedCapabilityValues(updatedCapabilities, expectedCapabilityValues);
160     }
161 
162     @Test
testUpdatedCapabilitiesInLineageWrittenToFile()163     public void testUpdatedCapabilitiesInLineageWrittenToFile() throws Exception {
164         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
165                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
166         SignerConfig oldSignerConfig = mSigners.get(0);
167         List<Boolean> expectedCapabilityValues = Arrays.asList(false, false, false, false, false);
168         SignerCapabilities newCapabilities = buildSignerCapabilities(expectedCapabilityValues);
169         lineage.updateSignerCapabilities(oldSignerConfig, newCapabilities);
170         File lineageFile = File.createTempFile(getClass().getSimpleName(), ".bin");
171         lineageFile.deleteOnExit();
172         lineage.writeToFile(lineageFile);
173         lineage = SigningCertificateLineage.readFromFile(lineageFile);
174         SignerCapabilities updatedCapabilities = lineage.getSignerCapabilities(oldSignerConfig);
175         assertExpectedCapabilityValues(updatedCapabilities, expectedCapabilityValues);
176     }
177 
178     @Test
testCapabilitiesAreNotUpdatedWithDefaultValues()179     public void testCapabilitiesAreNotUpdatedWithDefaultValues() throws Exception {
180         // This file contains the lineage with the first two rsa-2048 signers with the first signer
181         // having all of the capabilities set to false.
182         SigningCertificateLineage lineage = Resources.toSigningCertificateLineage(getClass(),
183                 "rsa-2048-lineage-no-capabilities-first-signer");
184         List<Boolean> expectedCapabilityValues = Arrays.asList(false, false, false, false, false);
185         SignerConfig oldSignerConfig = Resources.toLineageSignerConfig(getClass(),
186                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
187         SignerCapabilities oldSignerCapabilities = lineage.getSignerCapabilities(oldSignerConfig);
188         assertExpectedCapabilityValues(oldSignerCapabilities, expectedCapabilityValues);
189         // The builder is called directly to ensure all of the capabilities are set to the default
190         // values and the caller configured flags are not modified in this SignerCapabilities.
191         SignerCapabilities newCapabilities = new SignerCapabilities.Builder().build();
192         lineage.updateSignerCapabilities(oldSignerConfig, newCapabilities);
193         SignerCapabilities updatedCapabilities = lineage.getSignerCapabilities(oldSignerConfig);
194         assertExpectedCapabilityValues(updatedCapabilities, expectedCapabilityValues);
195     }
196 
197     @Test
testFirstRotationWitNonDefaultCapabilitiesForSigners()198     public void testFirstRotationWitNonDefaultCapabilitiesForSigners() throws Exception {
199         SignerConfig oldSigner = Resources.toLineageSignerConfig(getClass(),
200                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
201         SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(),
202                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
203         List<Boolean> oldSignerCapabilityValues = Arrays.asList(false, false, false, false, false);
204         List<Boolean> newSignerCapabilityValues = Arrays.asList(false, true, false, false, false);
205         SigningCertificateLineage lineage = new SigningCertificateLineage.Builder(oldSigner,
206                 newSigner)
207                 .setOriginalCapabilities(buildSignerCapabilities(oldSignerCapabilityValues))
208                 .setNewCapabilities(buildSignerCapabilities(newSignerCapabilityValues))
209                 .build();
210         SignerCapabilities oldSignerCapabilities = lineage.getSignerCapabilities(oldSigner);
211         assertExpectedCapabilityValues(oldSignerCapabilities, oldSignerCapabilityValues);
212         SignerCapabilities newSignerCapabilities = lineage.getSignerCapabilities(newSigner);
213         assertExpectedCapabilityValues(newSignerCapabilities, newSignerCapabilityValues);
214     }
215 
216     @Test
testRotationWithExitingLineageAndNonDefaultCapabilitiesForNewSigner()217     public void testRotationWithExitingLineageAndNonDefaultCapabilitiesForNewSigner()
218             throws Exception {
219         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
220                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
221         SignerConfig oldSigner = mSigners.get(mSigners.size() - 1);
222         SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(),
223                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
224         List<Boolean> newSignerCapabilityValues = Arrays.asList(false, false, false, false, false);
225         lineage = lineage.spawnDescendant(oldSigner, newSigner,
226                 buildSignerCapabilities(newSignerCapabilityValues));
227         SignerCapabilities newSignerCapabilities = lineage.getSignerCapabilities(newSigner);
228         assertExpectedCapabilityValues(newSignerCapabilities, newSignerCapabilityValues);
229     }
230 
231     @Test(expected = IllegalArgumentException.class)
testRotationWithExistingLineageUsingNonParentSignerFails()232     public void testRotationWithExistingLineageUsingNonParentSignerFails() throws Exception {
233         // When rotating the signing certificate the most recent signer must be provided to the
234         // spawnDescendant method. This test ensures that using an ancestor of the most recent
235         // signer will fail as expected.
236         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
237                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
238         SignerConfig oldestSigner = mSigners.get(0);
239         SignerConfig newSigner = Resources.toLineageSignerConfig(getClass(),
240                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
241         lineage.spawnDescendant(oldestSigner, newSigner);
242     }
243 
244     @Test
testLineageFromV3SignerAttribute()245     public void testLineageFromV3SignerAttribute() throws Exception {
246         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
247                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
248         // The format of the V3 Signer Attribute is as follows (little endian):
249         // * length-prefixed bytes: attribute pair
250         //   * uint32: ID
251         //   * bytes: value - encoded V3 SigningCertificateLineage
252         ByteBuffer v3SignerAttribute = ByteBuffer.wrap(lineage.generateV3SignerAttribute());
253         v3SignerAttribute.order(ByteOrder.LITTLE_ENDIAN);
254         ByteBuffer attribute = ApkSigningBlockUtils.getLengthPrefixedSlice(v3SignerAttribute);
255         // The generateV3SignerAttribute method should only use the PROOF_OF_ROTATION_ATTR_ID
256         // value for the ID.
257         int id = attribute.getInt();
258         assertEquals(
259                 "The ID of the v3SignerAttribute ByteBuffer is not the expected "
260                         + "PROOF_OF_ROTATION_ATTR_ID",
261                 V3SchemeSigner.PROOF_OF_ROTATION_ATTR_ID, id);
262         lineage = SigningCertificateLineage.readFromV3AttributeValue(
263                 ByteBufferUtils.toByteArray(attribute));
264         assertLineageContainsExpectedSigners(lineage, mSigners);
265     }
266 
267     @Test
testSortedSignerConfigsAreInSortedOrder()268     public void testSortedSignerConfigsAreInSortedOrder() throws Exception {
269         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
270                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
271         DefaultApkSignerEngine.SignerConfig oldSigner = getApkSignerEngineSignerConfigFromResources(
272                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
273         DefaultApkSignerEngine.SignerConfig newSigner = getApkSignerEngineSignerConfigFromResources(
274                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
275         List<DefaultApkSignerEngine.SignerConfig> signers = Arrays.asList(newSigner, oldSigner);
276         List<DefaultApkSignerEngine.SignerConfig> sortedSigners = lineage.sortSignerConfigs(
277                 signers);
278         assertEquals("The sorted signer list does not contain the expected number of elements",
279                 signers.size(), sortedSigners.size());
280         assertEquals("The first element in the sorted list should be the first signer", oldSigner,
281                 sortedSigners.get(0));
282         assertEquals("The second element in the sorted list should be the second signer", newSigner,
283                 sortedSigners.get(1));
284     }
285 
286     @Test(expected = IllegalArgumentException.class)
testSortedSignerConfigsWithUnknownSignerFails()287     public void testSortedSignerConfigsWithUnknownSignerFails() throws Exception {
288         // Since this test includes a signer that is not in the lineage the sort should fail with
289         // an IllegalArgumentException.
290         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
291                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
292         DefaultApkSignerEngine.SignerConfig oldSigner = getApkSignerEngineSignerConfigFromResources(
293                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
294         DefaultApkSignerEngine.SignerConfig newSigner = getApkSignerEngineSignerConfigFromResources(
295                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
296         DefaultApkSignerEngine.SignerConfig unknownSigner =
297                 getApkSignerEngineSignerConfigFromResources(THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
298         List<DefaultApkSignerEngine.SignerConfig> signers = Arrays.asList(newSigner, oldSigner,
299                 unknownSigner);
300         lineage.sortSignerConfigs(signers);
301     }
302 
303     @Test
testAllExpectedCertificatesAreInLineage()304     public void testAllExpectedCertificatesAreInLineage() throws Exception {
305         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
306                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
307         lineage = updateLineageWithSignerFromResources(lineage,
308                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
309         Set<X509Certificate> expectedCertSet = new HashSet<>();
310         for (int i = 0; i < mSigners.size(); i++) {
311             expectedCertSet.add(mSigners.get(i).getCertificate());
312         }
313         List<X509Certificate> certs = lineage.getCertificatesInLineage();
314         assertEquals(
315                 "The number of elements in the certificate list from the lineage does not equal "
316                         + "the expected number",
317                 expectedCertSet.size(), certs.size());
318         for (X509Certificate cert : certs) {
319             // remove the certificate from the Set to ensure duplicate certs were not returned.
320             assertTrue("An unexpected certificate, " + cert.getSubjectDN() + ", is in the lineage",
321                     expectedCertSet.remove(cert));
322         }
323     }
324 
325     @Test
testSublineageContainsExpectedSigners()326     public void testSublineageContainsExpectedSigners() throws Exception {
327         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
328                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
329         lineage = updateLineageWithSignerFromResources(lineage,
330                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
331         List<SignerConfig> subList = mSigners.subList(0, 2);
332         X509Certificate cert = subList.get(1).getCertificate();
333         SigningCertificateLineage subLineage = lineage.getSubLineage(cert);
334         assertLineageContainsExpectedSigners(subLineage, subList);
335     }
336 
337     @Test
testConsolidatedLineageContainsExpectedSigners()338     public void testConsolidatedLineageContainsExpectedSigners() throws Exception {
339         SigningCertificateLineage lineage = createLineageWithSignersFromResources(
340                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME, SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
341         SigningCertificateLineage updatedLineage = updateLineageWithSignerFromResources(lineage,
342                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
343         List<SigningCertificateLineage> lineages = Arrays.asList(lineage, updatedLineage);
344         SigningCertificateLineage consolidatedLineage =
345                 SigningCertificateLineage.consolidateLineages(lineages);
346         assertLineageContainsExpectedSigners(consolidatedLineage, mSigners);
347     }
348 
349     @Test(expected = IllegalArgumentException.class)
testConsolidatedLineageWithDisjointLineagesFail()350     public void testConsolidatedLineageWithDisjointLineagesFail() throws Exception {
351         List<SigningCertificateLineage> lineages = new ArrayList<>();
352         lineages.add(createLineageWithSignersFromResources(FIRST_RSA_1024_SIGNER_RESOURCE_NAME,
353                 SECOND_RSA_1024_SIGNER_RESOURCE_NAME));
354         lineages.add(createLineageWithSignersFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
355                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME));
356         SigningCertificateLineage.consolidateLineages(lineages);
357     }
358 
359     @Test
testLineageFromAPKContainsExpectedSigners()360     public void testLineageFromAPKContainsExpectedSigners() throws Exception {
361         SignerConfig firstSigner = getSignerConfigFromResources(
362                 FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
363         SignerConfig secondSigner = getSignerConfigFromResources(
364                 SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
365         SignerConfig thirdSigner = getSignerConfigFromResources(
366                 THIRD_RSA_2048_SIGNER_RESOURCE_NAME);
367         List<SignerConfig> expectedSigners = Arrays.asList(firstSigner, secondSigner, thirdSigner);
368         DataSource apkDataSource = Resources.toDataSource(getClass(),
369                 "v1v2v3-with-rsa-2048-lineage-3-signers.apk");
370         SigningCertificateLineage lineageFromApk = SigningCertificateLineage.readFromApkDataSource(
371                 apkDataSource);
372         assertLineageContainsExpectedSigners(lineageFromApk, expectedSigners);
373     }
374 
375     @Test(expected = ApkFormatException.class)
testLineageFromAPKWithInvalidZipCDSizeFails()376     public void testLineageFromAPKWithInvalidZipCDSizeFails() throws Exception {
377         // This test verifies that attempting to read the lineage from an APK where the zip
378         // sections cannot be parsed fails. This APK is based off the
379         // v1v2v3-with-rsa-2048-lineage-3-signers.apk with a modified CD size in the EoCD.
380         DataSource apkDataSource = Resources.toDataSource(getClass(),
381                 "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk");
382         SigningCertificateLineage.readFromApkDataSource(apkDataSource);
383     }
384 
385     @Test
testLineageFromAPKWithNoLineageFails()386     public void testLineageFromAPKWithNoLineageFails() throws Exception {
387         // This test verifies that attempting to read the lineage from an APK without a lineage
388         // fails.
389         // This is a valid APK that has only been signed with the V1 and V2 signature schemes;
390         // since the lineage is an attribute in the V3 signature block this test should fail.
391         DataSource apkDataSource = Resources.toDataSource(getClass(),
392                 "golden-aligned-v1v2-out.apk");
393         try {
394             SigningCertificateLineage.readFromApkDataSource(apkDataSource);
395             fail("A failure should have been reported due to the APK not containing a V3 signing "
396                     + "block");
397         } catch (IllegalArgumentException expected) {}
398 
399         // This is a valid APK signed with the V1, V2, and V3 signature schemes, but there is no
400         // lineage in the V3 signature block.
401         apkDataSource = Resources.toDataSource(getClass(), "golden-aligned-v1v2v3-out.apk");
402         try {
403             SigningCertificateLineage.readFromApkDataSource(apkDataSource);
404             fail("A failure should have been reported due to the APK containing a V3 signing "
405                     + "block without the lineage attribute");
406         } catch (IllegalArgumentException expected) {}
407 
408         // This APK is based off the v1v2v3-with-rsa-2048-lineage-3-signers.apk with a bit flip
409         // in the lineage attribute ID in the V3 signature block.
410         apkDataSource = Resources.toDataSource(getClass(),
411                 "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-lineage-attr.apk");
412         try {
413             SigningCertificateLineage.readFromApkDataSource(apkDataSource);
414             fail("A failure should have been reported due to the APK containing a V3 signing "
415                     + "block with a modified lineage attribute ID");
416         } catch (IllegalArgumentException expected) {}
417     }
418 
419     /**
420      * Builds a new {@code SigningCertificateLinage.SignerCapabilities} object using the values in
421      * the provided {@code List}. The {@code List} should contain {@code boolean} values to be
422      * passed to the following methods in the
423      * {@code SigningCertificateLineage.SignerCapabilities.Builder} (if a value is not provided the
424      * noted default is used):
425      *
426      *  {@code SigningCertificateLineage.SignerCapabilities.Builder.setInstalledData} [{@code true}]
427      *  {@code SigningCertificateLineage.SignerCapabilities.Builder.setSharedUid} [{@code true}]
428      *  {@code SigningCertificateLineage.SignerCapabilities.Builder.setPermission} [{@code true}]
429      *  {@code SigningCertificateLineage.SignerCapabilities.Builder.setRollback} [{@code false}]
430      *  {@code SigningCertificateLineage.SignerCapabilities.Builder.setAuth} [{@code true}]
431      *
432      * This method should not be used when testing caller configured capabilities since the setXX
433      * method for each capability is called.
434      */
buildSignerCapabilities(List<Boolean> capabilityValues)435     private SignerCapabilities buildSignerCapabilities(List<Boolean> capabilityValues) {
436         return new SignerCapabilities.Builder()
437                 .setInstalledData(capabilityValues.size() > 0 ? capabilityValues.get(0) : true)
438                 .setSharedUid(capabilityValues.size() > 1 ? capabilityValues.get(1) : true)
439                 .setPermission(capabilityValues.size() > 2 ? capabilityValues.get(2) : true)
440                 .setRollback(capabilityValues.size() > 3 ? capabilityValues.get(3) : false)
441                 .setAuth(capabilityValues.size() > 4 ? capabilityValues.get(4) : true)
442                 .build();
443     }
444 
445     /**
446      * Verifies the specified {@code SigningCertificateLinage.SignerCapabilities} contains the
447      * expected values from the provided {@code List}. The {@code List} should contain
448      * {@code boolean} values to be verified against the
449      * {@code SigningCertificateLinage.SignerCapabilities} methods in the following order:
450      *
451      *  {@mcode SigningCertificateLineage.SignerCapabilities.hasInstalledData}
452      *  {@mcode SigningCertificateLineage.SignerCapabilities.hasSharedUid}
453      *  {@mcode SigningCertificateLineage.SignerCapabilities.hasPermission}
454      *  {@mcode SigningCertificateLineage.SignerCapabilities.hasRollback}
455      *  {@mcode SigningCertificateLineage.SignerCapabilities.hasAuth}
456      */
assertExpectedCapabilityValues(SignerCapabilities capabilities, List<Boolean> expectedCapabilityValues)457     private void assertExpectedCapabilityValues(SignerCapabilities capabilities,
458             List<Boolean> expectedCapabilityValues) {
459         assertTrue("The expectedCapabilityValues do not contain the expected number of elements",
460                 expectedCapabilityValues.size() >= 5);
461         assertEquals(
462                 "The installed data capability is not set to the expected value",
463                 expectedCapabilityValues.get(0), capabilities.hasInstalledData());
464         assertEquals(
465                 "The shared UID capability is not set to the expected value",
466                 expectedCapabilityValues.get(1), capabilities.hasSharedUid());
467         assertEquals(
468                 "The permission capability is not set to the expected value",
469                 expectedCapabilityValues.get(2), capabilities.hasPermission());
470         assertEquals(
471                 "The rollback capability is not set to the expected value",
472                 expectedCapabilityValues.get(3), capabilities.hasRollback());
473         assertEquals(
474                 "The auth capability is not set to the expected value",
475                 expectedCapabilityValues.get(4), capabilities.hasAuth());
476     }
477 
478     /**
479      * Creates a new {@code SigningCertificateLineage} with the specified signers from the
480      * resources. {@code mSigners} will be updated with the
481      * {@code SigningCertificateLineage.SignerConfig} for each signer added to the lineage.
482      */
createLineageWithSignersFromResources( String oldSignerResourceName, String newSignerResourceName)483     private SigningCertificateLineage createLineageWithSignersFromResources(
484             String oldSignerResourceName, String newSignerResourceName) throws Exception {
485         SignerConfig oldSignerConfig = Resources.toLineageSignerConfig(getClass(),
486                 oldSignerResourceName);
487         mSigners.add(oldSignerConfig);
488         SignerConfig newSignerConfig = Resources.toLineageSignerConfig(getClass(),
489                 newSignerResourceName);
490         mSigners.add(newSignerConfig);
491         return new SigningCertificateLineage.Builder(oldSignerConfig, newSignerConfig).build();
492     }
493 
494     /**
495      * Updates the specified {@code SigningCertificateLineage} with the signer from the resources.
496      * Requires that the {@code mSigners} list contains the previous signers in the lineage since
497      * the most recent signer must be specified when adding a new signer to the lineage.
498      */
updateLineageWithSignerFromResources( SigningCertificateLineage lineage, String newSignerResourceName)499     private SigningCertificateLineage updateLineageWithSignerFromResources(
500             SigningCertificateLineage lineage, String newSignerResourceName) throws Exception {
501         // To add a new Signer to an existing lineage the config of the last signer must be
502         // specified. If this class was used to create the lineage then the last signer should
503         // be in the mSigners list.
504         assertTrue("The mSigners list did not contain the expected signers to update the lineage",
505                 mSigners.size() >= 2);
506         SignerConfig oldSignerConfig = mSigners.get(mSigners.size() - 1);
507         SignerConfig newSignerConfig = Resources.toLineageSignerConfig(getClass(),
508                 newSignerResourceName);
509         mSigners.add(newSignerConfig);
510         return lineage.spawnDescendant(oldSignerConfig, newSignerConfig);
511     }
512 
assertLineageContainsExpectedSigners(SigningCertificateLineage lineage, List<SignerConfig> signers)513     private void assertLineageContainsExpectedSigners(SigningCertificateLineage lineage,
514             List<SignerConfig> signers) {
515         assertEquals("The lineage does not contain the expected number of signers",
516                 signers.size(), lineage.size());
517         for (SignerConfig signer : signers) {
518             assertTrue("The signer " + signer.getCertificate().getSubjectDN()
519                     + " is expected to be in the lineage", lineage.isSignerInLineage(signer));
520         }
521     }
522 
getSignerConfigFromResources( String resourcePrefix)523     private static SignerConfig getSignerConfigFromResources(
524             String resourcePrefix) throws Exception {
525         PrivateKey privateKey =
526                 Resources.toPrivateKey(SigningCertificateLineageTest.class,
527                         resourcePrefix + ".pk8");
528         X509Certificate cert = Resources.toCertificate(SigningCertificateLineageTest.class,
529                 resourcePrefix + ".x509.pem");
530         return new SignerConfig.Builder(privateKey, cert).build();
531     }
532 
getApkSignerEngineSignerConfigFromResources( String resourcePrefix)533     private static DefaultApkSignerEngine.SignerConfig getApkSignerEngineSignerConfigFromResources(
534             String resourcePrefix) throws Exception {
535         PrivateKey privateKey =
536                 Resources.toPrivateKey(SigningCertificateLineageTest.class,
537                         resourcePrefix + ".pk8");
538         X509Certificate cert = Resources.toCertificate(SigningCertificateLineageTest.class,
539                 resourcePrefix + ".x509.pem");
540         return new DefaultApkSignerEngine.SignerConfig.Builder(resourcePrefix, privateKey,
541                 Collections.singletonList(cert)).build();
542     }
543 }
544