• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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