Merge "Support multi apk stamp verification" into rvc-dev

This commit is contained in:
Khaled Abdelmohsen
2020-03-14 07:23:34 +00:00
committed by Android (Google) Code Review
3 changed files with 83 additions and 2 deletions

View File

@@ -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;
@@ -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")) {

View File

@@ -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;
@@ -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)) {

View File

@@ -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;
@@ -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 {
@@ -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 =
SourceStampVerifier.verify(installationPath.getAbsolutePath());
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.