Merge "Extract source stamp during app install" into rvc-dev am: 759dbb7cf6 am: 08d82e4c1a am: 2b769b4c6e

Change-Id: Ib7738ba151b1dc02e31b6a4d30430cd0778fa405
This commit is contained in:
Automerger Merge Worker
2020-03-05 00:17:37 +00:00
5 changed files with 199 additions and 59 deletions

View File

@@ -42,6 +42,8 @@ public final class AppInstallMetadata {
private final List<String> mInstallerCertificates; private final List<String> mInstallerCertificates;
private final long mVersionCode; private final long mVersionCode;
private final boolean mIsPreInstalled; private final boolean mIsPreInstalled;
private final boolean mIsStampPresent;
private final boolean mIsStampVerified;
private final boolean mIsStampTrusted; private final boolean mIsStampTrusted;
// Raw string encoding for the SHA-256 hash of the certificate of the stamp. // Raw string encoding for the SHA-256 hash of the certificate of the stamp.
private final String mStampCertificateHash; private final String mStampCertificateHash;
@@ -54,6 +56,8 @@ public final class AppInstallMetadata {
this.mInstallerCertificates = builder.mInstallerCertificates; this.mInstallerCertificates = builder.mInstallerCertificates;
this.mVersionCode = builder.mVersionCode; this.mVersionCode = builder.mVersionCode;
this.mIsPreInstalled = builder.mIsPreInstalled; this.mIsPreInstalled = builder.mIsPreInstalled;
this.mIsStampPresent = builder.mIsStampPresent;
this.mIsStampVerified = builder.mIsStampVerified;
this.mIsStampTrusted = builder.mIsStampTrusted; this.mIsStampTrusted = builder.mIsStampTrusted;
this.mStampCertificateHash = builder.mStampCertificateHash; this.mStampCertificateHash = builder.mStampCertificateHash;
this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates; this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates;
@@ -89,6 +93,16 @@ public final class AppInstallMetadata {
return mIsPreInstalled; return mIsPreInstalled;
} }
/** @see AppInstallMetadata.Builder#setIsStampPresent(boolean) */
public boolean isStampPresent() {
return mIsStampPresent;
}
/** @see AppInstallMetadata.Builder#setIsStampVerified(boolean) */
public boolean isStampVerified() {
return mIsStampVerified;
}
/** @see AppInstallMetadata.Builder#setIsStampTrusted(boolean) */ /** @see AppInstallMetadata.Builder#setIsStampTrusted(boolean) */
public boolean isStampTrusted() { public boolean isStampTrusted() {
return mIsStampTrusted; return mIsStampTrusted;
@@ -108,14 +122,16 @@ public final class AppInstallMetadata {
public String toString() { public String toString() {
return String.format( return String.format(
"AppInstallMetadata { PackageName = %s, AppCerts = %s, InstallerName = %s," "AppInstallMetadata { PackageName = %s, AppCerts = %s, InstallerName = %s,"
+ " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b, " + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b, StampPresent ="
+ "StampTrusted = %b, StampCert = %s }", + " %b, StampVerified = %b, StampTrusted = %b, StampCert = %s }",
mPackageName, mPackageName,
mAppCertificates, mAppCertificates,
mInstallerName == null ? "null" : mInstallerName, mInstallerName == null ? "null" : mInstallerName,
mInstallerCertificates == null ? "null" : mInstallerCertificates, mInstallerCertificates == null ? "null" : mInstallerCertificates,
mVersionCode, mVersionCode,
mIsPreInstalled, mIsPreInstalled,
mIsStampPresent,
mIsStampVerified,
mIsStampTrusted, mIsStampTrusted,
mStampCertificateHash == null ? "null" : mStampCertificateHash); mStampCertificateHash == null ? "null" : mStampCertificateHash);
} }
@@ -128,6 +144,8 @@ public final class AppInstallMetadata {
private List<String> mInstallerCertificates; private List<String> mInstallerCertificates;
private long mVersionCode; private long mVersionCode;
private boolean mIsPreInstalled; private boolean mIsPreInstalled;
private boolean mIsStampPresent;
private boolean mIsStampVerified;
private boolean mIsStampTrusted; private boolean mIsStampTrusted;
private String mStampCertificateHash; private String mStampCertificateHash;
private Map<String, String> mAllowedInstallersAndCertificates; private Map<String, String> mAllowedInstallersAndCertificates;
@@ -221,16 +239,24 @@ public final class AppInstallMetadata {
} }
/** /**
* Set certificate hash of the stamp embedded in the APK. * Set whether the stamp embedded in the APK is present or not.
* *
* <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate * @see AppInstallMetadata#isStampPresent()
* of the stamp.
*
* @see AppInstallMetadata#getStampCertificateHash()
*/ */
@NonNull @NonNull
public Builder setStampCertificateHash(@NonNull String stampCertificateHash) { public Builder setIsStampPresent(boolean isStampPresent) {
this.mStampCertificateHash = Objects.requireNonNull(stampCertificateHash); this.mIsStampPresent = isStampPresent;
return this;
}
/**
* Set whether the stamp embedded in the APK is verified or not.
*
* @see AppInstallMetadata#isStampVerified()
*/
@NonNull
public Builder setIsStampVerified(boolean isStampVerified) {
this.mIsStampVerified = isStampVerified;
return this; return this;
} }
@@ -245,6 +271,20 @@ public final class AppInstallMetadata {
return this; return this;
} }
/**
* Set certificate hash of the stamp embedded in the APK.
*
* <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
* of the stamp.
*
* @see AppInstallMetadata#getStampCertificateHash()
*/
@NonNull
public Builder setStampCertificateHash(@NonNull String stampCertificateHash) {
this.mStampCertificateHash = Objects.requireNonNull(stampCertificateHash);
return this;
}
/** /**
* Build {@link AppInstallMetadata}. * Build {@link AppInstallMetadata}.
* *

View File

@@ -368,11 +368,10 @@ public abstract class AtomicFormula extends IntegrityFormula {
"Key %s cannot be used with StringAtomicFormula", keyToString(key))); "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
mValue = hashValue(key, value); mValue = hashValue(key, value);
mIsHashedValue = mIsHashedValue =
key == APP_CERTIFICATE (key == APP_CERTIFICATE
|| key == INSTALLER_CERTIFICATE || key == INSTALLER_CERTIFICATE
|| key == STAMP_CERTIFICATE_HASH || key == STAMP_CERTIFICATE_HASH)
? true || !mValue.equals(value);
: !mValue.equals(value);
} }
StringAtomicFormula(Parcel in) { StringAtomicFormula(Parcel in) {

View File

@@ -52,7 +52,10 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.security.FileIntegrityManager;
import android.util.Slog; import android.util.Slog;
import android.util.apk.SourceStampVerificationResult;
import android.util.apk.SourceStampVerifier;
import com.android.internal.R; import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -108,8 +111,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private static final String ALLOWED_INSTALLER_DELIMITER = ","; private static final String ALLOWED_INSTALLER_DELIMITER = ",";
private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|"; private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
private static final Set<String> PACKAGE_INSTALLER = new HashSet<>( private static final Set<String> PACKAGE_INSTALLER =
Arrays.asList("com.google.android.packageinstaller", "com.android.packageinstaller")); new HashSet<>(
Arrays.asList(
"com.google.android.packageinstaller", "com.android.packageinstaller"));
// Access to files inside mRulesDir is protected by mRulesLock; // Access to files inside mRulesDir is protected by mRulesLock;
private final Context mContext; private final Context mContext;
@@ -117,6 +122,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private final PackageManagerInternal mPackageManagerInternal; private final PackageManagerInternal mPackageManagerInternal;
private final RuleEvaluationEngine mEvaluationEngine; private final RuleEvaluationEngine mEvaluationEngine;
private final IntegrityFileManager mIntegrityFileManager; private final IntegrityFileManager mIntegrityFileManager;
private final FileIntegrityManager mFileIntegrityManager;
/** Create an instance of {@link AppIntegrityManagerServiceImpl}. */ /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
public static AppIntegrityManagerServiceImpl create(Context context) { public static AppIntegrityManagerServiceImpl create(Context context) {
@@ -128,6 +134,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
LocalServices.getService(PackageManagerInternal.class), LocalServices.getService(PackageManagerInternal.class),
RuleEvaluationEngine.getRuleEvaluationEngine(), RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(), IntegrityFileManager.getInstance(),
(FileIntegrityManager) context.getSystemService(Context.FILE_INTEGRITY_SERVICE),
handlerThread.getThreadHandler()); handlerThread.getThreadHandler());
} }
@@ -137,11 +144,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
PackageManagerInternal packageManagerInternal, PackageManagerInternal packageManagerInternal,
RuleEvaluationEngine evaluationEngine, RuleEvaluationEngine evaluationEngine,
IntegrityFileManager integrityFileManager, IntegrityFileManager integrityFileManager,
FileIntegrityManager fileIntegrityManager,
Handler handler) { Handler handler) {
mContext = context; mContext = context;
mPackageManagerInternal = packageManagerInternal; mPackageManagerInternal = packageManagerInternal;
mEvaluationEngine = evaluationEngine; mEvaluationEngine = evaluationEngine;
mIntegrityFileManager = integrityFileManager; mIntegrityFileManager = integrityFileManager;
mFileIntegrityManager = fileIntegrityManager;
mHandler = handler; mHandler = handler;
IntentFilter integrityVerificationFilter = new IntentFilter(); IntentFilter integrityVerificationFilter = new IntentFilter();
@@ -183,8 +192,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
success = false; success = false;
} }
FrameworkStatsLog.write(FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success, FrameworkStatsLog.write(
ruleProvider, version); FrameworkStatsLog.INTEGRITY_RULES_PUSHED,
success,
ruleProvider,
version);
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE); intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE);
@@ -242,8 +254,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
String installerPackageName = getInstallerPackageName(intent); String installerPackageName = getInstallerPackageName(intent);
// Skip integrity verification if the verifier is doing the install. // Skip integrity verification if the verifier is doing the install.
if (!integrityCheckIncludesRuleProvider() if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) {
&& isRuleProvider(installerPackageName)) {
Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
mPackageManagerInternal.setIntegrityVerificationResult( mPackageManagerInternal.setIntegrityVerificationResult(
verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
@@ -274,15 +285,17 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
builder.setInstallerCertificates(installerCertificates); builder.setInstallerCertificates(installerCertificates);
builder.setIsPreInstalled(isSystemApp(packageName)); builder.setIsPreInstalled(isSystemApp(packageName));
builder.setAllowedInstallersAndCert(getAllowedInstallers(packageInfo)); builder.setAllowedInstallersAndCert(getAllowedInstallers(packageInfo));
extractSourceStamp(intent.getData(), builder);
AppInstallMetadata appInstallMetadata = builder.build(); AppInstallMetadata appInstallMetadata = builder.build();
Slog.i( Slog.i(
TAG, TAG,
"To be verified: " + appInstallMetadata + " installers " + getAllowedInstallers( "To be verified: "
packageInfo)); + appInstallMetadata
IntegrityCheckResult result = + " installers "
mEvaluationEngine.evaluate(appInstallMetadata); + getAllowedInstallers(packageInfo));
IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata);
Slog.i( Slog.i(
TAG, TAG,
"Integrity check result: " "Integrity check result: "
@@ -323,7 +336,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
* Verify the UID and return the installer package name. * Verify the UID and return the installer package name.
* *
* @return the package name of the installer, or null if it cannot be determined or it is * @return the package name of the installer, or null if it cannot be determined or it is
* installed via adb. * installed via adb.
*/ */
@Nullable @Nullable
private String getInstallerPackageName(Intent intent) { private String getInstallerPackageName(Intent intent) {
@@ -442,7 +455,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
String cert = packageAndCert[1]; String cert = packageAndCert[1];
packageCertMap.put(packageName, cert); packageCertMap.put(packageName, cert);
} else if (packageAndCert.length == 1) { } else if (packageAndCert.length == 1) {
packageCertMap.put(getPackageNameNormalized(packageAndCert[0]), packageCertMap.put(
getPackageNameNormalized(packageAndCert[0]),
INSTALLER_CERTIFICATE_NOT_EVALUATED); INSTALLER_CERTIFICATE_NOT_EVALUATED);
} }
} }
@@ -452,6 +466,41 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
return packageCertMap; return packageCertMap;
} }
/** Extract the source stamp embedded in the APK, if present. */
private void extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata) {
File installationPath = getInstallationPath(dataUri);
if (installationPath == null) {
throw new IllegalArgumentException("Installation path is null, package not found");
}
SourceStampVerificationResult sourceStampVerificationResult =
SourceStampVerifier.verify(installationPath.getAbsolutePath());
appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent());
appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified());
if (sourceStampVerificationResult.isVerified()) {
X509Certificate sourceStampCertificate =
(X509Certificate) sourceStampVerificationResult.getCertificate();
// Sets source stamp certificate digest.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] certificateDigest = digest.digest(sourceStampCertificate.getEncoded());
appInstallMetadata.setStampCertificateHash(getHexDigest(certificateDigest));
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
throw new IllegalArgumentException(
"Error computing source stamp certificate digest", e);
}
// Checks if the source stamp certificate is trusted.
try {
appInstallMetadata.setIsStampTrusted(
mFileIntegrityManager.isApkVeritySupported()
&& mFileIntegrityManager.isAppSourceCertificateTrusted(
sourceStampCertificate));
} catch (CertificateEncodingException e) {
throw new IllegalArgumentException(
"Error checking if source stamp certificate is trusted", e);
}
}
}
private static Signature[] getSignatures(@NonNull PackageInfo packageInfo) { private static Signature[] getSignatures(@NonNull PackageInfo packageInfo) {
SigningInfo signingInfo = packageInfo.signingInfo; SigningInfo signingInfo = packageInfo.signingInfo;
@@ -505,8 +554,16 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
ParsedPackage pkg = parser.parsePackage(installationPath, 0, false); ParsedPackage pkg = parser.parsePackage(installationPath, 0, false);
int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA; int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA;
pkg.setSigningDetails(ParsingPackageUtils.collectCertificates(pkg, false)); pkg.setSigningDetails(ParsingPackageUtils.collectCertificates(pkg, false));
return PackageInfoUtils.generate(pkg, null, flags, 0, 0, null, new PackageUserState(), return PackageInfoUtils.generate(
UserHandle.getCallingUserId(), null); pkg,
null,
flags,
0,
0,
null,
new PackageUserState(),
UserHandle.getCallingUserId(),
null);
} catch (Exception e) { } catch (Exception e) {
Slog.w(TAG, "Exception reading " + dataUri, e); Slog.w(TAG, "Exception reading " + dataUri, e);
return null; return null;
@@ -633,9 +690,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private boolean integrityCheckIncludesRuleProvider() { private boolean integrityCheckIncludesRuleProvider() {
return Settings.Global.getInt( return Settings.Global.getInt(
mContext.getContentResolver(), mContext.getContentResolver(),
Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
0) 0)
== 1; == 1;
} }
} }

