diff options
author | Alex Buynytskyy <alexbuy@google.com> | 2020-02-12 19:19:06 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-02-12 19:19:06 +0000 |
commit | 780d2bb75d5757bfccdab8ca67ab6ab75ac65e10 (patch) | |
tree | baae472a823f0a9ae510732eddbd1f7fee479628 | |
parent | 223c27ef69c3c41b60971142fc2b9479f8167bcc (diff) | |
parent | 036864b62c26a623b5ba5c4fed6d9c957da1d206 (diff) |
Merge "v4 signing schema parsing and verification."
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 5 | ||||
-rw-r--r-- | core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java | 53 | ||||
-rw-r--r-- | core/java/android/util/apk/ApkSignatureVerifier.java | 79 |
3 files changed, 134 insertions, 3 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ef3b0c82c1f0..637e64d3d2c2 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -61,7 +61,6 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.ApkParseUtils; import android.content.pm.parsing.PackageImpl; -import android.content.pm.parsing.PackageInfoUtils; import android.content.pm.parsing.ParsedPackage; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.split.DefaultSplitAssetLoader; @@ -5967,12 +5966,14 @@ public class PackageParser { @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN, SigningDetails.SignatureSchemeVersion.JAR, SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, - SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3}) + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4}) public @interface SignatureSchemeVersion { int UNKNOWN = 0; int JAR = 1; int SIGNING_BLOCK_V2 = 2; int SIGNING_BLOCK_V3 = 3; + int SIGNING_BLOCK_V4 = 4; } @Nullable diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java new file mode 100644 index 000000000000..b6b8089b1743 --- /dev/null +++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util.apk; + +import android.os.incremental.IncrementalManager; + +import java.io.File; +import java.security.cert.Certificate; + +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.ParsingException; + +/** + * APK Signature Scheme v4 verifier. + * + * @hide for internal use only. + */ +public class ApkSignatureSchemeV4Verifier { + + /** + * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates + * associated with each signer. + */ + public static Certificate[] extractCertificates(String apkFile) + throws SignatureNotFoundException, SecurityException { + final byte[] rawSignature = IncrementalManager.unsafeGetFileSignature( + new File(apkFile).getAbsolutePath()); + if (rawSignature == null || rawSignature.length == 0) { + throw new SignatureNotFoundException("Failed to obtain raw signature from IncFS."); + } + + try { + PKCS7 pkcs7 = new PKCS7(rawSignature); + return pkcs7.getCertificates(); + } catch (ParsingException e) { + throw new SecurityException("Failed to parse signature and extract certificates", e); + } + } +} diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index 1d3e6d035bfe..f325c2171c23 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -92,6 +92,24 @@ public class ApkSignatureVerifier { @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) throws PackageParserException { + if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) { + // V3 and before are older than the requested minimum signing version + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No signature found in package of version " + minSignatureSchemeVersion + + " or newer for package " + apkPath); + } + + // first try v4 + try { + return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull); + } catch (SignatureNotFoundException e) { + // not signed with v4, try older if allowed + if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No APK Signature Scheme v4 signature in package " + apkPath, e); + } + } + if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { // V3 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, @@ -99,7 +117,13 @@ public class ApkSignatureVerifier { + " or newer for package " + apkPath); } - // first try v3 + return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull); + } + + private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath, + @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) + throws PackageParserException { + // try v3 try { return verifyV3Signature(apkPath, verifyFull); } catch (SignatureNotFoundException e) { @@ -142,6 +166,59 @@ public class ApkSignatureVerifier { } /** + * Verifies the provided APK using V4 schema. + * + * @param verifyFull whether to verify all contents of this APK or just collect certificates. + * @return the certificates associated with each signer. + * @throws SignatureNotFoundException if there are no V4 signatures in the APK + * @throws PackageParserException if there was a problem collecting certificates + */ + private static PackageParser.SigningDetails verifyV4Signature(String apkPath, + @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) + throws SignatureNotFoundException, PackageParserException { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4"); + try { + Certificate[] certs = ApkSignatureSchemeV4Verifier.extractCertificates(apkPath); + Certificate[][] signerCerts = new Certificate[][]{certs}; + Signature[] signerSigs = convertToSignatures(signerCerts); + + if (verifyFull) { + // v4 is an add-on and requires v2/v3 signature to validate against its certificates + final PackageParser.SigningDetails nonstreaming = verifyV3AndBelowSignatures( + apkPath, minSignatureSchemeVersion, false); + if (nonstreaming.signatureSchemeVersion <= SignatureSchemeVersion.JAR) { + throw new SecurityException( + "V4 signing block can only be verified along with V2 and above."); + } + if (nonstreaming.signatures.length == 0 + || nonstreaming.signatures.length != signerSigs.length) { + throw new SecurityException("Invalid number of signatures in " + + nonstreaming.signatureSchemeVersion); + } + + for (int i = 0, size = signerSigs.length; i < size; ++i) { + if (!nonstreaming.signatures[i].equals(signerSigs[i])) { + throw new SecurityException("V4 signature certificate does not match " + + nonstreaming.signatureSchemeVersion); + } + } + } + + return new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V4); + } catch (SignatureNotFoundException e) { + throw e; + } catch (Exception e) { + // APK Signature Scheme v4 signature found but did not verify + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Failed to collect certificates from " + apkPath + + " using APK Signature Scheme v4", e); + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + } + + /** * Verifies the provided APK using V3 schema. * * @param verifyFull whether to verify all contents of this APK or just collect certificates. |