blob: 1bbef8e9cfffcb82342114690f355fd9ee88c38b [file] [log] [blame]
Alex Klyubine4157182016-01-05 13:27:05 -08001/*
2 * Copyright (C) 2016 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
17package android.util.apk;
18
Victor Hsieh07bc80c2018-01-11 16:15:47 -080019import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
Daniel Cashman67096e02017-12-28 12:46:33 -080020import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
21import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
22import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
23import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256;
24import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512;
25import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA256;
26import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_RSA_PSS_WITH_SHA512;
Victor Hsieh4acad4c2018-01-04 13:36:15 -080027import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_DSA_WITH_SHA256;
28import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_ECDSA_WITH_SHA256;
29import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256;
Daniel Cashman67096e02017-12-28 12:46:33 -080030import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
31import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
32import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice;
33import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm;
34import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
35import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
36import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
37
Alex Klyubin0722ffc2016-03-18 16:09:06 -070038import android.util.ArrayMap;
Alex Klyubine4157182016-01-05 13:27:05 -080039import android.util.Pair;
40
41import java.io.ByteArrayInputStream;
42import java.io.IOException;
43import java.io.RandomAccessFile;
Alex Klyubine4157182016-01-05 13:27:05 -080044import java.nio.BufferUnderflowException;
45import java.nio.ByteBuffer;
Victor Hsieh07bc80c2018-01-11 16:15:47 -080046import java.security.DigestException;
Alex Klyubine4157182016-01-05 13:27:05 -080047import java.security.InvalidAlgorithmParameterException;
48import java.security.InvalidKeyException;
49import java.security.KeyFactory;
50import java.security.MessageDigest;
51import java.security.NoSuchAlgorithmException;
Alex Klyubine4157182016-01-05 13:27:05 -080052import java.security.PublicKey;
53import java.security.Signature;
54import java.security.SignatureException;
Alex Klyubine4157182016-01-05 13:27:05 -080055import java.security.cert.CertificateException;
Alex Klyubine4157182016-01-05 13:27:05 -080056import java.security.cert.CertificateFactory;
Alex Klyubine4157182016-01-05 13:27:05 -080057import java.security.cert.X509Certificate;
58import java.security.spec.AlgorithmParameterSpec;
59import java.security.spec.InvalidKeySpecException;
Alex Klyubine4157182016-01-05 13:27:05 -080060import java.security.spec.X509EncodedKeySpec;
61import java.util.ArrayList;
62import java.util.Arrays;
Alex Klyubine4157182016-01-05 13:27:05 -080063import java.util.List;
64import java.util.Map;
Alex Klyubine4157182016-01-05 13:27:05 -080065
66/**
67 * APK Signature Scheme v2 verifier.
68 *
Daniel Cashman67096e02017-12-28 12:46:33 -080069 * <p>APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single
70 * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and
71 * uncompressed contents of ZIP entries.
72 *
73 * @see <a href="https://source.android.com/security/apksigning/v2.html">APK Signature Scheme v2</a>
74 *
Alex Klyubine4157182016-01-05 13:27:05 -080075 * @hide for internal use only.
76 */
77public class ApkSignatureSchemeV2Verifier {
78
79 /**
Daniel Cashman67096e02017-12-28 12:46:33 -080080 * ID of this signature scheme as used in X-Android-APK-Signed header used in JAR signing.
Alex Klyubine4157182016-01-05 13:27:05 -080081 */
Alex Klyubin3a0095f2016-02-16 12:37:17 -080082 public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2;
Alex Klyubine4157182016-01-05 13:27:05 -080083
Daniel Cashman67096e02017-12-28 12:46:33 -080084 private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
85
Alex Klyubine4157182016-01-05 13:27:05 -080086 /**
Alex Klyubin0722ffc2016-03-18 16:09:06 -070087 * Returns {@code true} if the provided APK contains an APK Signature Scheme V2 signature.
88 *
89 * <p><b>NOTE: This method does not verify the signature.</b>
Todd Kennedy66c55532016-02-26 16:22:11 -080090 */
91 public static boolean hasSignature(String apkFile) throws IOException {
92 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
Alex Klyubin0722ffc2016-03-18 16:09:06 -070093 findSignature(apk);
Todd Kennedy66c55532016-02-26 16:22:11 -080094 return true;
95 } catch (SignatureNotFoundException e) {
Alex Klyubin0722ffc2016-03-18 16:09:06 -070096 return false;
Todd Kennedy66c55532016-02-26 16:22:11 -080097 }
Todd Kennedy66c55532016-02-26 16:22:11 -080098 }
99
100 /**
Alex Klyubine4157182016-01-05 13:27:05 -0800101 * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
102 * associated with each signer.
103 *
104 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
105 * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
106 * @throws IOException if an I/O error occurs while reading the APK file.
107 */
108 public static X509Certificate[][] verify(String apkFile)
109 throws SignatureNotFoundException, SecurityException, IOException {
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800110 VerifiedSigner vSigner = verify(apkFile, true);
111 return vSigner.certs;
Alex Klyubine4157182016-01-05 13:27:05 -0800112 }
113
114 /**
Dan Cashman636ea5e2017-12-18 10:38:20 -0800115 * Returns the certificates associated with each signer for the given APK without verification.
116 * This method is dangerous and should not be used, unless the caller is absolutely certain the
Daniel Cashman67096e02017-12-28 12:46:33 -0800117 * APK is trusted. Specifically, verification is only done for the APK Signature Scheme v2
Dan Cashman636ea5e2017-12-18 10:38:20 -0800118 * Block while gathering signer information. The APK contents are not verified.
119 *
120 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
121 * @throws IOException if an I/O error occurs while reading the APK file.
122 */
123 public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile)
124 throws SignatureNotFoundException, SecurityException, IOException {
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800125 VerifiedSigner vSigner = verify(apkFile, false);
126 return vSigner.certs;
Dan Cashman636ea5e2017-12-18 10:38:20 -0800127 }
128
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800129 private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
Dan Cashman636ea5e2017-12-18 10:38:20 -0800130 throws SignatureNotFoundException, SecurityException, IOException {
131 try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
132 return verify(apk, verifyIntegrity);
133 }
134 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800135
Dan Cashman636ea5e2017-12-18 10:38:20 -0800136 /**
Alex Klyubine4157182016-01-05 13:27:05 -0800137 * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
138 * associated with each signer.
139 *
140 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700141 * @throws SecurityException if an APK Signature Scheme v2 signature of this APK does not
142 * verify.
Alex Klyubine4157182016-01-05 13:27:05 -0800143 * @throws IOException if an I/O error occurs while reading the APK file.
144 */
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800145 private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
Alex Klyubine4157182016-01-05 13:27:05 -0800146 throws SignatureNotFoundException, SecurityException, IOException {
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700147 SignatureInfo signatureInfo = findSignature(apk);
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800148 return verify(apk, signatureInfo, verifyIntegrity);
Alex Klyubine4157182016-01-05 13:27:05 -0800149 }
150
151 /**
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700152 * Returns the APK Signature Scheme v2 block contained in the provided APK file and the
153 * additional information relevant for verifying the block against the file.
Alex Klyubine4157182016-01-05 13:27:05 -0800154 *
155 * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700156 * @throws IOException if an I/O error occurs while reading the APK file.
Alex Klyubine4157182016-01-05 13:27:05 -0800157 */
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700158 private static SignatureInfo findSignature(RandomAccessFile apk)
159 throws IOException, SignatureNotFoundException {
Daniel Cashman67096e02017-12-28 12:46:33 -0800160 return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
Alex Klyubine4157182016-01-05 13:27:05 -0800161 }
162
163 /**
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700164 * Verifies the contents of the provided APK file against the provided APK Signature Scheme v2
165 * Block.
166 *
167 * @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
168 * against the APK file.
Alex Klyubine4157182016-01-05 13:27:05 -0800169 */
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800170 private static VerifiedSigner verify(
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800171 RandomAccessFile apk,
Dan Cashman636ea5e2017-12-18 10:38:20 -0800172 SignatureInfo signatureInfo,
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800173 boolean doVerifyIntegrity) throws SecurityException, IOException {
Alex Klyubine4157182016-01-05 13:27:05 -0800174 int signerCount = 0;
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700175 Map<Integer, byte[]> contentDigests = new ArrayMap<>();
Alex Klyubine4157182016-01-05 13:27:05 -0800176 List<X509Certificate[]> signerCerts = new ArrayList<>();
177 CertificateFactory certFactory;
178 try {
179 certFactory = CertificateFactory.getInstance("X.509");
180 } catch (CertificateException e) {
181 throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
182 }
183 ByteBuffer signers;
184 try {
Alex Klyubin0722ffc2016-03-18 16:09:06 -0700185 signers = getLengthPrefixedSlice(signatureInfo.signatureBlock);
Alex Klyubine4157182016-01-05 13:27:05 -0800186 } catch (IOException e) {
187 throw new SecurityException("Failed to read list of signers", e);
188 }
189 while (signers.hasRemaining()) {
190 signerCount++;
191 try {
192 ByteBuffer signer = getLengthPrefixedSlice(signers);
193 X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory);
194 signerCerts.add(certs);
195 } catch (IOException | BufferUnderflowException | SecurityException e) {
196 throw new SecurityException(
197 "Failed to parse/verify signer #" + signerCount + " block",
198 e);
199 }
200 }
201
202 if (signerCount < 1) {
203 throw new SecurityException("No signers found");
204 }
205
206 if (contentDigests.isEmpty()) {
207 throw new SecurityException("No content digests found");
208 }
209
Dan Cashman636ea5e2017-12-18 10:38:20 -0800210 if (doVerifyIntegrity) {
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800211 ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
Dan Cashman636ea5e2017-12-18 10:38:20 -0800212 }
Alex Klyubine4157182016-01-05 13:27:05 -0800213
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800214 byte[] verityRootHash = null;
215 if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
Victor Hsieh4ba1eea2018-03-02 14:26:19 -0800216 byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
217 verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
218 verityDigest, apk.length(), signatureInfo);
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800219 }
220
221 return new VerifiedSigner(
222 signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
223 verityRootHash);
Alex Klyubine4157182016-01-05 13:27:05 -0800224 }
225
226 private static X509Certificate[] verifySigner(
227 ByteBuffer signerBlock,
228 Map<Integer, byte[]> contentDigests,
229 CertificateFactory certFactory) throws SecurityException, IOException {
230 ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
231 ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
232 byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
233
234 int signatureCount = 0;
235 int bestSigAlgorithm = -1;
236 byte[] bestSigAlgorithmSignatureBytes = null;
237 List<Integer> signaturesSigAlgorithms = new ArrayList<>();
238 while (signatures.hasRemaining()) {
239 signatureCount++;
240 try {
241 ByteBuffer signature = getLengthPrefixedSlice(signatures);
242 if (signature.remaining() < 8) {
243 throw new SecurityException("Signature record too short");
244 }
245 int sigAlgorithm = signature.getInt();
246 signaturesSigAlgorithms.add(sigAlgorithm);
247 if (!isSupportedSignatureAlgorithm(sigAlgorithm)) {
248 continue;
249 }
250 if ((bestSigAlgorithm == -1)
251 || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
252 bestSigAlgorithm = sigAlgorithm;
253 bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature);
254 }
255 } catch (IOException | BufferUnderflowException e) {
256 throw new SecurityException(
257 "Failed to parse signature record #" + signatureCount,
258 e);
259 }
260 }
261 if (bestSigAlgorithm == -1) {
262 if (signatureCount == 0) {
263 throw new SecurityException("No signatures found");
264 } else {
265 throw new SecurityException("No supported signatures found");
266 }
267 }
268
269 String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm);
270 Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
271 getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm);
272 String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
273 AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
274 boolean sigVerified;
275 try {
276 PublicKey publicKey =
277 KeyFactory.getInstance(keyAlgorithm)
278 .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
279 Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
280 sig.initVerify(publicKey);
281 if (jcaSignatureAlgorithmParams != null) {
282 sig.setParameter(jcaSignatureAlgorithmParams);
283 }
284 sig.update(signedData);
285 sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
286 } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
287 | InvalidAlgorithmParameterException | SignatureException e) {
288 throw new SecurityException(
289 "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
290 }
291 if (!sigVerified) {
292 throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
293 }
294
295 // Signature over signedData has verified.
296
297 byte[] contentDigest = null;
298 signedData.clear();
299 ByteBuffer digests = getLengthPrefixedSlice(signedData);
300 List<Integer> digestsSigAlgorithms = new ArrayList<>();
301 int digestCount = 0;
302 while (digests.hasRemaining()) {
303 digestCount++;
304 try {
305 ByteBuffer digest = getLengthPrefixedSlice(digests);
306 if (digest.remaining() < 8) {
307 throw new IOException("Record too short");
308 }
309 int sigAlgorithm = digest.getInt();
310 digestsSigAlgorithms.add(sigAlgorithm);
311 if (sigAlgorithm == bestSigAlgorithm) {
312 contentDigest = readLengthPrefixedByteArray(digest);
313 }
314 } catch (IOException | BufferUnderflowException e) {
315 throw new IOException("Failed to parse digest record #" + digestCount, e);
316 }
317 }
318
319 if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) {
320 throw new SecurityException(
321 "Signature algorithms don't match between digests and signatures records");
322 }
323 int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm);
324 byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest);
325 if ((previousSignerDigest != null)
326 && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) {
327 throw new SecurityException(
328 getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
329 + " contents digest does not match the digest specified by a preceding signer");
330 }
331
332 ByteBuffer certificates = getLengthPrefixedSlice(signedData);
333 List<X509Certificate> certs = new ArrayList<>();
334 int certificateCount = 0;
335 while (certificates.hasRemaining()) {
336 certificateCount++;
337 byte[] encodedCert = readLengthPrefixedByteArray(certificates);
338 X509Certificate certificate;
339 try {
340 certificate = (X509Certificate)
341 certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
342 } catch (CertificateException e) {
343 throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
344 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800345 certificate = new VerbatimX509Certificate(
346 certificate, encodedCert);
Alex Klyubine4157182016-01-05 13:27:05 -0800347 certs.add(certificate);
348 }
349
350 if (certs.isEmpty()) {
351 throw new SecurityException("No certificates listed");
352 }
353 X509Certificate mainCertificate = certs.get(0);
354 byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
355 if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
356 throw new SecurityException(
357 "Public key mismatch between certificate and signature record");
358 }
359
Daniel Cashman67096e02017-12-28 12:46:33 -0800360 ByteBuffer additionalAttrs = getLengthPrefixedSlice(signedData);
361 verifyAdditionalAttributes(additionalAttrs);
362
Alex Klyubine4157182016-01-05 13:27:05 -0800363 return certs.toArray(new X509Certificate[certs.size()]);
364 }
365
Daniel Cashman67096e02017-12-28 12:46:33 -0800366 // Attribute to check whether a newer APK Signature Scheme signature was stripped
367 private static final int STRIPPING_PROTECTION_ATTR_ID = 0xbeeff00d;
Alex Klyubine4157182016-01-05 13:27:05 -0800368
Daniel Cashman67096e02017-12-28 12:46:33 -0800369 private static void verifyAdditionalAttributes(ByteBuffer attrs)
370 throws SecurityException, IOException {
371 while (attrs.hasRemaining()) {
372 ByteBuffer attr = getLengthPrefixedSlice(attrs);
373 if (attr.remaining() < 4) {
374 throw new IOException("Remaining buffer too short to contain additional attribute "
375 + "ID. Remaining: " + attr.remaining());
Alex Klyubine4157182016-01-05 13:27:05 -0800376 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800377 int id = attr.getInt();
378 switch (id) {
379 case STRIPPING_PROTECTION_ATTR_ID:
380 if (attr.remaining() < 4) {
381 throw new IOException("V2 Signature Scheme Stripping Protection Attribute "
382 + " value too small. Expected 4 bytes, but found "
383 + attr.remaining());
Alex Klyubine4157182016-01-05 13:27:05 -0800384 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800385 int vers = attr.getInt();
386 if (vers == ApkSignatureSchemeV3Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
387 throw new SecurityException("V2 signature indicates APK is signed using APK"
388 + " Signature Scheme v3, but none was found. Signature stripped?");
389 }
390 break;
391 default:
392 // not the droid we're looking for, move along, move along.
393 break;
Alex Klyubine4157182016-01-05 13:27:05 -0800394 }
395 }
Daniel Cashman67096e02017-12-28 12:46:33 -0800396 return;
Alex Klyubine4157182016-01-05 13:27:05 -0800397 }
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800398
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800399 static byte[] getVerityRootHash(String apkPath)
400 throws IOException, SignatureNotFoundException, SecurityException {
401 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
402 SignatureInfo signatureInfo = findSignature(apk);
403 VerifiedSigner vSigner = verify(apk, false);
404 return vSigner.verityRootHash;
405 }
406 }
407
408 static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
409 throws IOException, SignatureNotFoundException, SecurityException, DigestException,
410 NoSuchAlgorithmException {
411 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
412 SignatureInfo signatureInfo = findSignature(apk);
Victor Hsieh27300922018-09-28 09:31:44 -0700413 return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800414 }
415 }
416
Victor Hsieh25195132018-09-06 16:32:06 -0700417 static byte[] generateApkVerityRootHash(String apkPath)
Victor Hsieh5f761242018-01-20 10:30:12 -0800418 throws IOException, SignatureNotFoundException, DigestException,
419 NoSuchAlgorithmException {
420 try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
421 SignatureInfo signatureInfo = findSignature(apk);
422 VerifiedSigner vSigner = verify(apk, false);
423 if (vSigner.verityRootHash == null) {
424 return null;
425 }
Victor Hsieh27300922018-09-28 09:31:44 -0700426 return VerityBuilder.generateApkVerityRootHash(
Victor Hsieh5f761242018-01-20 10:30:12 -0800427 apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
428 }
429 }
430
Alex Klyubine4157182016-01-05 13:27:05 -0800431 private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
432 switch (sigAlgorithm) {
433 case SIGNATURE_RSA_PSS_WITH_SHA256:
434 case SIGNATURE_RSA_PSS_WITH_SHA512:
435 case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
436 case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
437 case SIGNATURE_ECDSA_WITH_SHA256:
438 case SIGNATURE_ECDSA_WITH_SHA512:
439 case SIGNATURE_DSA_WITH_SHA256:
Victor Hsieh4acad4c2018-01-04 13:36:15 -0800440 case SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256:
441 case SIGNATURE_VERITY_ECDSA_WITH_SHA256:
442 case SIGNATURE_VERITY_DSA_WITH_SHA256:
Alex Klyubine4157182016-01-05 13:27:05 -0800443 return true;
444 default:
445 return false;
446 }
447 }
Victor Hsieh07bc80c2018-01-11 16:15:47 -0800448
449 /**
450 * Verified APK Signature Scheme v2 signer.
451 *
452 * @hide for internal use only.
453 */
454 public static class VerifiedSigner {
455 public final X509Certificate[][] certs;
456 public final byte[] verityRootHash;
457
458 public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
459 this.certs = certs;
460 this.verityRootHash = verityRootHash;
461 }
462
463 }
Alex Klyubine4157182016-01-05 13:27:05 -0800464}