View File

@@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
@@ -61,6 +62,7 @@ import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.provider.Settings; import android.provider.Settings;
import android.security.FileIntegrityManager;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
@@ -96,6 +98,9 @@ public class AppIntegrityManagerServiceImplTest {
private static final String TEST_APP_TWO_CERT_PATH = private static final String TEST_APP_TWO_CERT_PATH =
"AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk"; "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
private static final String TEST_APP_SOURCE_STAMP_PATH =
"AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String VERSION = "version"; private static final String VERSION = "version";
private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests"; private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
@@ -111,6 +116,8 @@ public class AppIntegrityManagerServiceImplTest {
// We use SHA256 for package names longer than 32 characters. // We use SHA256 for package names longer than 32 characters.
private static final String INSTALLER_SHA256 = private static final String INSTALLER_SHA256 =
"30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227"; "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
private static final String SOURCE_STAMP_CERTIFICATE_HASH =
"681B0E56A796350C08647352A4DB800CC44B2ADC8F4C72FA350BD05D4D50264D";
private static final String DUMMY_APP_TWO_CERTS_CERT_1 = private static final String DUMMY_APP_TWO_CERTS_CERT_1 =
"C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94"; "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94";
@@ -121,27 +128,22 @@ public class AppIntegrityManagerServiceImplTest {
private static final String ADB_INSTALLER = "adb"; private static final String ADB_INSTALLER = "adb";
private static final String PLAY_STORE_CERT = "play_store_cert"; private static final String PLAY_STORE_CERT = "play_store_cert";
@org.junit.Rule @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock @Mock PackageManagerInternal mPackageManagerInternal;
PackageManagerInternal mPackageManagerInternal; @Mock Context mMockContext;
@Mock @Mock Resources mMockResources;
Context mMockContext; @Mock RuleEvaluationEngine mRuleEvaluationEngine;
@Mock @Mock IntegrityFileManager mIntegrityFileManager;
Resources mMockResources; @Mock Handler mHandler;
@Mock FileIntegrityManager mFileIntegrityManager;
RuleEvaluationEngine mRuleEvaluationEngine;
@Mock
IntegrityFileManager mIntegrityFileManager;
@Mock
Handler mHandler;
private final Context mRealContext = InstrumentationRegistry.getTargetContext(); private final Context mRealContext = InstrumentationRegistry.getTargetContext();
private PackageManager mSpyPackageManager; private PackageManager mSpyPackageManager;
private File mTestApk; private File mTestApk;
private File mTestApkTwoCerts; private File mTestApkTwoCerts;
private File mTestApkSourceStamp;
// under test // under test
private AppIntegrityManagerServiceImpl mService; private AppIntegrityManagerServiceImpl mService;
@@ -158,19 +160,28 @@ public class AppIntegrityManagerServiceImplTest {
Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING); Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
} }
mTestApkSourceStamp = File.createTempFile("AppIntegritySourceStamp", ".apk");
try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_SOURCE_STAMP_PATH)) {
Files.copy(inputStream, mTestApkSourceStamp.toPath(), REPLACE_EXISTING);
}
mFileIntegrityManager =
(FileIntegrityManager)
mRealContext.getSystemService(Context.FILE_INTEGRITY_SERVICE);
mService = mService =
new AppIntegrityManagerServiceImpl( new AppIntegrityManagerServiceImpl(
mMockContext, mMockContext,
mPackageManagerInternal, mPackageManagerInternal,
mRuleEvaluationEngine, mRuleEvaluationEngine,
mIntegrityFileManager, mIntegrityFileManager,
mFileIntegrityManager,
mHandler); mHandler);
mSpyPackageManager = spy(mRealContext.getPackageManager()); mSpyPackageManager = spy(mRealContext.getPackageManager());
// setup mocks to prevent NPE // setup mocks to prevent NPE
when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager); when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
when(mMockContext.getResources()).thenReturn(mMockResources); when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{}); when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
when(mIntegrityFileManager.initialized()).thenReturn(true); when(mIntegrityFileManager.initialized()).thenReturn(true);
// These are needed to override the Settings.Global.get result. // These are needed to override the Settings.Global.get result.
when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver()); when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
@@ -181,6 +192,7 @@ public class AppIntegrityManagerServiceImplTest {
public void tearDown() throws Exception { public void tearDown() throws Exception {
mTestApk.delete(); mTestApk.delete();
mTestApkTwoCerts.delete(); mTestApkTwoCerts.delete();
mTestApkSourceStamp.delete();
} }
@Test @Test
@@ -241,7 +253,8 @@ public class AppIntegrityManagerServiceImplTest {
IntentSender mockReceiver = mock(IntentSender.class); IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules = List<Rule> rules =
Arrays.asList( Arrays.asList(
new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), new Rule(
IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
Rule.DENY)); Rule.DENY));
mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
@@ -261,7 +274,8 @@ public class AppIntegrityManagerServiceImplTest {
IntentSender mockReceiver = mock(IntentSender.class); IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules = List<Rule> rules =
Arrays.asList( Arrays.asList(
new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), new Rule(
IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
Rule.DENY)); Rule.DENY));
mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
@@ -305,8 +319,7 @@ public class AppIntegrityManagerServiceImplTest {
ArgumentCaptor<AppInstallMetadata> metadataCaptor = ArgumentCaptor<AppInstallMetadata> metadataCaptor =
ArgumentCaptor.forClass(AppInstallMetadata.class); ArgumentCaptor.forClass(AppInstallMetadata.class);
verify(mRuleEvaluationEngine) verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
.evaluate(metadataCaptor.capture());
AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName()); assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT); assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
@@ -341,8 +354,33 @@ public class AppIntegrityManagerServiceImplTest {
ArgumentCaptor.forClass(AppInstallMetadata.class); ArgumentCaptor.forClass(AppInstallMetadata.class);
verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
assertThat(appInstallMetadata.getAppCertificates()).containsExactly( assertThat(appInstallMetadata.getAppCertificates())
DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2); .containsExactly(DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2);
}
@Test
public void handleBroadcast_correctArgs_sourceStamp() throws Exception {
whitelistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext)
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent();
intent.setDataAndType(Uri.fromFile(mTestApkSourceStamp), PACKAGE_MIME_TYPE);
when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
ArgumentCaptor<AppInstallMetadata> metadataCaptor =
ArgumentCaptor.forClass(AppInstallMetadata.class);
verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
assertTrue(appInstallMetadata.isStampPresent());
assertTrue(appInstallMetadata.isStampVerified());
assertFalse(appInstallMetadata.isStampTrusted());
assertEquals(SOURCE_STAMP_CERTIFICATE_HASH, appInstallMetadata.getStampCertificateHash());
} }
@Test @Test
@@ -445,7 +483,7 @@ public class AppIntegrityManagerServiceImplTest {
private void whitelistUsAsRuleProvider() { private void whitelistUsAsRuleProvider() {
Resources mockResources = mock(Resources.class); Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages)) when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
.thenReturn(new String[]{TEST_FRAMEWORK_PACKAGE}); .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
when(mMockContext.getResources()).thenReturn(mockResources); when(mMockContext.getResources()).thenReturn(mockResources);
} }
@@ -478,8 +516,8 @@ public class AppIntegrityManagerServiceImplTest {
PackageInfo packageInfo = PackageInfo packageInfo =
mRealContext mRealContext
.getPackageManager() .getPackageManager()
.getPackageInfo(TEST_FRAMEWORK_PACKAGE, .getPackageInfo(
PackageManager.GET_SIGNING_CERTIFICATES); TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNING_CERTIFICATES);
doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt()); doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt()); doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
return makeVerificationIntent(INSTALLER); return makeVerificationIntent(INSTALLER);
@@ -501,10 +539,16 @@ public class AppIntegrityManagerServiceImplTest {
private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception { private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception {
int value = shouldInclude ? 1 : 0; int value = shouldInclude ? 1 : 0;
Settings.Global.putInt(mRealContext.getContentResolver(), Settings.Global.putInt(
Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, value); mRealContext.getContentResolver(),
assertThat(Settings.Global.getInt(mRealContext.getContentResolver(), Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, -1) == 1).isEqualTo( value);
shouldInclude); assertThat(
Settings.Global.getInt(
mRealContext.getContentResolver(),
Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
-1)
== 1)
.isEqualTo(shouldInclude);
} }
} }