Loading core/java/android/util/apk/SourceStampVerifier.java +19 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; Loading Loading @@ -78,6 +79,24 @@ public abstract class SourceStampVerifier { /** Hidden constructor to prevent instantiation. */ private SourceStampVerifier() {} /** Verifies SourceStamp present in a list of APKs. */ public static SourceStampVerificationResult verify(List<String> apkFiles) { Certificate stampCertificate = null; for (String apkFile : apkFiles) { SourceStampVerificationResult sourceStampVerificationResult = verify(apkFile); if (!sourceStampVerificationResult.isPresent() || !sourceStampVerificationResult.isVerified()) { return sourceStampVerificationResult; } if (stampCertificate != null && !stampCertificate.equals(sourceStampVerificationResult.getCertificate())) { return SourceStampVerificationResult.notVerified(); } stampCertificate = sourceStampVerificationResult.getCertificate(); } return SourceStampVerificationResult.verified(stampCertificate); } /** Verifies SourceStamp present in the provided APK. */ public static SourceStampVerificationResult verify(String apkFile) { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { Loading core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java +44 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; Loading Loading @@ -129,6 +131,48 @@ public class SourceStampVerifierTest { assertNull(result.getCertificate()); } @Test public void testSourceStamp_multiApk_validStamps() throws Exception { File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk"); File testApk2 = getApk("SourceStampVerifierTest/valid-stamp.apk"); ZipFile apkZipFile = new ZipFile(testApk1); ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256"); int size = (int) stampCertZipEntry.getSize(); byte[] expectedStampCertHash = new byte[size]; try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) { inputStream.read(expectedStampCertHash); } List<String> apkFiles = new ArrayList<>(); apkFiles.add(testApk1.getAbsolutePath()); apkFiles.add(testApk2.getAbsolutePath()); SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles); assertTrue(result.isPresent()); assertTrue(result.isVerified()); assertNotNull(result.getCertificate()); byte[] actualStampCertHash = MessageDigest.getInstance("SHA-256").digest(result.getCertificate().getEncoded()); assertArrayEquals(expectedStampCertHash, actualStampCertHash); } @Test public void testSourceStamp_multiApk_invalidStamps() throws Exception { File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk"); File testApk2 = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk"); List<String> apkFiles = new ArrayList<>(); apkFiles.add(testApk1.getAbsolutePath()); apkFiles.add(testApk2.getAbsolutePath()); SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles); assertTrue(result.isPresent()); assertFalse(result.isVerified()); assertNull(result.getCertificate()); } private File getApk(String apkPath) throws IOException { File testApk = File.createTempFile("SourceStampApk", ".apk"); try (InputStream inputStream = mContext.getAssets().open(apkPath)) { Loading services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +20 −2 Original line number Diff line number Diff line Loading @@ -69,8 +69,10 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; Loading @@ -85,6 +87,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** Implementation of {@link AppIntegrityManagerService}. */ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { Loading Loading @@ -467,8 +470,23 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { if (installationPath == null) { throw new IllegalArgumentException("Installation path is null, package not found"); } SourceStampVerificationResult sourceStampVerificationResult = SourceStampVerificationResult sourceStampVerificationResult; if (installationPath.isDirectory()) { try { List<String> apkFiles = Files.list(installationPath.toPath()) .map(path -> path.toAbsolutePath().toString()) .collect(Collectors.toList()); sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles); } catch (IOException e) { throw new IllegalArgumentException("Could not read APK directory"); } } else { sourceStampVerificationResult = SourceStampVerifier.verify(installationPath.getAbsolutePath()); } appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent()); appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified()); // A verified stamp is set to be trusted. Loading Loading
core/java/android/util/apk/SourceStampVerifier.java +19 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; Loading Loading @@ -78,6 +79,24 @@ public abstract class SourceStampVerifier { /** Hidden constructor to prevent instantiation. */ private SourceStampVerifier() {} /** Verifies SourceStamp present in a list of APKs. */ public static SourceStampVerificationResult verify(List<String> apkFiles) { Certificate stampCertificate = null; for (String apkFile : apkFiles) { SourceStampVerificationResult sourceStampVerificationResult = verify(apkFile); if (!sourceStampVerificationResult.isPresent() || !sourceStampVerificationResult.isVerified()) { return sourceStampVerificationResult; } if (stampCertificate != null && !stampCertificate.equals(sourceStampVerificationResult.getCertificate())) { return SourceStampVerificationResult.notVerified(); } stampCertificate = sourceStampVerificationResult.getCertificate(); } return SourceStampVerificationResult.verified(stampCertificate); } /** Verifies SourceStamp present in the provided APK. */ public static SourceStampVerificationResult verify(String apkFile) { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { Loading
core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java +44 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; Loading Loading @@ -129,6 +131,48 @@ public class SourceStampVerifierTest { assertNull(result.getCertificate()); } @Test public void testSourceStamp_multiApk_validStamps() throws Exception { File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk"); File testApk2 = getApk("SourceStampVerifierTest/valid-stamp.apk"); ZipFile apkZipFile = new ZipFile(testApk1); ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256"); int size = (int) stampCertZipEntry.getSize(); byte[] expectedStampCertHash = new byte[size]; try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) { inputStream.read(expectedStampCertHash); } List<String> apkFiles = new ArrayList<>(); apkFiles.add(testApk1.getAbsolutePath()); apkFiles.add(testApk2.getAbsolutePath()); SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles); assertTrue(result.isPresent()); assertTrue(result.isVerified()); assertNotNull(result.getCertificate()); byte[] actualStampCertHash = MessageDigest.getInstance("SHA-256").digest(result.getCertificate().getEncoded()); assertArrayEquals(expectedStampCertHash, actualStampCertHash); } @Test public void testSourceStamp_multiApk_invalidStamps() throws Exception { File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk"); File testApk2 = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk"); List<String> apkFiles = new ArrayList<>(); apkFiles.add(testApk1.getAbsolutePath()); apkFiles.add(testApk2.getAbsolutePath()); SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles); assertTrue(result.isPresent()); assertFalse(result.isVerified()); assertNull(result.getCertificate()); } private File getApk(String apkPath) throws IOException { File testApk = File.createTempFile("SourceStampApk", ".apk"); try (InputStream inputStream = mContext.getAssets().open(apkPath)) { Loading
services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +20 −2 Original line number Diff line number Diff line Loading @@ -69,8 +69,10 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; Loading @@ -85,6 +87,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** Implementation of {@link AppIntegrityManagerService}. */ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { Loading Loading @@ -467,8 +470,23 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { if (installationPath == null) { throw new IllegalArgumentException("Installation path is null, package not found"); } SourceStampVerificationResult sourceStampVerificationResult = SourceStampVerificationResult sourceStampVerificationResult; if (installationPath.isDirectory()) { try { List<String> apkFiles = Files.list(installationPath.toPath()) .map(path -> path.toAbsolutePath().toString()) .collect(Collectors.toList()); sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles); } catch (IOException e) { throw new IllegalArgumentException("Could not read APK directory"); } } else { sourceStampVerificationResult = SourceStampVerifier.verify(installationPath.getAbsolutePath()); } appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent()); appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified()); // A verified stamp is set to be trusted. Loading