Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3da32b37 authored by Ömer Yaveroğlu's avatar Ömer Yaveroğlu
Browse files

Remove unused code from the integrity service serializer and parser.

Change-Id: Ic1a44a1fbb3fb15811da2339d96e7f02c1925ff9
parent bbfd7f0e
Loading
Loading
Loading
Loading
+0 −66
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.parser;

import android.annotation.Nullable;
import android.util.Xml;

import com.android.modules.utils.TypedXmlPullParser;
import com.android.server.integrity.model.RuleMetadata;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;

/** Helper class for parsing rule metadata. */
public class RuleMetadataParser {

    public static final String RULE_PROVIDER_TAG = "P";
    public static final String VERSION_TAG = "V";

    /** Parse the rule metadata from an input stream. */
    @Nullable
    public static RuleMetadata parse(InputStream inputStream)
            throws XmlPullParserException, IOException {

        String ruleProvider = "";
        String version = "";

        TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream);

        int eventType;
        while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG) {
                String tag = xmlPullParser.getName();
                switch (tag) {
                    case RULE_PROVIDER_TAG:
                        ruleProvider = xmlPullParser.nextText();
                        break;
                    case VERSION_TAG:
                        version = xmlPullParser.nextText();
                        break;
                    default:
                        throw new IllegalStateException("Unknown tag in metadata: " + tag);
                }
            }
        }

        return new RuleMetadata(ruleProvider, version);
    }
}
+0 −324
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.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
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;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;

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;

import com.android.internal.util.Preconditions;
import com.android.server.integrity.model.BitOutputStream;
import com.android.server.integrity.model.ByteTrackedOutputStream;

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;
import java.util.stream.Collectors;

/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
public class RuleBinarySerializer implements RuleSerializer {
    static final int TOTAL_RULE_SIZE_LIMIT = 200000;
    static final int INDEXED_RULE_SIZE_LIMIT = 100000;
    static final int NONINDEXED_RULE_SIZE_LIMIT = 1000;

    // Get the byte representation for a list of rules.
    @Override
    public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
            throws RuleSerializeException {
        try {
            ByteArrayOutputStream rulesOutputStream = new ByteArrayOutputStream();
            serialize(rules, formatVersion, rulesOutputStream, new ByteArrayOutputStream());
            return rulesOutputStream.toByteArray();
        } catch (Exception e) {
            throw new RuleSerializeException(e.getMessage(), e);
        }
    }

