Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c892275 authored by Khaled Abdelmohsen's avatar Khaled Abdelmohsen Committed by Android (Google) Code Review
Browse files

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

parents 41b448e3 f6b1e8c3
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -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")) {
+44 −0
Original line number Diff line number Diff line
@@ -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)) {
+20 −2
Original line number Diff line number Diff line
@@ -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 =

        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.