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

Commit b49e6f44 authored by Khaled Abdelmohsen's avatar Khaled Abdelmohsen Committed by Android (Google) Code Review
Browse files

Merge changes I11fbf84d,Ia804137d

* changes:
  Implement rule binary parser
  Implement rule binary serializer
parents 7beb4c9d 4524c422
Loading
Loading
Loading
Loading
+66 −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.model;

/** A wrapper class for reading a stream of bits. */
public class BitInputStream {

    private byte[] mRuleBytes;
    private long mBitPointer;

    public BitInputStream(byte[] ruleBytes) {
        this.mRuleBytes = ruleBytes;
        this.mBitPointer = 0;
    }

    /**
     * Read the next number of bits from the stream.
     *
     * @param numOfBits The number of bits to read.
     * @return The value read from the stream.
     */
    public int getNext(int numOfBits) {
        int component = 0;
        int count = 0;

        int idx = (int) (mBitPointer / 8);
        int offset = 7 - (int) (mBitPointer % 8);

        while (count++ < numOfBits) {
            if (idx >= mRuleBytes.length) {
                throw new IllegalArgumentException(String.format("Invalid byte index: %d", idx));
            }

            component <<= 1;
            component |= (mRuleBytes[idx] >>> offset) & 1;

            offset--;
            if (offset == -1) {
                idx++;
                offset = 7;
            }
        }

        mBitPointer += numOfBits;
        return component;
    }

    /** Check if there are bits left in the stream. */
    public boolean hasNext() {
        return mBitPointer / 8 < mRuleBytes.length;
    }
}
+86 −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.model;

import java.util.BitSet;

/** A wrapper class for writing a stream of bits. */
public class BitOutputStream {

    private BitSet mBitSet;
    private int mIndex;

    public BitOutputStream() {
        mBitSet = new BitSet();
        mIndex = 0;
    }

    /**
     * Set the next number of bits in the stream to value.
     *
     * @param numOfBits The number of bits used to represent the value.
     * @param value The value to convert to bits.
     */
    public void setNext(int numOfBits, int value) {
        if (numOfBits <= 0) {
            return;
        }
        int offset = 1 << (numOfBits - 1);
        while (numOfBits-- > 0) {
            mBitSet.set(mIndex, (value & offset) != 0);
            offset >>= 1;
            mIndex++;
        }
    }

    /**
     * Set the next bit in the stream to value.
     *
     * @param value The value to set the bit to.
     */
    public void setNext(boolean value) {
        mBitSet.set(mIndex, value);
        mIndex++;
    }

    /** Set the next bit in the stream to true. */
    public void setNext() {
        setNext(/* value= */ true);
    }

    /** Convert BitSet in big-endian to ByteArray in big-endian. */
    public byte[] toByteArray() {
        int bitSetSize = mBitSet.length();
        int numOfBytes = bitSetSize / 8;
        if (bitSetSize % 8 != 0) {
            numOfBytes++;
        }
        byte[] bytes = new byte[numOfBytes];
        for (int i = 0; i < mBitSet.length(); i++) {
            if (mBitSet.get(i)) {
                bytes[i / 8] |= 1 << (7 - (i % 8));
            }
        }
        return bytes;
    }

    /** Clear the stream. */
    public void clear() {
        mBitSet.clear();
        mIndex = 0;
    }
}
+41 −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.model;

import android.content.integrity.Rule;

/**
 * A helper class containing information about the binary representation of different {@link Rule}
 * components.
 */
public final class ComponentBitSize {
    public static final int FORMAT_VERSION_BITS = 5;
    public static final int EFFECT_BITS = 3;
    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 VALUE_SIZE_BITS = 5;
    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 DEFAULT_FORMAT_VERSION = 1;
    public static final int SIGNAL_BIT = 1;
}
+115 −6
Original line number Diff line number Diff line
@@ -16,23 +16,132 @@

package com.android.server.integrity.parser;

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.EFFECT_BITS;
import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
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;
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 android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;

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

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/** A helper class to parse rules into the {@link Rule} model from Binary representation. */
public class RuleBinaryParser implements RuleParser {

    @Override
    public List<Rule> parse(byte[] ruleBytes) {
        // TODO: Implement binary text parser.
        return null;
    public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
        try {
            BitInputStream bitInputStream = new BitInputStream(ruleBytes);
            return parseRules(bitInputStream);
        } catch (Exception e) {
            throw new RuleParseException(e.getMessage(), e);
        }
    }

