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

Commit 4f5ceae9 authored by Omer Nebil Yaveroglu's avatar Omer Nebil Yaveroglu
Browse files

Introduce a RuleIndexingParser class that will help us to identify the

range of the bytes to be read and parsed.

Note that the tests for the RuleIndexingController will be implemented
when its identifyRulesToEvaluate method is implemented.

Bug: 145493956
Test: atest FrameworksServicesTests:BinaryFileOperationsTest
Change-Id: Ie43e292f1a19c6c067a61d1d03da911d67620516
parent 5f7bd9b8
Loading
Loading
Loading
Loading
+29 −3
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.integrity.model.RuleMetadata;
import com.android.server.integrity.parser.RuleBinaryParser;
import com.android.server.integrity.parser.RuleIndexingController;
import com.android.server.integrity.parser.RuleMetadataParser;
import com.android.server.integrity.parser.RuleParseException;
import com.android.server.integrity.parser.RuleParser;
@@ -61,7 +62,10 @@ public class IntegrityFileManager {
    // update rules atomically.
    private final File mStagingDir;

    @Nullable private RuleMetadata mRuleMetadataCache;
    @Nullable
    private RuleMetadata mRuleMetadataCache;
    @Nullable
    private RuleIndexingController mRuleIndexingController;

    /** Get the singleton instance of this class. */
    public static synchronized IntegrityFileManager getInstance() {
@@ -100,6 +104,8 @@ public class IntegrityFileManager {
                Slog.e(TAG, "Error reading metadata file.", e);
            }
        }

        updateRuleIndexingController();
    }

    /**
@@ -109,7 +115,8 @@ public class IntegrityFileManager {
     */
    public boolean initialized() {
        return new File(mRulesDir, RULES_FILE).exists()
                && new File(mRulesDir, METADATA_FILE).exists();
                && new File(mRulesDir, METADATA_FILE).exists()
                && new File(mRulesDir, INDEXING_FILE).exists();
    }

    /** Write rules to persistent storage. */
