Verify APKs using APK Signature Scheme v2.

This makes Package Manager check whether an APK is signed using APK
Signature Scheme v2 and, if it is, verify the APK's signatures using
that scheme rather than the usual JAR signature scheme.

APK Signature Scheme v2 is a whole-file signature scheme which aims
to protect every single bit of the APK as opposed to the JAR signature
scheme which protects only the names and uncompressed contents of ZIP
entries.

The two main goals of APK Signature Scheme v2 are:
1. Detect any unauthorized modifications to the APK. This is achieved
   by making the signature cover every byte of the APK being signed.
2. Enable much faster signature and integrity verification. This is
   achieved by requiring only a minimal amount of APK parsing before
   the signature is verified, thus completely bypassing ZIP entry
   decompression and by making integrity verification parallelizable
   by employing a hash tree.

Bug: 25794543
Change-Id: If59fe013f2e62bac7677bb20e65f6061b91eec2e
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index ca2aec1..0546a5f 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -32,8 +32,12 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
+import android.util.ArraySet;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
 import libcore.io.Base64;
 import sun.security.jca.Providers;
 import sun.security.pkcs.PKCS7;
@@ -353,6 +357,43 @@
             return;
         }
 
+        // Check whether APK Signature Scheme v2 signature was stripped.
+        String apkSignatureSchemeIdList =
+                attributes.getValue(
+                        ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
+        if (apkSignatureSchemeIdList != null) {
+            // This field contains a comma-separated list of APK signature scheme IDs which were
+            // used to sign this APK. If an ID is known to us, it means signatures of that scheme
+            // were stripped from the APK because otherwise we wouldn't have fallen back to
+            // verifying the APK using the JAR signature scheme.
+            boolean v2SignatureGenerated = false;
+            StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
+            while (tokenizer.hasMoreTokens()) {
+                String idText = tokenizer.nextToken().trim();
+                if (idText.isEmpty()) {
+                    continue;
+                }
+                int id;
+                try {
+                    id = Integer.parseInt(idText);
+                } catch (Exception ignored) {
+                    continue;
+                }
+                if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
+                    // This APK was supposed to be signed with APK Signature Scheme v2 but no such
+                    // signature was found.
+                    v2SignatureGenerated = true;
+                    break;
+                }
+            }
+
+            if (v2SignatureGenerated) {
+                throw new SecurityException(signatureFile + " indicates " + jarName + " is signed"
+                        + " using APK Signature Scheme v2, but no such signature was found."
+                        + " Signature stripped?");
+            }
+        }
+
         // Do we actually have any signatures to look at?
         if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) {
             return;