Loading core/java/android/content/pm/ManifestDigest.java +33 −20 Original line number Diff line number Diff line Loading @@ -18,10 +18,17 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; import android.util.Base64; import android.util.Slog; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.jar.Attributes; import libcore.io.IoUtils; /** * Represents the manifest digest for a package. This is suitable for comparison Loading @@ -30,17 +37,17 @@ import java.util.jar.Attributes; * @hide */ public class ManifestDigest implements Parcelable { private static final String TAG = "ManifestDigest"; /** The digest of the manifest in our preferred order. */ private final byte[] mDigest; /** Digest field names to look for in preferred order. */ private static final String[] DIGEST_TYPES = { "SHA1-Digest", "SHA-Digest", "MD5-Digest", }; /** What we print out first when toString() is called. */ private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest="; /** Digest algorithm to use. */ private static final String DIGEST_ALGORITHM = "SHA-256"; ManifestDigest(byte[] digest) { mDigest = digest; } Loading @@ -49,26 +56,32 @@ public class ManifestDigest implements Parcelable { mDigest = source.createByteArray(); } static ManifestDigest fromAttributes(Attributes attributes) { if (attributes == null) { static ManifestDigest fromInputStream(InputStream fileIs) { if (fileIs == null) { return null; } String encodedDigest = null; for (int i = 0; i < DIGEST_TYPES.length; i++) { final String value = attributes.getValue(DIGEST_TYPES[i]); if (value != null) { encodedDigest = value; break; } final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_ALGORITHM); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e); } if (encodedDigest == null) { final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md); try { byte[] readBuffer = new byte[8192]; while (dis.read(readBuffer, 0, readBuffer.length) != -1) { // not using } } catch (IOException e) { Slog.w(TAG, "Could not read manifest"); return null; } finally { IoUtils.closeQuietly(dis); } final byte[] digest = Base64.decode(encodedDigest, Base64.DEFAULT); final byte[] digest = md.digest(); return new ManifestDigest(digest); } Loading core/java/android/content/pm/PackageParser.java +25 −6 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; Loading Loading @@ -54,10 +53,9 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import com.android.internal.util.XmlUtils; Loading Loading @@ -567,6 +565,28 @@ public class PackageParser { return pkg; } /** * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the * APK. If it successfully scanned the package and found the * {@code AndroidManifest.xml}, {@code true} is returned. */ public boolean collectManifestDigest(Package pkg) { try { final JarFile jarFile = new JarFile(mArchiveSourcePath); try { final ZipEntry je = jarFile.getEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); } } finally { jarFile.close(); } return true; } catch (IOException e) { return false; } } public boolean collectCertificates(Package pkg, int flags) { pkg.mSignatures = null; Loading Loading @@ -618,7 +638,6 @@ public class PackageParser { } } else { Enumeration<JarEntry> entries = jarFile.entries(); final Manifest manifest = jarFile.getManifest(); while (entries.hasMoreElements()) { final JarEntry je = entries.nextElement(); if (je.isDirectory()) continue; Loading @@ -629,8 +648,8 @@ public class PackageParser { continue; if (ANDROID_MANIFEST_FILENAME.equals(name)) { final Attributes attributes = manifest.getAttributes(name); pkg.manifestDigest = ManifestDigest.fromAttributes(attributes); pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); } final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer); Loading core/tests/coretests/src/android/content/pm/ManifestDigestTest.java +27 −40 Original line number Diff line number Diff line Loading @@ -18,64 +18,51 @@ package android.content.pm; import android.os.Parcel; import android.test.AndroidTestCase; import android.util.Base64; import java.util.jar.Attributes; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; public class ManifestDigestTest extends AndroidTestCase { private static final byte[] DIGEST_1 = { private static final byte[] MESSAGE_1 = { (byte) 0x00, (byte) 0xAA, (byte) 0x55, (byte) 0xFF }; private static final String DIGEST_1_STR = Base64.encodeToString(DIGEST_1, Base64.DEFAULT); private static final byte[] DIGEST_2 = { (byte) 0x0A, (byte) 0xA5, (byte) 0xF0, (byte) 0x5A }; private static final String DIGEST_2_STR = Base64.encodeToString(DIGEST_2, Base64.DEFAULT); private static final Attributes.Name SHA1_DIGEST = new Attributes.Name("SHA1-Digest"); private static final Attributes.Name MD5_DIGEST = new Attributes.Name("MD5-Digest"); public void testManifestDigest_FromAttributes_Null() { public void testManifestDigest_FromInputStream_Null() { assertNull("Attributes were null, so ManifestDigest.fromAttributes should return null", ManifestDigest.fromAttributes(null)); ManifestDigest.fromInputStream(null)); } public void testManifestDigest_FromAttributes_NoAttributes() { Attributes a = new Attributes(); assertNull("There were no attributes to extract, so ManifestDigest should be null", ManifestDigest.fromAttributes(a)); public void testManifestDigest_FromInputStream_ThrowsIoException() { InputStream is = new InputStream() { @Override public int read() throws IOException { throw new IOException(); } }; public void testManifestDigest_FromAttributes_SHA1PreferredOverMD5() { Attributes a = new Attributes(); a.put(SHA1_DIGEST, DIGEST_1_STR); a.put(MD5_DIGEST, DIGEST_2_STR); ManifestDigest fromAttributes = ManifestDigest.fromAttributes(a); assertNull("InputStream threw exception, so ManifestDigest should be null", ManifestDigest.fromInputStream(is)); } assertNotNull("A valid ManifestDigest should be returned", fromAttributes); public void testManifestDigest_Equals() throws Exception { InputStream is = new ByteArrayInputStream(MESSAGE_1); ManifestDigest created = new ManifestDigest(DIGEST_1); ManifestDigest expected = new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1)); assertEquals("SHA-1 should be preferred over MD5: " + created.toString() + " vs. " + fromAttributes.toString(), created, fromAttributes); ManifestDigest actual = ManifestDigest.fromInputStream(is); assertEquals(expected, actual); assertEquals("Hash codes should be the same: " + created.toString() + " vs. " + fromAttributes.toString(), created.hashCode(), fromAttributes .hashCode()); ManifestDigest unexpected = new ManifestDigest(new byte[0]); assertFalse(unexpected.equals(actual)); } public void testManifestDigest_Parcel() { Attributes a = new Attributes(); a.put(SHA1_DIGEST, DIGEST_1_STR); public void testManifestDigest_Parcel() throws Exception { InputStream is = new ByteArrayInputStream(MESSAGE_1); ManifestDigest digest = ManifestDigest.fromAttributes(a); ManifestDigest digest = ManifestDigest.fromInputStream(is); Parcel p = Parcel.obtain(); digest.writeToParcel(p, 0); Loading Loading
core/java/android/content/pm/ManifestDigest.java +33 −20 Original line number Diff line number Diff line Loading @@ -18,10 +18,17 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; import android.util.Base64; import android.util.Slog; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.jar.Attributes; import libcore.io.IoUtils; /** * Represents the manifest digest for a package. This is suitable for comparison Loading @@ -30,17 +37,17 @@ import java.util.jar.Attributes; * @hide */ public class ManifestDigest implements Parcelable { private static final String TAG = "ManifestDigest"; /** The digest of the manifest in our preferred order. */ private final byte[] mDigest; /** Digest field names to look for in preferred order. */ private static final String[] DIGEST_TYPES = { "SHA1-Digest", "SHA-Digest", "MD5-Digest", }; /** What we print out first when toString() is called. */ private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest="; /** Digest algorithm to use. */ private static final String DIGEST_ALGORITHM = "SHA-256"; ManifestDigest(byte[] digest) { mDigest = digest; } Loading @@ -49,26 +56,32 @@ public class ManifestDigest implements Parcelable { mDigest = source.createByteArray(); } static ManifestDigest fromAttributes(Attributes attributes) { if (attributes == null) { static ManifestDigest fromInputStream(InputStream fileIs) { if (fileIs == null) { return null; } String encodedDigest = null; for (int i = 0; i < DIGEST_TYPES.length; i++) { final String value = attributes.getValue(DIGEST_TYPES[i]); if (value != null) { encodedDigest = value; break; } final MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_ALGORITHM); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e); } if (encodedDigest == null) { final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md); try { byte[] readBuffer = new byte[8192]; while (dis.read(readBuffer, 0, readBuffer.length) != -1) { // not using } } catch (IOException e) { Slog.w(TAG, "Could not read manifest"); return null; } finally { IoUtils.closeQuietly(dis); } final byte[] digest = Base64.decode(encodedDigest, Base64.DEFAULT); final byte[] digest = md.digest(); return new ManifestDigest(digest); } Loading
core/java/android/content/pm/PackageParser.java +25 −6 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; Loading Loading @@ -54,10 +53,9 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import com.android.internal.util.XmlUtils; Loading Loading @@ -567,6 +565,28 @@ public class PackageParser { return pkg; } /** * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the * APK. If it successfully scanned the package and found the * {@code AndroidManifest.xml}, {@code true} is returned. */ public boolean collectManifestDigest(Package pkg) { try { final JarFile jarFile = new JarFile(mArchiveSourcePath); try { final ZipEntry je = jarFile.getEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); } } finally { jarFile.close(); } return true; } catch (IOException e) { return false; } } public boolean collectCertificates(Package pkg, int flags) { pkg.mSignatures = null; Loading Loading @@ -618,7 +638,6 @@ public class PackageParser { } } else { Enumeration<JarEntry> entries = jarFile.entries(); final Manifest manifest = jarFile.getManifest(); while (entries.hasMoreElements()) { final JarEntry je = entries.nextElement(); if (je.isDirectory()) continue; Loading @@ -629,8 +648,8 @@ public class PackageParser { continue; if (ANDROID_MANIFEST_FILENAME.equals(name)) { final Attributes attributes = manifest.getAttributes(name); pkg.manifestDigest = ManifestDigest.fromAttributes(attributes); pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); } final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer); Loading
core/tests/coretests/src/android/content/pm/ManifestDigestTest.java +27 −40 Original line number Diff line number Diff line Loading @@ -18,64 +18,51 @@ package android.content.pm; import android.os.Parcel; import android.test.AndroidTestCase; import android.util.Base64; import java.util.jar.Attributes; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; public class ManifestDigestTest extends AndroidTestCase { private static final byte[] DIGEST_1 = { private static final byte[] MESSAGE_1 = { (byte) 0x00, (byte) 0xAA, (byte) 0x55, (byte) 0xFF }; private static final String DIGEST_1_STR = Base64.encodeToString(DIGEST_1, Base64.DEFAULT); private static final byte[] DIGEST_2 = { (byte) 0x0A, (byte) 0xA5, (byte) 0xF0, (byte) 0x5A }; private static final String DIGEST_2_STR = Base64.encodeToString(DIGEST_2, Base64.DEFAULT); private static final Attributes.Name SHA1_DIGEST = new Attributes.Name("SHA1-Digest"); private static final Attributes.Name MD5_DIGEST = new Attributes.Name("MD5-Digest"); public void testManifestDigest_FromAttributes_Null() { public void testManifestDigest_FromInputStream_Null() { assertNull("Attributes were null, so ManifestDigest.fromAttributes should return null", ManifestDigest.fromAttributes(null)); ManifestDigest.fromInputStream(null)); } public void testManifestDigest_FromAttributes_NoAttributes() { Attributes a = new Attributes(); assertNull("There were no attributes to extract, so ManifestDigest should be null", ManifestDigest.fromAttributes(a)); public void testManifestDigest_FromInputStream_ThrowsIoException() { InputStream is = new InputStream() { @Override public int read() throws IOException { throw new IOException(); } }; public void testManifestDigest_FromAttributes_SHA1PreferredOverMD5() { Attributes a = new Attributes(); a.put(SHA1_DIGEST, DIGEST_1_STR); a.put(MD5_DIGEST, DIGEST_2_STR); ManifestDigest fromAttributes = ManifestDigest.fromAttributes(a); assertNull("InputStream threw exception, so ManifestDigest should be null", ManifestDigest.fromInputStream(is)); } assertNotNull("A valid ManifestDigest should be returned", fromAttributes); public void testManifestDigest_Equals() throws Exception { InputStream is = new ByteArrayInputStream(MESSAGE_1); ManifestDigest created = new ManifestDigest(DIGEST_1); ManifestDigest expected = new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1)); assertEquals("SHA-1 should be preferred over MD5: " + created.toString() + " vs. " + fromAttributes.toString(), created, fromAttributes); ManifestDigest actual = ManifestDigest.fromInputStream(is); assertEquals(expected, actual); assertEquals("Hash codes should be the same: " + created.toString() + " vs. " + fromAttributes.toString(), created.hashCode(), fromAttributes .hashCode()); ManifestDigest unexpected = new ManifestDigest(new byte[0]); assertFalse(unexpected.equals(actual)); } public void testManifestDigest_Parcel() { Attributes a = new Attributes(); a.put(SHA1_DIGEST, DIGEST_1_STR); public void testManifestDigest_Parcel() throws Exception { InputStream is = new ByteArrayInputStream(MESSAGE_1); ManifestDigest digest = ManifestDigest.fromAttributes(a); ManifestDigest digest = ManifestDigest.fromInputStream(is); Parcel p = Parcel.obtain(); digest.writeToParcel(p, 0); Loading