diff --git a/api/system-current.txt b/api/system-current.txt index a722db95fdd60..a428d13fc6d9e 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1922,18 +1922,22 @@ package android.content.integrity { public abstract class IntegrityFormula { method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long); method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED; - field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE; + } + + public static final class IntegrityFormula.Application { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); + } + + public static final class IntegrityFormula.Installer { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); } public final class Rule implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index e352cb65156ea..78a0b0a2046c9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -812,18 +812,22 @@ package android.content.integrity { public abstract class IntegrityFormula { method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean); - method @NonNull public android.content.integrity.IntegrityFormula equalTo(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long); - method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long); method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE; - field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME; - field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED; - field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE; + } + + public static final class IntegrityFormula.Application { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); + method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); + } + + public static final class IntegrityFormula.Installer { + method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); + method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); + method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); } public final class Rule implements android.os.Parcelable { diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java index cd5117be61233..4be7e6df46e0a 100644 --- a/core/java/android/content/integrity/AppInstallMetadata.java +++ b/core/java/android/content/integrity/AppInstallMetadata.java @@ -18,7 +18,9 @@ package android.content.integrity; import android.annotation.NonNull; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -40,6 +42,7 @@ public final class AppInstallMetadata { private final List mInstallerCertificates; private final long mVersionCode; private final boolean mIsPreInstalled; + private final Map mAllowedInstallersAndCertificates; private AppInstallMetadata(Builder builder) { this.mPackageName = builder.mPackageName; @@ -48,6 +51,7 @@ public final class AppInstallMetadata { this.mInstallerCertificates = builder.mInstallerCertificates; this.mVersionCode = builder.mVersionCode; this.mIsPreInstalled = builder.mIsPreInstalled; + this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates; } @NonNull @@ -80,6 +84,13 @@ public final class AppInstallMetadata { return mIsPreInstalled; } + /** + * Get the allowed installers and their corresponding cert. + */ + public Map getAllowedInstallersAndCertificates() { + return mAllowedInstallersAndCertificates; + } + @Override public String toString() { return String.format( @@ -101,6 +112,23 @@ public final class AppInstallMetadata { private List mInstallerCertificates; private long mVersionCode; private boolean mIsPreInstalled; + private Map mAllowedInstallersAndCertificates; + + public Builder() { + mAllowedInstallersAndCertificates = new HashMap<>(); + } + + /** + * Add allowed installers and cert. + * + * @see AppInstallMetadata#getAllowedInstallersAndCertificates() + */ + @NonNull + public Builder setAllowedInstallersAndCert( + @NonNull Map allowedInstallersAndCertificates) { + this.mAllowedInstallersAndCertificates = allowedInstallersAndCertificates; + return this; + } /** * Set package name of the app to be installed. diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index 42459779e212d..d911eabc3b839 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -55,14 +55,12 @@ public abstract class AtomicFormula extends IntegrityFormula { PRE_INSTALLED, }) @Retention(RetentionPolicy.SOURCE) - public @interface Key { - } + public @interface Key {} /** @hide */ @IntDef(value = {EQ, GT, GTE}) @Retention(RetentionPolicy.SOURCE) - public @interface Operator { - } + public @interface Operator {} /** * Package name of the app. @@ -354,7 +352,8 @@ public abstract class AtomicFormula extends IntegrityFormula { "Key %s cannot be used with StringAtomicFormula", keyToString(key))); mValue = hashValue(key, value); mIsHashedValue = - key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE + key == APP_CERTIFICATE + || key == INSTALLER_CERTIFICATE ? true : !mValue.equals(value); } diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java new file mode 100644 index 0000000000000..475f019e7b261 --- /dev/null +++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.integrity; + +import java.util.Map; + +/** + * An atomic formula that evaluates to true if the installer of the current install is specified in + * the "allowed installer" field in the android manifest. Note that an empty "allowed installer" by + * default means containing all possible installers. + * + * @hide + */ +public class InstallerAllowedByManifestFormula extends IntegrityFormula { + + @Override + public int getTag() { + return IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG; + } + + @Override + public boolean matches(AppInstallMetadata appInstallMetadata) { + Map allowedInstallersAndCertificates = + appInstallMetadata.getAllowedInstallersAndCertificates(); + return allowedInstallersAndCertificates.isEmpty() + || installerInAllowedInstallersFromManifest( + appInstallMetadata, allowedInstallersAndCertificates); + } + + @Override + public boolean isAppCertificateFormula() { + return false; + } + + @Override + public boolean isInstallerFormula() { + return true; + } + + private static boolean installerInAllowedInstallersFromManifest( + AppInstallMetadata appInstallMetadata, + Map allowedInstallersAndCertificates) { + return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName()) + && appInstallMetadata.getInstallerCertificates() + .contains( + allowedInstallersAndCertificates + .get(appInstallMetadata.getInstallerName())); + } +} diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java index a2d937e4df317..ac4c9071f7553 100644 --- a/core/java/android/content/integrity/IntegrityFormula.java +++ b/core/java/android/content/integrity/IntegrityFormula.java @@ -42,66 +42,88 @@ import java.util.Arrays; @VisibleForTesting public abstract class IntegrityFormula { - /** - * A static formula base for package name formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula PACKAGE_NAME = - new StringAtomicFormula(AtomicFormula.PACKAGE_NAME); + /** Factory class for creating integrity formulas based on the app being installed. */ + public static final class Application { + /** Returns an integrity formula that checks the equality to a package name. */ + @NonNull + public static IntegrityFormula packageNameEquals(@NonNull String packageName) { + return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName); + } - /** - * A static formula base for app certificate formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula APP_CERTIFICATE = - new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE); + /** + * Returns an integrity formula that checks if the app certificates contain {@code + * appCertificate}. + */ + @NonNull + public static IntegrityFormula certificatesContain(@NonNull String appCertificate) { + return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate); + } - /** - * A static formula base for installer name formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula INSTALLER_NAME = - new StringAtomicFormula(AtomicFormula.INSTALLER_NAME); + /** Returns an integrity formula that checks the equality to a version code. */ + @NonNull + public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) { + return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode); + } - /** - * A static formula base for installer certificate formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula INSTALLER_CERTIFICATE = - new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE); + /** + * Returns an integrity formula that checks the app's version code is greater than the + * provided value. + */ + @NonNull + public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) { + return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode); + } - /** - * A static formula base for version code name formulas. - * - * This formulation is incomplete and should always be used with {@code equals}, - * {@code greaterThan} and {@code greaterThanEquals} formulation. Evaluates to false when used - * directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula VERSION_CODE = - new LongAtomicFormula(AtomicFormula.VERSION_CODE); + /** + * Returns an integrity formula that checks the app's version code is greater than or equal + * to the provided value. + */ + @NonNull + public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) { + return new LongAtomicFormula( + AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode); + } - /** - * A static formula base for pre-installed status formulas. - * - * This formulation is incomplete and should always be used with {@code equals} formulation. - * Evaluates to false when used directly and cannot be written as a parcel. - */ - @NonNull - public static final IntegrityFormula PRE_INSTALLED = - new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED); + /** Returns an integrity formula that is valid when app is pre-installed. */ + @NonNull + public static IntegrityFormula isPreInstalled() { + return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); + } + + private Application() { + } + } + + /** Factory class for creating integrity formulas based on installer. */ + public static final class Installer { + /** Returns an integrity formula that checks the equality to an installer name. */ + @NonNull + public static IntegrityFormula packageNameEquals(@NonNull String installerName) { + return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName); + } + + /** + * An static formula that evaluates to true if the installer is NOT allowed according to the + * "allowed installer" field in the android manifest. + */ + @NonNull + public static IntegrityFormula notAllowedByManifest() { + return not(new InstallerAllowedByManifestFormula()); + } + + /** + * Returns an integrity formula that checks if the installer certificates contain {@code + * installerCertificate}. + */ + @NonNull + public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) { + return new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE, + installerCertificate); + } + + private Installer() { + } + } /** @hide */ @IntDef( @@ -109,10 +131,12 @@ public abstract class IntegrityFormula { COMPOUND_FORMULA_TAG, STRING_ATOMIC_FORMULA_TAG, LONG_ATOMIC_FORMULA_TAG, - BOOLEAN_ATOMIC_FORMULA_TAG + BOOLEAN_ATOMIC_FORMULA_TAG, + INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG }) @Retention(RetentionPolicy.SOURCE) - @interface Tag {} + @interface Tag { + } /** @hide */ public static final int COMPOUND_FORMULA_TAG = 0; @@ -122,6 +146,8 @@ public abstract class IntegrityFormula { public static final int LONG_ATOMIC_FORMULA_TAG = 2; /** @hide */ public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3; + /** @hide */ + public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4; /** * Returns the tag that identifies the current class. @@ -135,14 +161,14 @@ public abstract class IntegrityFormula { * * @hide */ - public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata); + public abstract boolean matches(AppInstallMetadata appInstallMetadata); /** * Returns true when the formula (or one of its atomic formulas) has app certificate as key. * * @hide */ - public abstract @Tag boolean isAppCertificateFormula(); + public abstract boolean isAppCertificateFormula(); /** * Returns true when the formula (or one of its atomic formulas) has installer package name @@ -150,7 +176,7 @@ public abstract class IntegrityFormula { * * @hide */ - public abstract @Tag boolean isInstallerFormula(); + public abstract boolean isInstallerFormula(); /** * Write an {@link IntegrityFormula} to {@link android.os.Parcel}. @@ -159,7 +185,6 @@ public abstract class IntegrityFormula { * {@link Parcelable}. * * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass - * * @hide */ public static void writeToParcel( @@ -194,70 +219,6 @@ public abstract class IntegrityFormula { } } - /** - * Returns an integrity formula that evaluates to true when value of the key matches to the - * provided string value. - * - *

The value will be hashed with SHA256 and the hex digest will be computed; for - * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value is less than - * 32 characters. - * - *

Throws an {@link IllegalArgumentException} if the key is not string typed. - */ - @NonNull - public IntegrityFormula equalTo(@NonNull String value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.StringAtomicFormula(baseFormula.getKey(), value); - } - - /** - * Returns an integrity formula that evaluates to true when the boolean value of the key matches - * the provided boolean value. It can only be used with the boolean comparison keys. - * - *

Throws an {@link IllegalArgumentException} if the key is not boolean typed. - */ - @NonNull - public IntegrityFormula equalTo(boolean value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.BooleanAtomicFormula(baseFormula.getKey(), value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is equal to {@code value}. - * - *

Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula equalTo(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.EQ, value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is greater than {@code value}. - * - *

Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula greaterThan(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GT, value); - } - - /** - * Returns a formula that evaluates to true when the value of the key in the package being - * installed is greater than or equals to the {@code value}. - * - *

Throws an {@link IllegalArgumentException} if the key is not long typed. - */ - @NonNull - public IntegrityFormula greaterThanOrEquals(long value) { - AtomicFormula baseFormula = (AtomicFormula) this; - return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GTE, value); - } - /** * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to * true. diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java new file mode 100644 index 0000000000000..c897ace0e0b52 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.integrity; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableMap; + +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; + +public class InstallerAllowedByManifestFormulaTest { + + private static final InstallerAllowedByManifestFormula + FORMULA = new InstallerAllowedByManifestFormula(); + + @Test + public void testFormulaMatches_installerAndCertBothInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert1", + "installer2", "installer_cert2" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + + @Test + public void testFormulaMatches_installerAndCertDoesNotMatchInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_installerNotInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer3") + .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_certificateNotInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", "installer_cert2", + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isFalse(); + } + + @Test + public void testFormulaMatches_emptyManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of()).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + + /** Returns a builder with all fields filled with some dummy data. */ + private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { + return new AppInstallMetadata.Builder() + .setPackageName("abc") + .setAppCertificates(Collections.emptyList()) + .setInstallerCertificates(Collections.emptyList()) + .setInstallerName("abc") + .setVersionCode(-1) + .setIsPreInstalled(true); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java index 75ef1f22b8194..62c9c98f4e1d3 100644 --- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java @@ -20,8 +20,6 @@ import static android.content.integrity.IntegrityFormula.COMPOUND_FORMULA_TAG; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.assertThrows; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,8 +30,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_packageName() { String packageName = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); + IntegrityFormula formula = IntegrityFormula.Application.packageNameEquals(packageName); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -46,8 +43,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_appCertificate() { String appCertificate = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.APP_CERTIFICATE.equalTo(appCertificate); + IntegrityFormula formula = IntegrityFormula.Application.certificatesContain(appCertificate); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -60,8 +56,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_installerName() { String installerName = "com.test.app"; - IntegrityFormula formula = - IntegrityFormula.INSTALLER_NAME.equalTo(installerName); + IntegrityFormula formula = IntegrityFormula.Installer.packageNameEquals(installerName); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -75,7 +70,7 @@ public class IntegrityFormulaTest { public void createEqualsFormula_installerCertificate() { String installerCertificate = "com.test.app"; IntegrityFormula formula = - IntegrityFormula.INSTALLER_CERTIFICATE.equalTo(installerCertificate); + IntegrityFormula.Installer.certificatesContain(installerCertificate); AtomicFormula.StringAtomicFormula stringAtomicFormula = (AtomicFormula.StringAtomicFormula) formula; @@ -88,8 +83,7 @@ public class IntegrityFormulaTest { @Test public void createEqualsFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.equalTo(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeEquals(versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -99,25 +93,10 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.EQ); } - @Test - public void createEqualsFormula_invalidKeyTypeForStringParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PRE_INSTALLED.equalTo("wrongString")); - } - - @Test - public void createEqualsFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.equalTo(12)); - } - @Test public void createGreaterThanFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.greaterThan(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThan(versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -127,18 +106,11 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.GT); } - @Test - public void createGreaterThanFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.greaterThan(12)); - } - @Test public void createGreaterThanOrEqualsToFormula_versionCode() { int versionCode = 12; - IntegrityFormula formula = - IntegrityFormula.VERSION_CODE.greaterThanOrEquals(versionCode); + IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo( + versionCode); AtomicFormula.LongAtomicFormula stringAtomicFormula = (AtomicFormula.LongAtomicFormula) formula; @@ -148,16 +120,9 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getOperator()).isEqualTo(AtomicFormula.GTE); } - @Test - public void createGreaterThanOrEqualsToFormula_invalidKeyTypeForLongParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.greaterThanOrEquals(12)); - } - @Test public void createIsTrueFormula_preInstalled() { - IntegrityFormula formula = IntegrityFormula.PRE_INSTALLED.equalTo(true); + IntegrityFormula formula = IntegrityFormula.Application.isPreInstalled(); AtomicFormula.BooleanAtomicFormula stringAtomicFormula = (AtomicFormula.BooleanAtomicFormula) formula; @@ -166,21 +131,13 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getValue()).isTrue(); } - @Test - public void createIsTrueFormula_invalidKeyTypeForBoolParameter() { - assertThrows( - IllegalArgumentException.class, - () -> IntegrityFormula.PACKAGE_NAME.equalTo(true)); - } - @Test public void createAllFormula() { String packageName = "com.test.package"; String certificateName = "certificate"; - IntegrityFormula formula1 = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); - IntegrityFormula formula2 = - IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName); + IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName); + IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain( + certificateName); IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2); @@ -191,10 +148,9 @@ public class IntegrityFormulaTest { public void createAnyFormula() { String packageName = "com.test.package"; String certificateName = "certificate"; - IntegrityFormula formula1 = - IntegrityFormula.PACKAGE_NAME.equalTo(packageName); - IntegrityFormula formula2 = - IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName); + IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName); + IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain( + certificateName); IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2); @@ -206,8 +162,7 @@ public class IntegrityFormulaTest { String packageName = "com.test.package"; IntegrityFormula compoundFormula = - IntegrityFormula.not( - IntegrityFormula.PACKAGE_NAME.equalTo(packageName)); + IntegrityFormula.not(IntegrityFormula.Application.packageNameEquals(packageName)); assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG); } diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java index c3899638f40ff..94e6708c3038a 100644 --- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java +++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java @@ -29,13 +29,14 @@ public final class ComponentBitSize { public static final int KEY_BITS = 4; public static final int OPERATOR_BITS = 3; public static final int CONNECTOR_BITS = 2; - public static final int SEPARATOR_BITS = 2; + public static final int SEPARATOR_BITS = 3; public static final int VALUE_SIZE_BITS = 8; public static final int IS_HASHED_BITS = 1; public static final int ATOMIC_FORMULA_START = 0; public static final int COMPOUND_FORMULA_START = 1; public static final int COMPOUND_FORMULA_END = 2; + public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3; public static final int DEFAULT_FORMAT_VERSION = 1; public static final int SIGNAL_BIT = 1; diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index 4b8efafcb6b0f..11e8d91dde124 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -23,6 +23,7 @@ import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMU import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; +import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS; import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; @@ -35,6 +36,7 @@ import static com.android.server.integrity.parser.BinaryFileOperations.getString import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; +import android.content.integrity.InstallerAllowedByManifestFormula; import android.content.integrity.IntegrityFormula; import android.content.integrity.Rule; @@ -140,6 +142,8 @@ public class RuleBinaryParser implements RuleParser { return parseCompoundFormula(bitInputStream); case COMPOUND_FORMULA_END: return null; + case INSTALLER_ALLOWED_BY_MANIFEST_START: + return new InstallerAllowedByManifestFormula(); default: throw new IllegalArgumentException( String.format("Unknown formula separator: %s", separator)); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java index 00e054596cd70..8ba5870aef0f3 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -23,6 +23,7 @@ import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION; import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; +import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; @@ -36,6 +37,7 @@ import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAG import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; +import android.content.integrity.InstallerAllowedByManifestFormula; import android.content.integrity.IntegrityFormula; import android.content.integrity.IntegrityUtils; import android.content.integrity.Rule; @@ -202,6 +204,8 @@ public class RuleBinarySerializer implements RuleSerializer { serializeAtomicFormula((AtomicFormula) formula, bitOutputStream); } else if (formula instanceof CompoundFormula) { serializeCompoundFormula((CompoundFormula) formula, bitOutputStream); + } else if (formula instanceof InstallerAllowedByManifestFormula) { + bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START); } else { throw new IllegalArgumentException( String.format("Invalid formula type: %s", formula.getClass())); diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java index 6f7d172aabcc8..e7235591fb9b9 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java @@ -84,6 +84,7 @@ class RuleIndexingDetailsIdentifier { return getIndexingDetailsForStringAtomicFormula( (AtomicFormula.StringAtomicFormula) formula); case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG: + case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG: case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG: // Package name and app certificate related formulas are string atomic formulas. return new RuleIndexingDetails(NOT_INDEXED); diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index d40130a62fd9f..8dae48cafd7b2 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -211,7 +211,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List rules = Arrays.asList( - new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY)); + new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); runJobInHandler(); @@ -230,7 +231,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List rules = Arrays.asList( - new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY)); + new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); runJobInHandler(); @@ -390,7 +392,7 @@ public class AppIntegrityManagerServiceImplTest { public void getCurrentRules() throws Exception { whitelistUsAsRuleProvider(); makeUsSystemApp(); - Rule rule = new Rule(IntegrityFormula.PACKAGE_NAME.equalTo("package"), Rule.DENY); + Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY); when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule)); assertThat(mService.getCurrentRules().getList()).containsExactly(rule); diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index 38cf562f8c5b4..3dc26afdb9af3 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -58,7 +58,7 @@ public class RuleBinaryParserTest { getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS); private static final String ATOMIC_FORMULA_START_BITS = getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS); - private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3; + private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1; private static final String INVALID_FORMULA_SEPARATOR_BITS = getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS); diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java index 913aff7daaa96..ea9e6ff867289 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java @@ -112,6 +112,7 @@ public class RuleIndexingDetailsIdentifierTest { ATOMIC_FORMULA_WITH_VERSION_CODE, ATOMIC_FORMULA_WITH_ISPREINSTALLED)), Rule.DENY); + public static final int INVALID_FORMULA_TAG = -1; @Test public void getIndexType_nullRule() { @@ -290,7 +291,7 @@ public class RuleIndexingDetailsIdentifierTest { return new AtomicFormula(0) { @Override public int getTag() { - return 4; + return INVALID_FORMULA_TAG; } @Override