diff options
3 files changed, 106 insertions, 120 deletions
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 f964d4cf2724..d3588d38d976 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java @@ -48,6 +48,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -82,28 +83,28 @@ public class RuleBinarySerializer implements RuleSerializer { Map<Integer, TreeMap<String, List<Rule>>> indexedRules = RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules); + // Serialize the rules. ByteTrackedOutputStream ruleFileByteTrackedOutputStream = new ByteTrackedOutputStream(rulesFileOutputStream); - serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream); - Map<String, Integer> packageNameIndexes = serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED), ruleFileByteTrackedOutputStream); - indexingFileOutputStream.write( - serializeIndexes(packageNameIndexes, /* isIndexed= */true)); - Map<String, Integer> appCertificateIndexes = serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED), ruleFileByteTrackedOutputStream); - indexingFileOutputStream.write( - serializeIndexes(appCertificateIndexes, /* isIndexed= */true)); - Map<String, Integer> unindexedRulesIndexes = serializeRuleList(indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream); - indexingFileOutputStream.write( - serializeIndexes(unindexedRulesIndexes, /* isIndexed= */false)); + + // Serialize their indexes. + BitOutputStream indexingBitOutputStream = new BitOutputStream(); + serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */true); + serializeIndexGroup(appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ + true); + serializeIndexGroup(unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ + false); + indexingFileOutputStream.write(indexingBitOutputStream.toByteArray()); } catch (Exception e) { throw new RuleSerializeException(e.getMessage(), e); } @@ -126,7 +127,7 @@ public class RuleBinarySerializer implements RuleSerializer { "serializeRuleList should never be called with null rule list."); BitOutputStream bitOutputStream = new BitOutputStream(); - Map<String, Integer> indexMapping = new TreeMap(); + Map<String, Integer> indexMapping = new LinkedHashMap(); int indexTracker = 0; indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount()); @@ -220,8 +221,8 @@ public class RuleBinarySerializer implements RuleSerializer { } } - private byte[] serializeIndexes(Map<String, Integer> indexes, boolean isIndexed) { - BitOutputStream bitOutputStream = new BitOutputStream(); + private void serializeIndexGroup( + Map<String, Integer> indexes, BitOutputStream bitOutputStream, boolean isIndexed) { // Output the starting location of this indexing group. serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */false, @@ -244,7 +245,8 @@ public class RuleBinarySerializer implements RuleSerializer { serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream); serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream); - return bitOutputStream.toByteArray(); + // This dummy bit is set for fixing the padding issue. songpan@ will fix it and remove it. + bitOutputStream.setNext(); } private void serializeStringValue( diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java index 63189e7546e2..dd69c6613ab2 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java @@ -16,14 +16,7 @@ package com.android.server.integrity; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasItems; -import static org.junit.Assert.assertThat; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; import android.content.integrity.AppInstallMetadata; import android.content.integrity.AtomicFormula; @@ -33,26 +26,26 @@ import android.content.integrity.CompoundFormula; import android.content.integrity.Rule; import android.util.Slog; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.integrity.parser.RuleXmlParser; -import com.android.server.integrity.serializer.RuleXmlSerializer; +import com.android.server.integrity.parser.RuleBinaryParser; +import com.android.server.integrity.serializer.RuleBinarySerializer; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; /** Unit test for {@link IntegrityFileManager} */ -@RunWith(AndroidJUnit4.class) +@RunWith(JUnit4.class) public class IntegrityFileManagerTest { private static final String TAG = "IntegrityFileManagerTest"; @@ -72,7 +65,7 @@ public class IntegrityFileManagerTest { // Use Xml Parser/Serializer to help with debugging since we can just print the file. mIntegrityFileManager = new IntegrityFileManager( - new RuleXmlParser(), new RuleXmlSerializer(), mTmpDir); + new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir); Files.walk(mTmpDir.toPath()) .forEach( path -> { @@ -97,12 +90,19 @@ public class IntegrityFileManagerTest { @Test public void testGetMetadata() throws Exception { - assertNull(mIntegrityFileManager.readMetadata()); + assertThat(mIntegrityFileManager.readMetadata()).isNull(); mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - assertNotNull(mIntegrityFileManager.readMetadata()); - assertEquals(VERSION, mIntegrityFileManager.readMetadata().getVersion()); - assertEquals(RULE_PROVIDER, mIntegrityFileManager.readMetadata().getRuleProvider()); + assertThat(mIntegrityFileManager.readMetadata()).isNotNull(); + assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION); + assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER); + } + + @Test + public void testIsInitialized() throws Exception { + assertThat(mIntegrityFileManager.initialized()).isFalse(); + mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); + assertThat(mIntegrityFileManager.initialized()).isTrue(); } @Test @@ -110,20 +110,8 @@ public class IntegrityFileManagerTest { String packageName = "package"; String packageCert = "cert"; int version = 123; - Rule packageNameRule = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - Rule.DENY); - Rule packageCertRule = - new Rule( - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - packageCert, - /* isHashedValue= */ false), - Rule.DENY); + Rule packageNameRule = getPackageNameIndexedRule(packageName); + Rule packageCertRule = getAppCertificateIndexedRule(packageCert); Rule versionCodeRule = new Rule( new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, version), @@ -142,9 +130,7 @@ public class IntegrityFileManagerTest { AtomicFormula.LE, version))), Rule.DENY); - // We will test the specifics of indexing in other classes. Here, we just require that all - // rules that are related to the given AppInstallMetadata are returned and do not assert - // anything on other rules. + List<Rule> rules = Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule); mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); @@ -159,17 +145,77 @@ public class IntegrityFileManagerTest { .build(); List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); - assertThat(rulesFetched, hasItems( - equalTo(packageNameRule), - equalTo(packageCertRule), - equalTo(versionCodeRule) - )); + assertThat(rulesFetched) + .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule); } @Test - public void testIsInitialized() throws Exception { - assertFalse(mIntegrityFileManager.initialized()); - mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST); - assertTrue(mIntegrityFileManager.initialized()); + public void testGetRules_indexedForManyRules() throws Exception { + String packageName = "package"; + String installerName = "installer"; + String appCertificate = "cert"; + + // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and + // 500 unindexed rules. + List<Rule> rules = new ArrayList<>(); + + for (int i = 0; i < 2500; i++) { + rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i))); + rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i))); + } + + for (int i = 0; i < 70; i++) { + rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i))); + } + + // Write the rules and get them indexed. + mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules); + + // Read the rules for a specific rule. + String installedPackageName = String.format("%s%04d", packageName, 264); + String installedAppCertificate = String.format("%s%04d", appCertificate, 1264); + AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder() + .setPackageName(installedPackageName) + .setAppCertificate(installedAppCertificate) + .setVersionCode(250) + .setInstallerName("abc") + .setInstallerCertificate("abc") + .setIsPreInstalled(true) + .build(); + List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata); + + // Verify that we do not load all the rules and we have the necessary rules to evaluate. + assertThat(rulesFetched.size()).isEqualTo(170); + assertThat(rulesFetched) + .containsAllOf( + getPackageNameIndexedRule(installedPackageName), + getAppCertificateIndexedRule(installedAppCertificate)); + } + + private Rule getPackageNameIndexedRule(String packageName) { + return new Rule( + new StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + packageName, + /* isHashedValue= */ false), + Rule.DENY); + } + + private Rule getAppCertificateIndexedRule(String appCertificate) { + return new Rule( + new StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE, + appCertificate, + /* isHashedValue= */ false), + Rule.DENY); + } + + private Rule getInstallerCertificateRule(String installerCert) { + return new Rule( + new StringAtomicFormula( + AtomicFormula.INSTALLER_NAME, + installerCert, + /* isHashedValue= */ false), + Rule.DENY); } } 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 51f5c755754c..881b3d6bba52 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 @@ -48,7 +48,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -656,65 +655,4 @@ public class RuleBinaryParserTest { /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit", () -> binaryParser.parse(rule.array())); } - - @Test - public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly() - throws Exception { - String packageName2 = "com.test.2"; - - byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1")); - byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2)); - byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3")); - - ByteBuffer rule = - ByteBuffer.allocate( - DEFAULT_FORMAT_VERSION_BYTES.length - + ruleBytes1.length - + ruleBytes2.length - + ruleBytes3.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes1); - rule.put(ruleBytes2); - rule.put(ruleBytes3); - InputStream inputStream = new ByteArrayInputStream(rule.array()); - - RuleParser binaryParser = new RuleBinaryParser(); - - List<RuleIndexRange> indexRanges = new ArrayList<>(); - indexRanges.add( - new RuleIndexRange( - DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length, - DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length - + ruleBytes2.length)); - List<Rule> rules = binaryParser.parse(inputStream, indexRanges); - - Rule expectedRule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName2, - /* isHashedValue= */ false))), - Rule.DENY); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - private static String getRulesWithPackageName(String packageName) { - return START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - - } } |