Merge "Support multi apk stamp verification" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2c89227525
@@ -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")) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user