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 android.util.apk; 18 19 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; 20 import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; 21 import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; 22 import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; 23 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm; 24 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; 25 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; 26 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; 27 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; 28 import static android.util.apk.ApkSigningBlockUtils.verifyProofOfRotationStruct; 29 30 import android.os.Build; 31 import android.util.ArrayMap; 32 import android.util.Pair; 33 34 import java.io.ByteArrayInputStream; 35 import java.io.IOException; 36 import java.io.RandomAccessFile; 37 import java.nio.BufferUnderflowException; 38 import java.nio.ByteBuffer; 39 import java.security.DigestException; 40 import java.security.InvalidAlgorithmParameterException; 41 import java.security.InvalidKeyException; 42 import java.security.KeyFactory; 43 import java.security.MessageDigest; 44 import java.security.NoSuchAlgorithmException; 45 import java.security.PublicKey; 46 import java.security.Signature; 47 import java.security.SignatureException; 48 import java.security.cert.CertificateEncodingException; 49 import java.security.cert.CertificateException; 50 import java.security.cert.CertificateFactory; 51 import java.security.cert.X509Certificate; 52 import java.security.spec.AlgorithmParameterSpec; 53 import java.security.spec.InvalidKeySpecException; 54 import java.security.spec.X509EncodedKeySpec; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.OptionalInt; 60 61 /** 62 * APK Signature Scheme v3 verifier. 63 * 64 * @hide for internal use only. 65 */ 66 public class ApkSignatureSchemeV3Verifier { 67 68 /** 69 * ID of this signature scheme as used in X-Android-APK-Signed header used in JAR signing. 70 */ 71 public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 3; 72 73 static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; 74 static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = 0x1b93ad61; 75 76 /** 77 * Returns {@code true} if the provided APK contains an APK Signature Scheme V3 signature. 78 * 79 * <p><b>NOTE: This method does not verify the signature.</b> 80 */ hasSignature(String apkFile)81 public static boolean hasSignature(String apkFile) throws IOException { 82 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { 83 findSignature(apk); 84 return true; 85 } catch (SignatureNotFoundException e) { 86 return false; 87 } 88 } 89 90 /** 91 * Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates 92 * associated with each signer. 93 * 94 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. 95 * @throws SecurityException if the APK Signature Scheme v3 signature of this APK does 96 * not 97 * verify. 98 * @throws IOException if an I/O error occurs while reading the APK file. 99 */ verify(String apkFile)100 public static VerifiedSigner verify(String apkFile) 101 throws SignatureNotFoundException, SecurityException, IOException { 102 return verify(apkFile, true); 103 } 104 105 /** 106 * Returns the certificates associated with each signer for the given APK without verification. 107 * This method is dangerous and should not be used, unless the caller is absolutely certain the 108 * APK is trusted. Specifically, verification is only done for the APK Signature Scheme v3 109 * Block while gathering signer information. The APK contents are not verified. 110 * 111 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. 112 * @throws IOException if an I/O error occurs while reading the APK file. 113 */ unsafeGetCertsWithoutVerification(String apkFile)114 public static VerifiedSigner unsafeGetCertsWithoutVerification(String apkFile) 115 throws SignatureNotFoundException, SecurityException, IOException { 116 return verify(apkFile, false); 117 } 118 verify(String apkFile, boolean verifyIntegrity)119 private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) 120 throws SignatureNotFoundException, SecurityException, IOException { 121 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { 122 return verify(apk, verifyIntegrity); 123 } 124 } 125 126 /** 127 * Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates 128 * associated with each signer. 129 * 130 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. 131 * @throws SecurityException if an APK Signature Scheme v3 signature of this APK does 132 * not 133 * verify. 134 * @throws IOException if an I/O error occurs while reading the APK file. 135 */ verify(RandomAccessFile apk, boolean verifyIntegrity)136 private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity) 137 throws SignatureNotFoundException, SecurityException, IOException { 138 ApkSignatureSchemeV3Verifier verifier = new ApkSignatureSchemeV3Verifier(apk, 139 verifyIntegrity); 140 try { 141 SignatureInfo signatureInfo = findSignature(apk, APK_SIGNATURE_SCHEME_V31_BLOCK_ID); 142 return verifier.verify(signatureInfo, APK_SIGNATURE_SCHEME_V31_BLOCK_ID); 143 } catch (SignatureNotFoundException ignored) { 144 // This is expected if the APK is not using v3.1 to target rotation. 145 } catch (PlatformNotSupportedException ignored) { 146 // This is expected if the APK is targeting a platform version later than that of the 147 // device for rotation. 148 } 149 try { 150 SignatureInfo signatureInfo = findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID); 151 return verifier.verify(signatureInfo, APK_SIGNATURE_SCHEME_V3_BLOCK_ID); 152 } catch (PlatformNotSupportedException e) { 153 throw new SecurityException(e); 154 } 155 } 156 157 /** 158 * Returns the APK Signature Scheme v3 block contained in the provided APK file and the 159 * additional information relevant for verifying the block against the file. 160 * 161 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3. 162 * @throws IOException if an I/O error occurs while reading the APK file. 163 */ findSignature(RandomAccessFile apk)164 public static SignatureInfo findSignature(RandomAccessFile apk) 165 throws IOException, SignatureNotFoundException { 166 return findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID); 167 } 168 169 /* 170 * Returns the APK Signature Scheme v3 block in the provided {@code apk} file with the specified 171 * {@code blockId} and the additional information relevant for verifying the block against the 172 * file. 173 * 174 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3 175 * @throws IOException if an I/O error occurs while reading the APK file 176 **/ findSignature(RandomAccessFile apk, int blockId)177 private static SignatureInfo findSignature(RandomAccessFile apk, int blockId) 178 throws IOException, SignatureNotFoundException { 179 return ApkSigningBlockUtils.findSignature(apk, blockId); 180 } 181 182 private final RandomAccessFile mApk; 183 private final boolean mVerifyIntegrity; 184 private OptionalInt mOptionalRotationMinSdkVersion = OptionalInt.empty(); 185 private int mSignerMinSdkVersion; 186 private int mBlockId; 187 ApkSignatureSchemeV3Verifier(RandomAccessFile apk, boolean verifyIntegrity)188 private ApkSignatureSchemeV3Verifier(RandomAccessFile apk, boolean verifyIntegrity) { 189 mApk = apk; 190 mVerifyIntegrity = verifyIntegrity; 191 } 192 193 /** 194 * Verifies the contents of the provided APK file against the provided APK Signature Scheme v3 195 * Block. 196 * 197 * @param signatureInfo APK Signature Scheme v3 Block and information relevant for verifying it 198 * against the APK file. 199 */ verify(SignatureInfo signatureInfo, int blockId)200 private VerifiedSigner verify(SignatureInfo signatureInfo, int blockId) 201 throws SecurityException, IOException, PlatformNotSupportedException { 202 mBlockId = blockId; 203 int signerCount = 0; 204 Map<Integer, byte[]> contentDigests = new ArrayMap<>(); 205 Pair<X509Certificate[], ApkSigningBlockUtils.VerifiedProofOfRotation> result = null; 206 CertificateFactory certFactory; 207 try { 208 certFactory = CertificateFactory.getInstance("X.509"); 209 } catch (CertificateException e) { 210 throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); 211 } 212 ByteBuffer signers; 213 try { 214 signers = getLengthPrefixedSlice(signatureInfo.signatureBlock); 215 } catch (IOException e) { 216 throw new SecurityException("Failed to read list of signers", e); 217 } 218 while (signers.hasRemaining()) { 219 try { 220 ByteBuffer signer = getLengthPrefixedSlice(signers); 221 result = verifySigner(signer, contentDigests, certFactory); 222 signerCount++; 223 } catch (PlatformNotSupportedException e) { 224 // this signer is for a different platform, ignore it. 225 continue; 226 } catch (IOException | BufferUnderflowException | SecurityException e) { 227 throw new SecurityException( 228 "Failed to parse/verify signer #" + signerCount + " block", 229 e); 230 } 231 } 232 233 if (signerCount < 1 || result == null) { 234 // There must always be a valid signer targeting the device SDK version for a v3.0 235 // signature. 236 if (blockId == APK_SIGNATURE_SCHEME_V3_BLOCK_ID) { 237 throw new SecurityException("No signers found"); 238 } 239 throw new PlatformNotSupportedException( 240 "None of the signers support the current platform version"); 241 } 242 243 if (signerCount != 1) { 244 throw new SecurityException("APK Signature Scheme V3 only supports one signer: " 245 + "multiple signers found."); 246 } 247 248 if (contentDigests.isEmpty()) { 249 throw new SecurityException("No content digests found"); 250 } 251 252 if (mVerifyIntegrity) { 253 ApkSigningBlockUtils.verifyIntegrity(contentDigests, mApk, signatureInfo); 254 } 255 256 byte[] verityRootHash = null; 257 if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) { 258 byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256); 259 verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength( 260 verityDigest, mApk.getChannel().size(), signatureInfo); 261 } 262 263 return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests, 264 blockId); 265 } 266 267 private Pair<X509Certificate[], ApkSigningBlockUtils.VerifiedProofOfRotation> verifySigner( ByteBuffer signerBlock, Map<Integer, byte[]> contentDigests, CertificateFactory certFactory)268 verifySigner( 269 ByteBuffer signerBlock, 270 Map<Integer, byte[]> contentDigests, 271 CertificateFactory certFactory) 272 throws SecurityException, IOException, PlatformNotSupportedException { 273 ByteBuffer signedData = getLengthPrefixedSlice(signerBlock); 274 int minSdkVersion = signerBlock.getInt(); 275 int maxSdkVersion = signerBlock.getInt(); 276 277 if (Build.VERSION.SDK_INT < minSdkVersion || Build.VERSION.SDK_INT > maxSdkVersion) { 278 // if this is a v3.1 block then save the minimum SDK version for rotation for comparison 279 // against the v3.0 additional attribute. 280 if (mBlockId == APK_SIGNATURE_SCHEME_V31_BLOCK_ID) { 281 if (!mOptionalRotationMinSdkVersion.isPresent() 282 || mOptionalRotationMinSdkVersion.getAsInt() > minSdkVersion) { 283 mOptionalRotationMinSdkVersion = OptionalInt.of(minSdkVersion); 284 } 285 } 286 // this signature isn't meant to be used with this platform, skip it. 287 throw new PlatformNotSupportedException( 288 "Signer not supported by this platform " 289 + "version. This platform: " + Build.VERSION.SDK_INT 290 + ", signer minSdkVersion: " + minSdkVersion 291 + ", maxSdkVersion: " + maxSdkVersion); 292 } 293 294 ByteBuffer signatures = getLengthPrefixedSlice(signerBlock); 295 byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock); 296 297 int signatureCount = 0; 298 int bestSigAlgorithm = -1; 299 byte[] bestSigAlgorithmSignatureBytes = null; 300 List<Integer> signaturesSigAlgorithms = new ArrayList<>(); 301 while (signatures.hasRemaining()) { 302 signatureCount++; 303 try { 304 ByteBuffer signature = getLengthPrefixedSlice(signatures); 305 if (signature.remaining() < 8) { 306 throw new SecurityException("Signature record too short"); 307 } 308 int sigAlgorithm = signature.getInt(); 309 signaturesSigAlgorithms.add(sigAlgorithm); 310 if (!isSupportedSignatureAlgorithm(sigAlgorithm)) { 311 continue; 312 } 313 if ((bestSigAlgorithm == -1) 314 || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) { 315 bestSigAlgorithm = sigAlgorithm; 316 bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature); 317 } 318 } catch (IOException | BufferUnderflowException e) { 319 throw new SecurityException( 320 "Failed to parse signature record #" + signatureCount, 321 e); 322 } 323 } 324 if (bestSigAlgorithm == -1) { 325 if (signatureCount == 0) { 326 throw new SecurityException("No signatures found"); 327 } else { 328 throw new SecurityException("No supported signatures found"); 329 } 330 } 331 332 String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm); 333 Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams = 334 getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm); 335 String jcaSignatureAlgorithm = signatureAlgorithmParams.first; 336 AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second; 337 boolean sigVerified; 338 try { 339 PublicKey publicKey = 340 KeyFactory.getInstance(keyAlgorithm) 341 .generatePublic(new X509EncodedKeySpec(publicKeyBytes)); 342 Signature sig = Signature.getInstance(jcaSignatureAlgorithm); 343 sig.initVerify(publicKey); 344 if (jcaSignatureAlgorithmParams != null) { 345 sig.setParameter(jcaSignatureAlgorithmParams); 346 } 347 sig.update(signedData); 348 sigVerified = sig.verify(bestSigAlgorithmSignatureBytes); 349 } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException 350 | InvalidAlgorithmParameterException | SignatureException e) { 351 throw new SecurityException( 352 "Failed to verify " + jcaSignatureAlgorithm + " signature", e); 353 } 354 if (!sigVerified) { 355 throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify"); 356 } 357 358 // Signature over signedData has verified. 359 360 byte[] contentDigest = null; 361 signedData.clear(); 362 ByteBuffer digests = getLengthPrefixedSlice(signedData); 363 List<Integer> digestsSigAlgorithms = new ArrayList<>(); 364 int digestCount = 0; 365 while (digests.hasRemaining()) { 366 digestCount++; 367 try { 368 ByteBuffer digest = getLengthPrefixedSlice(digests); 369 if (digest.remaining() < 8) { 370 throw new IOException("Record too short"); 371 } 372 int sigAlgorithm = digest.getInt(); 373 digestsSigAlgorithms.add(sigAlgorithm); 374 if (sigAlgorithm == bestSigAlgorithm) { 375 contentDigest = readLengthPrefixedByteArray(digest); 376 } 377 } catch (IOException | BufferUnderflowException e) { 378 throw new IOException("Failed to parse digest record #" + digestCount, e); 379 } 380 } 381 382 if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) { 383 throw new SecurityException( 384 "Signature algorithms don't match between digests and signatures records"); 385 } 386 int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm); 387 byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest); 388 if ((previousSignerDigest != null) 389 && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) { 390 throw new SecurityException( 391 getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm) 392 + " contents digest does not match the digest specified by a " 393 + "preceding signer"); 394 } 395 396 ByteBuffer certificates = getLengthPrefixedSlice(signedData); 397 List<X509Certificate> certs = new ArrayList<>(); 398 int certificateCount = 0; 399 while (certificates.hasRemaining()) { 400 certificateCount++; 401 byte[] encodedCert = readLengthPrefixedByteArray(certificates); 402 X509Certificate certificate; 403 try { 404 certificate = (X509Certificate) 405 certFactory.generateCertificate(new ByteArrayInputStream(encodedCert)); 406 } catch (CertificateException e) { 407 throw new SecurityException("Failed to decode certificate #" + certificateCount, e); 408 } 409 certificate = new VerbatimX509Certificate(certificate, encodedCert); 410 certs.add(certificate); 411 } 412 413 if (certs.isEmpty()) { 414 throw new SecurityException("No certificates listed"); 415 } 416 X509Certificate mainCertificate = certs.get(0); 417 byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded(); 418 if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) { 419 throw new SecurityException( 420 "Public key mismatch between certificate and signature record"); 421 } 422 423 int signedMinSDK = signedData.getInt(); 424 if (signedMinSDK != minSdkVersion) { 425 throw new SecurityException( 426 "minSdkVersion mismatch between signed and unsigned in v3 signer block."); 427 } 428 mSignerMinSdkVersion = signedMinSDK; 429 430 int signedMaxSDK = signedData.getInt(); 431 if (signedMaxSDK != maxSdkVersion) { 432 throw new SecurityException( 433 "maxSdkVersion mismatch between signed and unsigned in v3 signer block."); 434 } 435 436 ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData); 437 return verifyAdditionalAttributes(additionalAttrs, certs, certFactory); 438 } 439 440 private static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; 441 private static final int ROTATION_MIN_SDK_VERSION_ATTR_ID = 0x559f8b02; 442 private static final int ROTATION_ON_DEV_RELEASE_ATTR_ID = 0xc2a6b3ba; 443 444 private Pair<X509Certificate[], ApkSigningBlockUtils.VerifiedProofOfRotation> verifyAdditionalAttributes(ByteBuffer attrs, List<X509Certificate> certs, CertificateFactory certFactory)445 verifyAdditionalAttributes(ByteBuffer attrs, List<X509Certificate> certs, 446 CertificateFactory certFactory) throws IOException, PlatformNotSupportedException { 447 X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]); 448 ApkSigningBlockUtils.VerifiedProofOfRotation por = null; 449 450 while (attrs.hasRemaining()) { 451 ByteBuffer attr = getLengthPrefixedSlice(attrs); 452 if (attr.remaining() < 4) { 453 throw new IOException("Remaining buffer too short to contain additional attribute " 454 + "ID. Remaining: " + attr.remaining()); 455 } 456 int id = attr.getInt(); 457 switch (id) { 458 case PROOF_OF_ROTATION_ATTR_ID: 459 if (por != null) { 460 throw new SecurityException("Encountered multiple Proof-of-rotation records" 461 + " when verifying APK Signature Scheme v3 signature"); 462 } 463 por = verifyProofOfRotationStruct(attr, certFactory); 464 // make sure that the last certificate in the Proof-of-rotation record matches 465 // the one used to sign this APK. 466 try { 467 if (por.certs.size() > 0 468 && !Arrays.equals(por.certs.get(por.certs.size() - 1).getEncoded(), 469 certChain[0].getEncoded())) { 470 throw new SecurityException("Terminal certificate in Proof-of-rotation" 471 + " record does not match APK signing certificate"); 472 } 473 } catch (CertificateEncodingException e) { 474 throw new SecurityException("Failed to encode certificate when comparing" 475 + " Proof-of-rotation record and signing certificate", e); 476 } 477 478 break; 479 case ROTATION_MIN_SDK_VERSION_ATTR_ID: 480 if (attr.remaining() < 4) { 481 throw new IOException( 482 "Remaining buffer too short to contain rotation minSdkVersion " 483 + "value. Remaining: " 484 + attr.remaining()); 485 } 486 int attrRotationMinSdkVersion = attr.getInt(); 487 if (!mOptionalRotationMinSdkVersion.isPresent()) { 488 throw new SecurityException( 489 "Expected a v3.1 signing block targeting SDK version " 490 + attrRotationMinSdkVersion 491 + ", but a v3.1 block was not found"); 492 } 493 int rotationMinSdkVersion = mOptionalRotationMinSdkVersion.getAsInt(); 494 if (rotationMinSdkVersion != attrRotationMinSdkVersion) { 495 throw new SecurityException( 496 "Expected a v3.1 signing block targeting SDK version " 497 + attrRotationMinSdkVersion 498 + ", but the v3.1 block was targeting " 499 + rotationMinSdkVersion); 500 } 501 break; 502 case ROTATION_ON_DEV_RELEASE_ATTR_ID: 503 // This attribute should only be used in a v3.1 signer as it allows the v3.1 504 // signature scheme to target a platform under development. 505 if (mBlockId == APK_SIGNATURE_SCHEME_V31_BLOCK_ID) { 506 // A platform under development uses the same SDK version as the most 507 // recently released platform; if this platform's SDK version is the same as 508 // the minimum of the signer, then only allow this signer to be used if this 509 // is not a "REL" platform. 510 if (Build.VERSION.SDK_INT == mSignerMinSdkVersion 511 && "REL".equals(Build.VERSION.CODENAME)) { 512 // Set the rotation-min-sdk-version to be verified against the stripping 513 // attribute in the v3.0 block. 514 mOptionalRotationMinSdkVersion = OptionalInt.of(mSignerMinSdkVersion); 515 throw new PlatformNotSupportedException( 516 "The device is running a release version of " 517 + mSignerMinSdkVersion 518 + ", but the signer is targeting a dev release"); 519 } 520 } 521 break; 522 default: 523 // not the droid we're looking for, move along, move along. 524 break; 525 } 526 } 527 return Pair.create(certChain, por); 528 } 529 getVerityRootHash(String apkPath)530 static byte[] getVerityRootHash(String apkPath) 531 throws IOException, SignatureNotFoundException, SecurityException { 532 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { 533 SignatureInfo signatureInfo = findSignature(apk); 534 VerifiedSigner vSigner = verify(apk, false); 535 return vSigner.verityRootHash; 536 } 537 } 538 generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)539 static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory) 540 throws IOException, SignatureNotFoundException, SecurityException, DigestException, 541 NoSuchAlgorithmException { 542 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { 543 SignatureInfo signatureInfo = findSignature(apk); 544 return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); 545 } 546 } 547 548 /** 549 * Verified APK Signature Scheme v3 signer, including the proof of rotation structure. 550 * 551 * @hide for internal use only. 552 */ 553 public static class VerifiedSigner { 554 public final X509Certificate[] certs; 555 public final ApkSigningBlockUtils.VerifiedProofOfRotation por; 556 557 public final byte[] verityRootHash; 558 // Algorithm -> digest map of signed digests in the signature. 559 // All these are verified if requested. 560 public final Map<Integer, byte[]> contentDigests; 561 562 // ID of the signature block used to verify. 563 public final int blockId; 564 VerifiedSigner(X509Certificate[] certs, ApkSigningBlockUtils.VerifiedProofOfRotation por, byte[] verityRootHash, Map<Integer, byte[]> contentDigests, int blockId)565 public VerifiedSigner(X509Certificate[] certs, 566 ApkSigningBlockUtils.VerifiedProofOfRotation por, 567 byte[] verityRootHash, Map<Integer, byte[]> contentDigests, 568 int blockId) { 569 this.certs = certs; 570 this.por = por; 571 this.verityRootHash = verityRootHash; 572 this.contentDigests = contentDigests; 573 this.blockId = blockId; 574 } 575 576 } 577 578 private static class PlatformNotSupportedException extends Exception { 579 PlatformNotSupportedException(String s)580 PlatformNotSupportedException(String s) { 581 super(s); 582 } 583 } 584 } 585