summaryrefslogtreecommitdiff
path: root/core/java/android/content/integrity/IntegrityFormula.java
blob: 9e2a3321f6e4c7b0ca3e2054fbee22f7abd56191 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
 * 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 android.content.integrity;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
import android.content.integrity.AtomicFormula.LongAtomicFormula;
import android.content.integrity.AtomicFormula.StringAtomicFormula;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;

/**
 * Represents a rule logic/content.
 *
 * @hide
 */
@SystemApi
@VisibleForTesting
public abstract class IntegrityFormula {

    /** Factory class for creating integrity formulas based on the app being installed. */
    public static final class Application {
        /** Returns an integrity formula that checks the equality to a package name. */
        @NonNull
        public static IntegrityFormula packageNameEquals(@NonNull String packageName) {
            return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName);
        }

        /**
         * Returns an integrity formula that checks if the app certificates contain the string
         * provided by the appCertificate parameter.
         */
        @NonNull
        public static IntegrityFormula certificatesContain(@NonNull String appCertificate) {
            return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate);
        }

        /**
         * Returns an integrity formula that checks if the app certificate lineage contains the
         * string provided by the appCertificate parameter.
         */
        @NonNull
        public static IntegrityFormula certificateLineageContains(@NonNull String appCertificate) {
            return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE_LINEAGE, appCertificate);
        }

        /** Returns an integrity formula that checks the equality to a version code. */
        @NonNull
        public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) {
            return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode);
        }

        /**
         * Returns an integrity formula that checks the app's version code is greater than the
         * provided value.
         */
        @NonNull
        public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) {
            return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode);
        }

        /**
         * Returns an integrity formula that checks the app's version code is greater than or equal
         * to the provided value.
         */
        @NonNull
        public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) {
            return new LongAtomicFormula(
                    AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode);
        }

        /** Returns an integrity formula that is valid when app is pre-installed. */
        @NonNull
        public static IntegrityFormula isPreInstalled() {
            return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
        }

        private Application() {}
    }

    /** Factory class for creating integrity formulas based on installer. */
    public static final class Installer {
        /** Returns an integrity formula that checks the equality to an installer name. */
        @NonNull
        public static IntegrityFormula packageNameEquals(@NonNull String installerName) {
            return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName);
        }

        /**
         * An static formula that evaluates to true if the installer is NOT allowed according to the
         * "allowed installer" field in the android manifest.
         */
        @NonNull
        public static IntegrityFormula notAllowedByManifest() {
            return not(new InstallerAllowedByManifestFormula());
        }

        /**
         * Returns an integrity formula that checks if the installer certificates contain {@code
         * installerCertificate}.
         */
        @NonNull
        public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) {
            return new StringAtomicFormula(
                    AtomicFormula.INSTALLER_CERTIFICATE, installerCertificate);
        }

        private Installer() {}
    }

    /** Factory class for creating integrity formulas based on source stamp. */
    public static final class SourceStamp {
        /** Returns an integrity formula that checks the equality to a stamp certificate hash. */
        @NonNull
        public static IntegrityFormula stampCertificateHashEquals(
                @NonNull String stampCertificateHash) {
            return new StringAtomicFormula(
                    AtomicFormula.STAMP_CERTIFICATE_HASH, stampCertificateHash);
        }

        /**
         * Returns an integrity formula that is valid when stamp embedded in the APK is NOT trusted.
         */
        @NonNull
        public static IntegrityFormula notTrusted() {
            return new BooleanAtomicFormula(AtomicFormula.STAMP_TRUSTED, /* value= */ false);
        }

        private SourceStamp() {}
    }

    /** @hide */
    @IntDef(
            value = {
                COMPOUND_FORMULA_TAG,
                STRING_ATOMIC_FORMULA_TAG,
                LONG_ATOMIC_FORMULA_TAG,
                BOOLEAN_ATOMIC_FORMULA_TAG,
                INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG
            })
    @Retention(RetentionPolicy.SOURCE)
    @interface Tag {}

    /** @hide */
    public static final int COMPOUND_FORMULA_TAG = 0;
    /** @hide */
    public static final int STRING_ATOMIC_FORMULA_TAG = 1;
    /** @hide */
    public static final int LONG_ATOMIC_FORMULA_TAG = 2;
    /** @hide */
    public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
    /** @hide */
    public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4;

    /**
     * Returns the tag that identifies the current class.
     *
     * @hide
     */
    public abstract @Tag int getTag();

    /**
     * Returns true when the integrity formula is satisfied by the {@code appInstallMetadata}.
     *
     * @hide
     */
    public abstract boolean matches(AppInstallMetadata appInstallMetadata);

    /**
     * Returns true when the formula (or one of its atomic formulas) has app certificate as key.
     *
     * @hide
     */
    public abstract boolean isAppCertificateFormula();

    /**
     * Returns true when the formula (or one of its atomic formulas) has app certificate lineage as
     * key.
     *
     * @hide
     */
    public abstract boolean isAppCertificateLineageFormula();

    /**
     * Returns true when the formula (or one of its atomic formulas) has installer package name or
     * installer certificate as key.
     *
     * @hide
     */
    public abstract boolean isInstallerFormula();

    /**
     * Write an {@link IntegrityFormula} to {@link android.os.Parcel}.
     *
     * <p>This helper method is needed because non-final class/interface are not allowed to be
     * {@link Parcelable}.
     *
     * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass
     * @hide
     */
    public static void writeToParcel(
            @NonNull IntegrityFormula formula, @NonNull Parcel dest, int flags) {
        dest.writeInt(formula.getTag());
        ((Parcelable) formula).writeToParcel(dest, flags);
    }

    /**
     * Read a {@link IntegrityFormula} from a {@link android.os.Parcel}.
     *
     * <p>We need this (hacky) helper method because non-final class/interface cannot be {@link
     * Parcelable} (api lint error).
     *
     * @throws IllegalArgumentException if the parcel cannot be parsed
     * @hide
     */
    @NonNull
    public static IntegrityFormula readFromParcel(@NonNull Parcel in) {
        int tag = in.readInt();
        switch (tag) {
            case COMPOUND_FORMULA_TAG:
                return CompoundFormula.CREATOR.createFromParcel(in);
            case STRING_ATOMIC_FORMULA_TAG:
                return StringAtomicFormula.CREATOR.createFromParcel(in);
            case LONG_ATOMIC_FORMULA_TAG:
                return LongAtomicFormula.CREATOR.createFromParcel(in);
            case BOOLEAN_ATOMIC_FORMULA_TAG:
                return BooleanAtomicFormula.CREATOR.createFromParcel(in);
            case INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG:
                return InstallerAllowedByManifestFormula.CREATOR.createFromParcel(in);
            default:
                throw new IllegalArgumentException("Unknown formula tag " + tag);
        }
    }

    /**
     * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to
     * true.
     *
     * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements.
     */
    @NonNull
    public static IntegrityFormula any(@NonNull IntegrityFormula... formulae) {
        return new CompoundFormula(CompoundFormula.OR, Arrays.asList(formulae));
    }

    /**
     * Returns a formula that evaluates to true when all formula in {@code formulae} evaluates to
     * true.
     *
     * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements.
     */
    @NonNull
    public static IntegrityFormula all(@NonNull IntegrityFormula... formulae) {
        return new CompoundFormula(CompoundFormula.AND, Arrays.asList(formulae));
    }

    /** Returns a formula that evaluates to true when {@code formula} evaluates to false. */
    @NonNull
    public static IntegrityFormula not(@NonNull IntegrityFormula formula) {
        return new CompoundFormula(CompoundFormula.NOT, Arrays.asList(formula));
    }

    // Constructor is package private so it cannot be inherited outside of this package.
    IntegrityFormula() {}
}