Loading services/core/java/com/android/server/integrity/serializer/RuleIndexTypeIdentifier.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 com.android.server.integrity.serializer; import android.annotation.IntDef; import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; import android.content.integrity.Formula; import android.content.integrity.Rule; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** A helper class for identifying the indexing type of a given rule. */ public class RuleIndexTypeIdentifier { static final int NOT_INDEXED = 0; static final int PACKAGE_NAME_INDEXED = 1; static final int APP_CERTIFICATE_INDEXED = 2; /** Represents which indexed file the rule should be located. */ @IntDef( value = { NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED }) @Retention(RetentionPolicy.SOURCE) public @interface IndexType { } /** Determines the indexing file type that a given rule should be located at. */ public static int getIndexType(Rule rule) { if (rule == null) { throw new IllegalArgumentException("Indexing type cannot be determined for null rule."); } return getIndexType(rule.getFormula()); } private static int getIndexType(Formula formula) { if (formula == null) { throw new IllegalArgumentException( "Indexing type cannot be determined for null formula."); } switch (formula.getTag()) { case Formula.COMPOUND_FORMULA_TAG: return getIndexTypeForCompoundFormula((CompoundFormula) formula); case Formula.STRING_ATOMIC_FORMULA_TAG: return getIndexTypeForAtomicStringFormula((AtomicFormula) formula); case Formula.INT_ATOMIC_FORMULA_TAG: case Formula.BOOLEAN_ATOMIC_FORMULA_TAG: // Package name and app certificate related formulas are string atomic formulas. return NOT_INDEXED; default: throw new IllegalArgumentException( String.format("Invalid formula tag type: %s", formula.getTag())); } } private static int getIndexTypeForCompoundFormula(CompoundFormula compoundFormula) { int connector = compoundFormula.getConnector(); List<Formula> formulas = compoundFormula.getFormulas(); switch (connector) { case CompoundFormula.NOT: // Having a NOT operator in the indexing messes up the indexing; e.g., deny // installation if app certificate is NOT X (should not be indexed with app cert // X). We will not keep these rules indexed. return NOT_INDEXED; case CompoundFormula.AND: case CompoundFormula.OR: Set<Integer> indexingTypesForAllFormulas = formulas.stream() .map(formula -> getIndexType(formula)) .collect(Collectors.toSet()); if (indexingTypesForAllFormulas.contains(PACKAGE_NAME_INDEXED)) { return PACKAGE_NAME_INDEXED; } else if (indexingTypesForAllFormulas.contains(APP_CERTIFICATE_INDEXED)) { return APP_CERTIFICATE_INDEXED; } else { return NOT_INDEXED; } default: return NOT_INDEXED; } } private static int getIndexTypeForAtomicStringFormula(AtomicFormula atomicFormula) { switch (atomicFormula.getKey()) { case AtomicFormula.PACKAGE_NAME: return PACKAGE_NAME_INDEXED; case AtomicFormula.APP_CERTIFICATE: return APP_CERTIFICATE_INDEXED; default: return NOT_INDEXED; } } } services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexTypeIdentifierTest.java 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 com.android.server.integrity.serializer; import static com.android.server.testutils.TestUtils.assertExpectException; import static com.google.common.truth.Truth.assertThat; import android.content.integrity.AppInstallMetadata; import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; import android.content.integrity.Formula; import android.content.integrity.Rule; import androidx.annotation.NonNull; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Arrays; /** Unit tests for {@link RuleIndexTypeIdentifier}. */ @RunWith(JUnit4.class) public class RuleIndexTypeIdentifierTest { @Test public void getIndexType_nullRule() { Rule rule = null; assertExpectException( IllegalArgumentException.class, /* expectedExceptionMessageRegex= */ "Indexing type cannot be determined for null rule.", () -> RuleIndexTypeIdentifier.getIndexType(rule)); } @Test public void getIndexType_invalidFormula() { Rule rule = new Rule(getInvalidFormula(), Rule.DENY); assertExpectException( IllegalArgumentException.class, /* expectedExceptionMessageRegex= */ "Invalid formula tag type.", () -> RuleIndexTypeIdentifier.getIndexType(rule)); } @Test public void getIndexType_ruleContainingPackageNameFormula() { String packageName = "com.test.app"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.PACKAGE_NAME_INDEXED); } @Test public void getIndexType_ruleContainingAppCertificateFormula() { String appCertificate = "cert1"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.APP_CERTIFICATE, appCertificate, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.APP_CERTIFICATE_INDEXED); } @Test public void getIndexType_ruleWithUnindexedCompoundFormula() { String installerCertificate = "cert1"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_CERTIFICATE, installerCertificate, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED); } @Test public void getIndexType_rulContainingCompoundFormulaWithIntAndBoolean() { int appVersion = 12; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.BooleanAtomicFormula( AtomicFormula.PRE_INSTALLED, /* booleanValue= */ true), new AtomicFormula.IntAtomicFormula( AtomicFormula.VERSION_CODE, AtomicFormula.EQ, appVersion))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED); } @Test public void getIndexType_negatedRuleContainingPackageNameFormula() { String packageName = "com.test.app"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.NOT, Arrays.asList( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED); } private Formula getInvalidFormula() { return new Formula() { @Override public boolean isSatisfied(AppInstallMetadata appInstallMetadata) { return false; } @Override public int getTag() { return 4; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @NonNull @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return super.toString(); } @Override protected void finalize() throws Throwable { super.finalize(); } }; } } Loading
services/core/java/com/android/server/integrity/serializer/RuleIndexTypeIdentifier.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 com.android.server.integrity.serializer; import android.annotation.IntDef; import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; import android.content.integrity.Formula; import android.content.integrity.Rule; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** A helper class for identifying the indexing type of a given rule. */ public class RuleIndexTypeIdentifier { static final int NOT_INDEXED = 0; static final int PACKAGE_NAME_INDEXED = 1; static final int APP_CERTIFICATE_INDEXED = 2; /** Represents which indexed file the rule should be located. */ @IntDef( value = { NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED }) @Retention(RetentionPolicy.SOURCE) public @interface IndexType { } /** Determines the indexing file type that a given rule should be located at. */ public static int getIndexType(Rule rule) { if (rule == null) { throw new IllegalArgumentException("Indexing type cannot be determined for null rule."); } return getIndexType(rule.getFormula()); } private static int getIndexType(Formula formula) { if (formula == null) { throw new IllegalArgumentException( "Indexing type cannot be determined for null formula."); } switch (formula.getTag()) { case Formula.COMPOUND_FORMULA_TAG: return getIndexTypeForCompoundFormula((CompoundFormula) formula); case Formula.STRING_ATOMIC_FORMULA_TAG: return getIndexTypeForAtomicStringFormula((AtomicFormula) formula); case Formula.INT_ATOMIC_FORMULA_TAG: case Formula.BOOLEAN_ATOMIC_FORMULA_TAG: // Package name and app certificate related formulas are string atomic formulas. return NOT_INDEXED; default: throw new IllegalArgumentException( String.format("Invalid formula tag type: %s", formula.getTag())); } } private static int getIndexTypeForCompoundFormula(CompoundFormula compoundFormula) { int connector = compoundFormula.getConnector(); List<Formula> formulas = compoundFormula.getFormulas(); switch (connector) { case CompoundFormula.NOT: // Having a NOT operator in the indexing messes up the indexing; e.g., deny // installation if app certificate is NOT X (should not be indexed with app cert // X). We will not keep these rules indexed. return NOT_INDEXED; case CompoundFormula.AND: case CompoundFormula.OR: Set<Integer> indexingTypesForAllFormulas = formulas.stream() .map(formula -> getIndexType(formula)) .collect(Collectors.toSet()); if (indexingTypesForAllFormulas.contains(PACKAGE_NAME_INDEXED)) { return PACKAGE_NAME_INDEXED; } else if (indexingTypesForAllFormulas.contains(APP_CERTIFICATE_INDEXED)) { return APP_CERTIFICATE_INDEXED; } else { return NOT_INDEXED; } default: return NOT_INDEXED; } } private static int getIndexTypeForAtomicStringFormula(AtomicFormula atomicFormula) { switch (atomicFormula.getKey()) { case AtomicFormula.PACKAGE_NAME: return PACKAGE_NAME_INDEXED; case AtomicFormula.APP_CERTIFICATE: return APP_CERTIFICATE_INDEXED; default: return NOT_INDEXED; } } }
services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexTypeIdentifierTest.java 0 → 100644 +218 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 com.android.server.integrity.serializer; import static com.android.server.testutils.TestUtils.assertExpectException; import static com.google.common.truth.Truth.assertThat; import android.content.integrity.AppInstallMetadata; import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; import android.content.integrity.Formula; import android.content.integrity.Rule; import androidx.annotation.NonNull; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Arrays; /** Unit tests for {@link RuleIndexTypeIdentifier}. */ @RunWith(JUnit4.class) public class RuleIndexTypeIdentifierTest { @Test public void getIndexType_nullRule() { Rule rule = null; assertExpectException( IllegalArgumentException.class, /* expectedExceptionMessageRegex= */ "Indexing type cannot be determined for null rule.", () -> RuleIndexTypeIdentifier.getIndexType(rule)); } @Test public void getIndexType_invalidFormula() { Rule rule = new Rule(getInvalidFormula(), Rule.DENY); assertExpectException( IllegalArgumentException.class, /* expectedExceptionMessageRegex= */ "Invalid formula tag type.", () -> RuleIndexTypeIdentifier.getIndexType(rule)); } @Test public void getIndexType_ruleContainingPackageNameFormula() { String packageName = "com.test.app"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.PACKAGE_NAME_INDEXED); } @Test public void getIndexType_ruleContainingAppCertificateFormula() { String appCertificate = "cert1"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.APP_CERTIFICATE, appCertificate, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.APP_CERTIFICATE_INDEXED); } @Test public void getIndexType_ruleWithUnindexedCompoundFormula() { String installerCertificate = "cert1"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_CERTIFICATE, installerCertificate, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED); } @Test public void getIndexType_rulContainingCompoundFormulaWithIntAndBoolean() { int appVersion = 12; Rule rule = new Rule( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.BooleanAtomicFormula( AtomicFormula.PRE_INSTALLED, /* booleanValue= */ true), new AtomicFormula.IntAtomicFormula( AtomicFormula.VERSION_CODE, AtomicFormula.EQ, appVersion))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED); } @Test public void getIndexType_negatedRuleContainingPackageNameFormula() { String packageName = "com.test.app"; String installerName = "com.test.installer"; Rule rule = new Rule( new CompoundFormula( CompoundFormula.NOT, Arrays.asList( new CompoundFormula( CompoundFormula.AND, Arrays.asList( new AtomicFormula.StringAtomicFormula( AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false), new AtomicFormula.StringAtomicFormula( AtomicFormula.INSTALLER_NAME, installerName, /* isHashedValue= */ false))))), Rule.DENY); assertThat(RuleIndexTypeIdentifier.getIndexType(rule)) .isEqualTo(RuleIndexTypeIdentifier.NOT_INDEXED); } private Formula getInvalidFormula() { return new Formula() { @Override public boolean isSatisfied(AppInstallMetadata appInstallMetadata) { return false; } @Override public int getTag() { return 4; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @NonNull @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return super.toString(); } @Override protected void finalize() throws Throwable { super.finalize(); } }; } }