    // Get the byte representation for a list of rules, and write them to an output stream.
    @Override
    public void serialize(
            List<Rule> rules,
            Optional<Integer> formatVersion,
            OutputStream rulesFileOutputStream,
            OutputStream indexingFileOutputStream)
            throws RuleSerializeException {
        try {
            if (rules == null) {
                throw new IllegalArgumentException("Null rules cannot be serialized.");
            }

            if (rules.size() > TOTAL_RULE_SIZE_LIMIT) {
                throw new IllegalArgumentException("Too many rules provided: " + rules.size());
            }

            // Determine the indexing groups and the order of the rules within each indexed group.
            Map<Integer, Map<String, List<Rule>>> indexedRules =
                    RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);

            // Validate the rule blocks are not larger than expected limits.
            verifySize(indexedRules.get(PACKAGE_NAME_INDEXED), INDEXED_RULE_SIZE_LIMIT);
            verifySize(indexedRules.get(APP_CERTIFICATE_INDEXED), INDEXED_RULE_SIZE_LIMIT);
            verifySize(indexedRules.get(NOT_INDEXED), NONINDEXED_RULE_SIZE_LIMIT);

            // Serialize the rules.
            ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
                    new ByteTrackedOutputStream(rulesFileOutputStream);
            serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
            LinkedHashMap<String, Integer> packageNameIndexes =
                    serializeRuleList(
                            indexedRules.get(PACKAGE_NAME_INDEXED),
                            ruleFileByteTrackedOutputStream);
            LinkedHashMap<String, Integer> appCertificateIndexes =
                    serializeRuleList(
                            indexedRules.get(APP_CERTIFICATE_INDEXED),
                            ruleFileByteTrackedOutputStream);
            LinkedHashMap<String, Integer> unindexedRulesIndexes =
                    serializeRuleList(
                            indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream);

            // Serialize their indexes.
            BitOutputStream indexingBitOutputStream = new BitOutputStream(indexingFileOutputStream);
            serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */ true);
            serializeIndexGroup(
                    appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ true);
            serializeIndexGroup(
                    unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ false);
            indexingBitOutputStream.flush();
        } catch (Exception e) {
            throw new RuleSerializeException(e.getMessage(), e);
        }
    }

    private void verifySize(Map<String, List<Rule>> ruleListMap, int ruleSizeLimit) {
        int totalRuleCount =
                ruleListMap.values().stream()
                        .map(list -> list.size())
                        .collect(Collectors.summingInt(Integer::intValue));
        if (totalRuleCount > ruleSizeLimit) {
            throw new IllegalArgumentException(
                    "Too many rules provided in the indexing group. Provided "
                            + totalRuleCount
                            + " limit "
                            + ruleSizeLimit);
        }
    }

    private void serializeRuleFileMetadata(
            Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream)
            throws IOException {
        int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);

        BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
        bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
        bitOutputStream.flush();
    }

    private LinkedHashMap<String, Integer> serializeRuleList(
            Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream)
            throws IOException {
        Preconditions.checkArgument(
                rulesMap != null, "serializeRuleList should never be called with null rule list.");

        BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
        LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap();
        indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());

        List<String> sortedKeys = rulesMap.keySet().stream().sorted().collect(Collectors.toList());
        int indexTracker = 0;
        for (String key : sortedKeys) {
            if (indexTracker >= INDEXING_BLOCK_SIZE) {
                indexMapping.put(key, outputStream.getWrittenBytesCount());
                indexTracker = 0;
            }

            for (Rule rule : rulesMap.get(key)) {
                serializeRule(rule, bitOutputStream);
                bitOutputStream.flush();
                indexTracker++;
            }
        }
        indexMapping.put(END_INDEXING_KEY, outputStream.getWrittenBytesCount());

        return indexMapping;
    }

    private void serializeRule(Rule rule, BitOutputStream bitOutputStream) throws IOException {
        if (rule == null) {
            throw new IllegalArgumentException("Null rule can not be serialized");
        }

        // Start with a '1' bit to mark the start of a rule.
        bitOutputStream.setNext();

        serializeFormula(rule.getFormula(), bitOutputStream);
        bitOutputStream.setNext(EFFECT_BITS, rule.getEffect());

        // End with a '1' bit to mark the end of a rule.
        bitOutputStream.setNext();
    }

    private void serializeFormula(IntegrityFormula formula, BitOutputStream bitOutputStream)
            throws IOException {
        if (formula instanceof AtomicFormula) {
            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()));
        }
    }

    private void serializeCompoundFormula(
            CompoundFormula compoundFormula, BitOutputStream bitOutputStream) throws IOException {
        if (compoundFormula == null) {
            throw new IllegalArgumentException("Null compound formula can not be serialized");
        }

        bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START);
        bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector());
        for (IntegrityFormula formula : compoundFormula.getFormulas()) {
            serializeFormula(formula, bitOutputStream);
        }
        bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END);
    }

    private void serializeAtomicFormula(
            AtomicFormula atomicFormula, BitOutputStream bitOutputStream) throws IOException {
        if (atomicFormula == null) {
            throw new IllegalArgumentException("Null atomic formula can not be serialized");
        }

        bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START);
        bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey());
        if (atomicFormula.getTag() == AtomicFormula.STRING_ATOMIC_FORMULA_TAG) {
            AtomicFormula.StringAtomicFormula stringAtomicFormula =
                    (AtomicFormula.StringAtomicFormula) atomicFormula;
            bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
            serializeStringValue(
                    stringAtomicFormula.getValue(),
                    stringAtomicFormula.getIsHashedValue(),
                    bitOutputStream);
        } else if (atomicFormula.getTag() == AtomicFormula.LONG_ATOMIC_FORMULA_TAG) {
            AtomicFormula.LongAtomicFormula longAtomicFormula =
                    (AtomicFormula.LongAtomicFormula) atomicFormula;
            bitOutputStream.setNext(OPERATOR_BITS, longAtomicFormula.getOperator());
            // TODO(b/147880712): Temporary hack until we support long values in bitOutputStream
            long value = longAtomicFormula.getValue();
            serializeIntValue((int) (value >>> 32), bitOutputStream);
            serializeIntValue((int) value, bitOutputStream);
        } else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) {
            AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
                    (AtomicFormula.BooleanAtomicFormula) atomicFormula;
            bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
            serializeBooleanValue(booleanAtomicFormula.getValue(), bitOutputStream);
        } else {
            throw new IllegalArgumentException(
                    String.format("Invalid atomic formula type: %s", atomicFormula.getClass()));
        }
    }

    private void serializeIndexGroup(
            LinkedHashMap<String, Integer> indexes,
            BitOutputStream bitOutputStream,
            boolean isIndexed)
            throws IOException {
        // Output the starting location of this indexing group.
        serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */ false, bitOutputStream);
        serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);

        // If the group is indexed, output the locations of the indexes.
        if (isIndexed) {
            for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
                if (!entry.getKey().equals(START_INDEXING_KEY)
                        && !entry.getKey().equals(END_INDEXING_KEY)) {
                    serializeStringValue(
                            entry.getKey(), /* isHashedValue= */ false, bitOutputStream);
                    serializeIntValue(entry.getValue(), bitOutputStream);
                }
            }
        }

        // Output the end location of this indexing group.
        serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream);
        serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream);
    }

    private void serializeStringValue(
            String value, boolean isHashedValue, BitOutputStream bitOutputStream)
            throws IOException {
        if (value == null) {
            throw new IllegalArgumentException("String value can not be null.");
        }
        byte[] valueBytes = getBytesForString(value, isHashedValue);

        bitOutputStream.setNext(isHashedValue);
        bitOutputStream.setNext(VALUE_SIZE_BITS, valueBytes.length);
        for (byte valueByte : valueBytes) {
            bitOutputStream.setNext(/* numOfBits= */ 8, valueByte);
        }
    }

    private void serializeIntValue(int value, BitOutputStream bitOutputStream) throws IOException {
        bitOutputStream.setNext(/* numOfBits= */ 32, value);
    }

    private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream)
            throws IOException {
        bitOutputStream.setNext(value);
    }

    // Get the byte array for a value.
    // If the value is not hashed, use its byte array form directly.
    // If the value is hashed, get the raw form decoding of the value. All hashed values are
    // hex-encoded. Serialized values are in raw form.
    private static byte[] getBytesForString(String value, boolean isHashedValue) {
        if (!isHashedValue) {
            return value.getBytes(StandardCharsets.UTF_8);
        }
        return IntegrityUtils.getBytesFromHexDigest(value);
    }
}
+0 −69
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 java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** Holds the indexing type and indexing key of a given formula. */
class RuleIndexingDetails {