    @Override
    public List<Rule> parse(InputStream inputStream) {
        // TODO: Implement stream parser.
    public List<Rule> parse(InputStream inputStream) throws RuleParseException {
        try {
            byte[] ruleBytes = new byte[inputStream.available()];
            inputStream.read(ruleBytes);
            return parse(ruleBytes);
        } catch (Exception e) {
            throw new RuleParseException(e.getMessage(), e);
        }
    }

    private List<Rule> parseRules(BitInputStream bitInputStream) {
        List<Rule> parsedRules = new ArrayList<>();

        // Read the rule binary file format version.
        bitInputStream.getNext(FORMAT_VERSION_BITS);

        while (bitInputStream.hasNext()) {
            if (bitInputStream.getNext(SIGNAL_BIT) == 1) {
                parsedRules.add(parseRule(bitInputStream));
            }
        }

        return parsedRules;
    }

    private Rule parseRule(BitInputStream bitInputStream) {
        Formula formula = parseFormula(bitInputStream);
        int effect = bitInputStream.getNext(EFFECT_BITS);

        if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
            throw new IllegalArgumentException("A rule must end with a '1' bit.");
        }

        return new Rule(formula, effect);
    }

    private Formula parseFormula(BitInputStream bitInputStream) {
        int separator = bitInputStream.getNext(SEPARATOR_BITS);
        switch (separator) {
            case ATOMIC_FORMULA_START:
                return parseAtomicFormula(bitInputStream);
            case COMPOUND_FORMULA_START:
                return parseCompoundFormula(bitInputStream);
            case COMPOUND_FORMULA_END:
                return null;
            default:
                throw new IllegalArgumentException(
                        String.format("Unknown formula separator: %s", separator));
        }
    }

    private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) {
        int connector = bitInputStream.getNext(CONNECTOR_BITS);
        List<Formula> formulas = new ArrayList<>();

        Formula parsedFormula = parseFormula(bitInputStream);
        while (parsedFormula != null) {
            formulas.add(parsedFormula);
            parsedFormula = parseFormula(bitInputStream);
        }

        return new CompoundFormula(connector, formulas);
    }

    private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) {
        int key = bitInputStream.getNext(KEY_BITS);
        int operator = bitInputStream.getNext(OPERATOR_BITS);

        boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
        int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
        StringBuilder value = new StringBuilder();
        while (valueSize-- > 0) {
            value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
        }

        switch (key) {
            case AtomicFormula.PACKAGE_NAME:
            case AtomicFormula.APP_CERTIFICATE:
            case AtomicFormula.INSTALLER_NAME:
            case AtomicFormula.INSTALLER_CERTIFICATE:
                return new AtomicFormula.StringAtomicFormula(key, value.toString(), isHashedValue);
            case AtomicFormula.VERSION_CODE:
                return new AtomicFormula.IntAtomicFormula(
                        key, operator, Integer.parseInt(value.toString()));
            case AtomicFormula.PRE_INSTALLED:
                return new AtomicFormula.BooleanAtomicFormula(key, value.toString().equals("1"));
            default:
                throw new IllegalArgumentException(String.format("Unknown key: %d", key));
        }
    }
}
+8 −7
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ public final class RuleXmlParser implements RuleParser {
    private static final String NAMESPACE = "";
    private static final String RULE_LIST_TAG = "RL";
    private static final String RULE_TAG = "R";
    private static final String OPEN_FORMULA_TAG = "OF";
    private static final String COMPOUND_FORMULA_TAG = "OF";
    private static final String ATOMIC_FORMULA_TAG = "AF";
    private static final String EFFECT_ATTRIBUTE = "E";
    private static final String KEY_ATTRIBUTE = "K";
@@ -118,8 +118,8 @@ public final class RuleXmlParser implements RuleParser {

            if (eventType == XmlPullParser.START_TAG) {
                switch (nodeName) {
                    case OPEN_FORMULA_TAG:
                        formula = parseOpenFormula(parser);
                    case COMPOUND_FORMULA_TAG:
                        formula = parseCompoundFormula(parser);
                        break;
                    case ATOMIC_FORMULA_TAG:
                        formula = parseAtomicFormula(parser);
@@ -137,7 +137,7 @@ public final class RuleXmlParser implements RuleParser {
        return new Rule(formula, effect);
    }

    private static Formula parseOpenFormula(XmlPullParser parser)
    private static Formula parseCompoundFormula(XmlPullParser parser)
            throws IOException, XmlPullParserException {
        int connector =
                Integer.parseInt(extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1"));
@@ -147,7 +147,8 @@ public final class RuleXmlParser implements RuleParser {
        while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
            String nodeName = parser.getName();

            if (eventType == XmlPullParser.END_TAG && parser.getName().equals(OPEN_FORMULA_TAG)) {
            if (eventType == XmlPullParser.END_TAG
                    && parser.getName().equals(COMPOUND_FORMULA_TAG)) {
                break;
            }

@@ -156,8 +157,8 @@ public final class RuleXmlParser implements RuleParser {
                    case ATOMIC_FORMULA_TAG:
                        formulas.add(parseAtomicFormula(parser));
                        break;
                    case OPEN_FORMULA_TAG:
                        formulas.add(parseOpenFormula(parser));
                    case COMPOUND_FORMULA_TAG:
                        formulas.add(parseCompoundFormula(parser));
                        break;
                    default:
                        throw new RuntimeException(
Loading