diff options
author | 2017-04-11 00:28:34 -0700 | |
---|---|---|
committer | 2017-04-17 13:51:02 -0700 | |
commit | e01b520dd490935694a50f9f0abd7dc0564d95c5 (patch) | |
tree | bcea550c096c9a65bf930a4d44601152d34339a2 | |
parent | 5b06dbd12bcd28fbc051c88c3ea7f31bcf20d1f8 (diff) |
RecoverySystem: Verify the package compatibility.
This CL extends the existing API RecoverySystem.verifyPackage() to
verify the compatibility entry in an OTA package. It returns true
directly if such an entry doesn't exist. Otherwise it reads and passes
the contents to VintfObject to perform the verification.
This CL also adds a new system API
RecoverySystem.verifyPackageCompatibility() to verify a given
compatibility zip file. It extracts all the entries and passes them to
VintfObject.verify() to verify the package compatibility. This API is
for streaming A/B OTAs only, where we don't download the full package
but only needed entries.
Bug: 36592877
Bug: 36596980
Test: RecoverySystem.verifyPackage() returns the same result for
packages w/o a compatibility entry.
Change-Id: I038be672868a91820c045d1da100e8e33b23d442
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | core/java/android/os/RecoverySystem.java | 73 |
2 files changed, 74 insertions, 0 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 303a607b383c..91d0d7abe85b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -34402,6 +34402,7 @@ package android.os { method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException; method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; method public static void verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File) throws java.security.GeneralSecurityException, java.io.IOException; + method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; } public static abstract interface RecoverySystem.ProgressListener { diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 5f66abd58d8b..bb0fdbe4e26a 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -16,6 +16,8 @@ package android.os; +import static java.nio.charset.StandardCharsets.UTF_8; + import android.annotation.SystemApi; import android.content.BroadcastReceiver; import android.content.Context; @@ -24,9 +26,12 @@ import android.os.UserManager; import android.text.TextUtils; import android.util.Log; +import libcore.io.Streams; + import java.io.ByteArrayInputStream; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; @@ -39,6 +44,7 @@ import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; @@ -46,6 +52,7 @@ import java.util.List; import java.util.Locale; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; import com.android.internal.logging.MetricsLogger; @@ -317,6 +324,72 @@ public class RecoverySystem { } finally { raf.close(); } + + // Additionally verify the package compatibility. + if (!readAndVerifyPackageCompatibilityEntry(packageFile)) { + throw new SignatureException("package compatibility verification failed"); + } + } + + /** + * Verifies the compatibility entry from an {@link InputStream}. + * + * @return the verification result. + */ + private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException { + ArrayList<String> list = new ArrayList<>(); + ZipInputStream zis = new ZipInputStream(inputStream); + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + long entrySize = entry.getSize(); + if (entrySize > Integer.MAX_VALUE || entrySize < 0) { + throw new IOException( + "invalid entry size (" + entrySize + ") in the compatibility file"); + } + byte[] bytes = new byte[(int) entrySize]; + Streams.readFully(zis, bytes); + list.add(new String(bytes, UTF_8)); + } + if (list.isEmpty()) { + throw new IOException("no entries found in the compatibility file"); + } + // TODO(b/36814503): Enable the actual verification when VintfObject APIs are ready. + // return (VintfObject.verify(list.toArray(new String[list.size()])) == 0); + return true; + } + + /** + * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is + * a zip file (inside the OTA package zip). + * + * @return {@code true} if the entry doesn't exist or verification passes. + */ + private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile) + throws IOException { + try (ZipFile zip = new ZipFile(packageFile)) { + ZipEntry entry = zip.getEntry("compatibility.zip"); + if (entry == null) { + return true; + } + InputStream inputStream = zip.getInputStream(entry); + return verifyPackageCompatibility(inputStream); + } + } + + /** + * Verifies the package compatibility info against the current system. + * + * @param compatibilityFile the {@link File} that contains the package compatibility info. + * @throws IOException if there were any errors reading the compatibility file. + * @return the compatibility verification result. + * + * {@hide} + */ + @SystemApi + public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException { + try (InputStream inputStream = new FileInputStream(compatibilityFile)) { + return verifyPackageCompatibility(inputStream); + } } /** |