1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.integrity.parser; 18 19 import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START; 20 import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS; 21 import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END; 22 import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START; 23 import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; 24 import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; 25 import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; 26 import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; 27 import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS; 28 import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; 29 import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; 30 import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; 31 import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT; 32 import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; 33 import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue; 34 import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue; 35 import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue; 36 37 import android.content.integrity.AtomicFormula; 38 import android.content.integrity.CompoundFormula; 39 import android.content.integrity.InstallerAllowedByManifestFormula; 40 import android.content.integrity.IntegrityFormula; 41 import android.content.integrity.Rule; 42 43 import com.android.server.integrity.model.BitInputStream; 44 45 import java.io.BufferedInputStream; 46 import java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.List; 50 51 /** A helper class to parse rules into the {@link Rule} model from Binary representation. */ 52 public class RuleBinaryParser implements RuleParser { 53 54 @Override parse(byte[] ruleBytes)55 public List<Rule> parse(byte[] ruleBytes) throws RuleParseException { 56 return parse(RandomAccessObject.ofBytes(ruleBytes), Collections.emptyList()); 57 } 58 59 @Override parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> indexRanges)60 public List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> indexRanges) 61 throws RuleParseException { 62 try (RandomAccessInputStream randomAccessInputStream = 63 new RandomAccessInputStream(randomAccessObject)) { 64 return parseRules(randomAccessInputStream, indexRanges); 65 } catch (Exception e) { 66 throw new RuleParseException(e.getMessage(), e); 67 } 68 } 69 parseRules( RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)70 private List<Rule> parseRules( 71 RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges) 72 throws IOException { 73 74 // Read the rule binary file format version. 75 randomAccessInputStream.skip(FORMAT_VERSION_BITS / BYTE_BITS); 76 77 return indexRanges.isEmpty() 78 ? parseAllRules(randomAccessInputStream) 79 : parseIndexedRules(randomAccessInputStream, indexRanges); 80 } 81 parseAllRules(RandomAccessInputStream randomAccessInputStream)82 private List<Rule> parseAllRules(RandomAccessInputStream randomAccessInputStream) 83 throws IOException { 84 List<Rule> parsedRules = new ArrayList<>(); 85 86 BitInputStream inputStream = 87 new BitInputStream(new BufferedInputStream(randomAccessInputStream)); 88 while (inputStream.hasNext()) { 89 if (inputStream.getNext(SIGNAL_BIT) == 1) { 90 parsedRules.add(parseRule(inputStream)); 91 } 92 } 93 94 return parsedRules; 95 } 96 parseIndexedRules( RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)97 private List<Rule> parseIndexedRules( 98 RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges) 99 throws IOException { 100 List<Rule> parsedRules = new ArrayList<>(); 101 102 for (RuleIndexRange range : indexRanges) { 103 randomAccessInputStream.seek(range.getStartIndex()); 104 105 BitInputStream inputStream = 106 new BitInputStream( 107 new BufferedInputStream( 108 new LimitInputStream( 109 randomAccessInputStream, 110 range.getEndIndex() - range.getStartIndex()))); 111 112 // Read the rules until we reach the end index. available() here is not reliable. 113 while (inputStream.hasNext()) { 114 if (inputStream.getNext(SIGNAL_BIT) == 1) { 115 parsedRules.add(parseRule(inputStream)); 116 } 117 } 118 } 119 120 return parsedRules; 121 } 122 parseRule(BitInputStream bitInputStream)123 private Rule parseRule(BitInputStream bitInputStream) throws IOException { 124 IntegrityFormula formula = parseFormula(bitInputStream); 125 int effect = bitInputStream.getNext(EFFECT_BITS); 126 127 if (bitInputStream.getNext(SIGNAL_BIT) != 1) { 128 throw new IllegalArgumentException("A rule must end with a '1' bit."); 129 } 130 131 return new Rule(formula, effect); 132 } 133 parseFormula(BitInputStream bitInputStream)134 private IntegrityFormula parseFormula(BitInputStream bitInputStream) throws IOException { 135 int separator = bitInputStream.getNext(SEPARATOR_BITS); 136 switch (separator) { 137 case ATOMIC_FORMULA_START: 138 return parseAtomicFormula(bitInputStream); 139 case COMPOUND_FORMULA_START: 140 return parseCompoundFormula(bitInputStream); 141 case COMPOUND_FORMULA_END: 142 return null; 143 case INSTALLER_ALLOWED_BY_MANIFEST_START: 144 return new InstallerAllowedByManifestFormula(); 145 default: 146 throw new IllegalArgumentException( 147 String.format("Unknown formula separator: %s", separator)); 148 } 149 } 150 parseCompoundFormula(BitInputStream bitInputStream)151 private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException { 152 int connector = bitInputStream.getNext(CONNECTOR_BITS); 153 List<IntegrityFormula> formulas = new ArrayList<>(); 154 155 IntegrityFormula parsedFormula = parseFormula(bitInputStream); 156 while (parsedFormula != null) { 157 formulas.add(parsedFormula); 158 parsedFormula = parseFormula(bitInputStream); 159 } 160 161 return new CompoundFormula(connector, formulas); 162 } 163 parseAtomicFormula(BitInputStream bitInputStream)164 private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException { 165 int key = bitInputStream.getNext(KEY_BITS); 166 int operator = bitInputStream.getNext(OPERATOR_BITS); 167 168 switch (key) { 169 case AtomicFormula.PACKAGE_NAME: 170 case AtomicFormula.APP_CERTIFICATE: 171 case AtomicFormula.INSTALLER_NAME: 172 case AtomicFormula.INSTALLER_CERTIFICATE: 173 case AtomicFormula.STAMP_CERTIFICATE_HASH: 174 boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1; 175 int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS); 176 String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue); 177 return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue); 178 case AtomicFormula.VERSION_CODE: 179 // TODO(b/147880712): temporary hack until our input handles long 180 long upper = getIntValue(bitInputStream); 181 long lower = getIntValue(bitInputStream); 182 long longValue = (upper << 32) | lower; 183 return new AtomicFormula.LongAtomicFormula(key, operator, longValue); 184 case AtomicFormula.PRE_INSTALLED: 185 case AtomicFormula.STAMP_TRUSTED: 186 boolean booleanValue = getBooleanValue(bitInputStream); 187 return new AtomicFormula.BooleanAtomicFormula(key, booleanValue); 188 default: 189 throw new IllegalArgumentException(String.format("Unknown key: %d", key)); 190 } 191 } 192 } 193