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