Merge "Extract source stamp during app install" into rvc-dev am: 759dbb7cf6
Change-Id: Ia7aee5979b5efd2ec99fe6bd026e1b522af84f08
This commit is contained in:
@@ -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}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user