@@ -131,6 +138,9 @@ public class IntegrityFileManager {
        }

        switchStagingRulesDir();

        // Update object holding the indexing information.
        updateRuleIndexingController();
    }

    /**
@@ -140,8 +150,13 @@ public class IntegrityFileManager {
     */
    public List<Rule> readRules(AppInstallMetadata appInstallMetadata)
            throws IOException, RuleParseException {
        // TODO: select rules by index
        synchronized (RULES_LOCK) {
            // Try to identify indexes from the index file.
            List<List<Integer>> ruleReadingIndexes =
                    mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);

            // Read the rules based on the index information.
            // TODO(b/145493956): Provide the identified indexes to the rule reader.
            try (FileInputStream inputStream =
                         new FileInputStream(new File(mRulesDir, RULES_FILE))) {
                List<Rule> rules = mRuleParser.parse(inputStream);
@@ -168,6 +183,17 @@ public class IntegrityFileManager {
        }
    }

    private void updateRuleIndexingController() {
        File ruleIndexingFile = new File(mRulesDir, INDEXING_FILE);
        if (ruleIndexingFile.exists()) {
            try (FileInputStream inputStream = new FileInputStream(ruleIndexingFile)) {
                mRuleIndexingController = new RuleIndexingController(inputStream);
            } catch (Exception e) {
                Slog.e(TAG, "Error parsing the rule indexing file.", e);
            }
        }
    }

    private void writeMetadata(File directory, String ruleProvider, String version)
            throws IOException {
        mRuleMetadataCache = new RuleMetadata(ruleProvider, version);
+27 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.integrity.model;

/**  A helper class containing special indexing file constants. */
public final class IndexingFileConstants {
    // The parsing time seems acceptable for this block size based on the tests in
    // go/ic-rule-file-format.
    public static final int INDEXING_BLOCK_SIZE = 100;

    public static final String START_INDEXING_KEY = "START_KEY";
    public static final String END_INDEXING_KEY = "END_KEY";
}
+77 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.integrity.parser;

import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;

import com.android.server.integrity.IntegrityUtils;
import com.android.server.integrity.model.BitInputStream;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Helper methods for reading standard data structures from {@link BitInputStream}.
 */
public class BinaryFileOperations {

    /**
     * Read an string value with the given size and hash status from a {@code BitInputStream}.
     *
     * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
     * All hashed values are hex-encoded.
     */
    public static String getStringValue(BitInputStream bitInputStream) throws IOException {
        boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
        int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
        return getStringValue(bitInputStream, valueSize, isHashedValue);
    }

    /**
     * Read an string value with the given size and hash status from a {@code BitInputStream}.
     *
     * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
     * All hashed values are hex-encoded.
     */
    public static String getStringValue(
            BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
            throws IOException {
        if (!isHashedValue) {
            StringBuilder value = new StringBuilder();
            while (valueSize-- > 0) {
                value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
            }
            return value.toString();
        }
        ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
        while (valueSize-- > 0) {
            byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
        }
        return IntegrityUtils.getHexDigest(byteBuffer.array());
    }

    /** Read an integer value from a {@code BitInputStream}. */
    public static int getIntValue(BitInputStream bitInputStream) throws IOException {
        return bitInputStream.getNext(/* numOfBits= */ 32);
    }

    /** Read an boolean value from a {@code BitInputStream}. */
    public static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
        return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
    }
}
+3 −31
Original line number Diff line number Diff line
@@ -28,18 +28,19 @@ 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.SIGNAL_BIT;
import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;

import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;

import com.android.server.integrity.IntegrityUtils;
import com.android.server.integrity.model.BitInputStream;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

@@ -145,33 +146,4 @@ public class RuleBinaryParser implements RuleParser {
                throw new IllegalArgumentException(String.format("Unknown key: %d", key));
        }
    }

    // Get value string from stream.
    // If the value is not hashed, get its raw form directly.
    // If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
    // All hashed values are hex-encoded.
    private static String getStringValue(
            BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
            throws IOException {
        if (!isHashedValue) {
            StringBuilder value = new StringBuilder();
            while (valueSize-- > 0) {
                value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
            }
            return value.toString();
        }
        ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
        while (valueSize-- > 0) {
            byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
        }
        return IntegrityUtils.getHexDigest(byteBuffer.array());
    }

    private static int getIntValue(BitInputStream bitInputStream) throws IOException {
        return bitInputStream.getNext(/* numOfBits= */ 32);
    }

    private static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
        return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
    }
}
+75 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.integrity.parser;

import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;

import android.content.integrity.AppInstallMetadata;

import com.android.server.integrity.model.BitInputStream;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

/** Helper class to identify the necessary indexes that needs to be read. */
public class RuleIndexingController {

    private static TreeMap<String, Integer> sPackageNameBasedIndexes;
    private static TreeMap<String, Integer> sAppCertificateBasedIndexes;
    private static TreeMap<String, Integer> sUnindexedRuleIndexes;

    /**
     * Provide the indexing file to read and the object will be constructed by reading and
     * identifying the indexes.
     */
    public RuleIndexingController(FileInputStream fileInputStream) throws IOException {
        BitInputStream bitInputStream = new BitInputStream(fileInputStream);
        sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
        sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
        sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
    }

    /**
     * Returns a list of integers with the starting and ending bytes of the rules that needs to be
     * read and evaluated.
     */
    public List<List<Integer>> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
        // TODO(b/145493956): Identify and return the indexes that needs to be read.
        return new ArrayList<>();
    }

    private TreeMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
            throws IOException {
        TreeMap<String, Integer> keyToIndexMap = new TreeMap<>();
        while (bitInputStream.hasNext()) {
            String key = getStringValue(bitInputStream);
            int value = getIntValue(bitInputStream);

            keyToIndexMap.put(key, value);

            if (key == END_INDEXING_KEY) {
                break;
            }
        }
        return keyToIndexMap;
    }
}
Loading