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