    static final int NOT_INDEXED = 0;
    static final int PACKAGE_NAME_INDEXED = 1;
    static final int APP_CERTIFICATE_INDEXED = 2;

    static final String DEFAULT_RULE_KEY = "N/A";

    /** 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 {
    }

    private @IndexType int mIndexType;
    private String mRuleKey;

    /** Constructor without a ruleKey for {@code NOT_INDEXED}. */
    RuleIndexingDetails(@IndexType int indexType) {
        this.mIndexType = indexType;
        this.mRuleKey = DEFAULT_RULE_KEY;
    }

    /** Constructor with a ruleKey for indexed rules. */
    RuleIndexingDetails(@IndexType int indexType, String ruleKey) {
        this.mIndexType = indexType;
        this.mRuleKey = ruleKey;
    }

    /** Returns the indexing type for the rule. */
    @IndexType
    public int getIndexType() {
        return mIndexType;
    }

    /** Returns the identified rule key. */
    public String getRuleKey() {
        return mRuleKey;
    }
}
+0 −151

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −52
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.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG;
import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG;

import android.util.Xml;

import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.integrity.model.RuleMetadata;

import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

/** Helper class for writing rule metadata. */
public class RuleMetadataSerializer {
    /** Serialize the rule metadata to an output stream. */
    public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream)
            throws IOException {
        TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream);

        serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider());
        serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion());

        xmlSerializer.endDocument();
    }

    private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag,
            String value) throws IOException {
        xmlSerializer.startTag(/* namespace= */ null, tag);
        xmlSerializer.text(value);
        xmlSerializer.endTag(/* namespace= */ null, tag);
    }
}
Loading