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 android.content.integrity; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.SystemApi; 22 import android.content.integrity.AtomicFormula.BooleanAtomicFormula; 23 import android.content.integrity.AtomicFormula.LongAtomicFormula; 24 import android.content.integrity.AtomicFormula.StringAtomicFormula; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.util.Arrays; 33 34 /** 35 * Represents a rule logic/content. 36 * 37 * @hide 38 */ 39 @SystemApi 40 @VisibleForTesting 41 public abstract class IntegrityFormula { 42 43 /** Factory class for creating integrity formulas based on the app being installed. */ 44 public static final class Application { 45 /** Returns an integrity formula that checks the equality to a package name. */ 46 @NonNull packageNameEquals(@onNull String packageName)47 public static IntegrityFormula packageNameEquals(@NonNull String packageName) { 48 return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName); 49 } 50 51 /** 52 * Returns an integrity formula that checks if the app certificates contain {@code 53 * appCertificate}. 54 */ 55 @NonNull certificatesContain(@onNull String appCertificate)56 public static IntegrityFormula certificatesContain(@NonNull String appCertificate) { 57 return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate); 58 } 59 60 /** Returns an integrity formula that checks the equality to a version code. */ 61 @NonNull versionCodeEquals(@onNull long versionCode)62 public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) { 63 return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode); 64 } 65 66 /** 67 * Returns an integrity formula that checks the app's version code is greater than the 68 * provided value. 69 */ 70 @NonNull versionCodeGreaterThan(@onNull long versionCode)71 public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) { 72 return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode); 73 } 74 75 /** 76 * Returns an integrity formula that checks the app's version code is greater than or equal 77 * to the provided value. 78 */ 79 @NonNull versionCodeGreaterThanOrEqualTo(@onNull long versionCode)80 public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) { 81 return new LongAtomicFormula( 82 AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode); 83 } 84 85 /** Returns an integrity formula that is valid when app is pre-installed. */ 86 @NonNull isPreInstalled()87 public static IntegrityFormula isPreInstalled() { 88 return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true); 89 } 90 Application()91 private Application() {} 92 } 93 94 /** Factory class for creating integrity formulas based on installer. */ 95 public static final class Installer { 96 /** Returns an integrity formula that checks the equality to an installer name. */ 97 @NonNull packageNameEquals(@onNull String installerName)98 public static IntegrityFormula packageNameEquals(@NonNull String installerName) { 99 return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName); 100 } 101 102 /** 103 * An static formula that evaluates to true if the installer is NOT allowed according to the 104 * "allowed installer" field in the android manifest. 105 */ 106 @NonNull notAllowedByManifest()107 public static IntegrityFormula notAllowedByManifest() { 108 return not(new InstallerAllowedByManifestFormula()); 109 } 110 111 /** 112 * Returns an integrity formula that checks if the installer certificates contain {@code 113 * installerCertificate}. 114 */ 115 @NonNull certificatesContain(@onNull String installerCertificate)116 public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) { 117 return new StringAtomicFormula( 118 AtomicFormula.INSTALLER_CERTIFICATE, installerCertificate); 119 } 120 Installer()121 private Installer() {} 122 } 123 124 /** Factory class for creating integrity formulas based on source stamp. */ 125 public static final class SourceStamp { 126 /** Returns an integrity formula that checks the equality to a stamp certificate hash. */ 127 @NonNull stampCertificateHashEquals( @onNull String stampCertificateHash)128 public static IntegrityFormula stampCertificateHashEquals( 129 @NonNull String stampCertificateHash) { 130 return new StringAtomicFormula( 131 AtomicFormula.STAMP_CERTIFICATE_HASH, stampCertificateHash); 132 } 133 134 /** 135 * Returns an integrity formula that is valid when stamp embedded in the APK is NOT trusted. 136 */ 137 @NonNull notTrusted()138 public static IntegrityFormula notTrusted() { 139 return new BooleanAtomicFormula(AtomicFormula.STAMP_TRUSTED, /* value= */ false); 140 } 141 SourceStamp()142 private SourceStamp() {} 143 } 144 145 /** @hide */ 146 @IntDef( 147 value = { 148 COMPOUND_FORMULA_TAG, 149 STRING_ATOMIC_FORMULA_TAG, 150 LONG_ATOMIC_FORMULA_TAG, 151 BOOLEAN_ATOMIC_FORMULA_TAG, 152 INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG 153 }) 154 @Retention(RetentionPolicy.SOURCE) 155 @interface Tag {} 156 157 /** @hide */ 158 public static final int COMPOUND_FORMULA_TAG = 0; 159 /** @hide */ 160 public static final int STRING_ATOMIC_FORMULA_TAG = 1; 161 /** @hide */ 162 public static final int LONG_ATOMIC_FORMULA_TAG = 2; 163 /** @hide */ 164 public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3; 165 /** @hide */ 166 public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4; 167 168 /** 169 * Returns the tag that identifies the current class. 170 * 171 * @hide 172 */ getTag()173 public abstract @Tag int getTag(); 174 175 /** 176 * Returns true when the integrity formula is satisfied by the {@code appInstallMetadata}. 177 * 178 * @hide 179 */ matches(AppInstallMetadata appInstallMetadata)180 public abstract boolean matches(AppInstallMetadata appInstallMetadata); 181 182 /** 183 * Returns true when the formula (or one of its atomic formulas) has app certificate as key. 184 * 185 * @hide 186 */ isAppCertificateFormula()187 public abstract boolean isAppCertificateFormula(); 188 189 /** 190 * Returns true when the formula (or one of its atomic formulas) has installer package name or 191 * installer certificate as key. 192 * 193 * @hide 194 */ isInstallerFormula()195 public abstract boolean isInstallerFormula(); 196 197 /** 198 * Write an {@link IntegrityFormula} to {@link android.os.Parcel}. 199 * 200 * <p>This helper method is needed because non-final class/interface are not allowed to be 201 * {@link Parcelable}. 202 * 203 * @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass 204 * @hide 205 */ writeToParcel( @onNull IntegrityFormula formula, @NonNull Parcel dest, int flags)206 public static void writeToParcel( 207 @NonNull IntegrityFormula formula, @NonNull Parcel dest, int flags) { 208 dest.writeInt(formula.getTag()); 209 ((Parcelable) formula).writeToParcel(dest, flags); 210 } 211 212 /** 213 * Read a {@link IntegrityFormula} from a {@link android.os.Parcel}. 214 * 215 * <p>We need this (hacky) helper method because non-final class/interface cannot be {@link 216 * Parcelable} (api lint error). 217 * 218 * @throws IllegalArgumentException if the parcel cannot be parsed 219 * @hide 220 */ 221 @NonNull readFromParcel(@onNull Parcel in)222 public static IntegrityFormula readFromParcel(@NonNull Parcel in) { 223 int tag = in.readInt(); 224 switch (tag) { 225 case COMPOUND_FORMULA_TAG: 226 return CompoundFormula.CREATOR.createFromParcel(in); 227 case STRING_ATOMIC_FORMULA_TAG: 228 return StringAtomicFormula.CREATOR.createFromParcel(in); 229 case LONG_ATOMIC_FORMULA_TAG: 230 return LongAtomicFormula.CREATOR.createFromParcel(in); 231 case BOOLEAN_ATOMIC_FORMULA_TAG: 232 return BooleanAtomicFormula.CREATOR.createFromParcel(in); 233 case INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG: 234 return InstallerAllowedByManifestFormula.CREATOR.createFromParcel(in); 235 default: 236 throw new IllegalArgumentException("Unknown formula tag " + tag); 237 } 238 } 239 240 /** 241 * Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to 242 * true. 243 * 244 * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements. 245 */ 246 @NonNull any(@onNull IntegrityFormula... formulae)247 public static IntegrityFormula any(@NonNull IntegrityFormula... formulae) { 248 return new CompoundFormula(CompoundFormula.OR, Arrays.asList(formulae)); 249 } 250 251 /** 252 * Returns a formula that evaluates to true when all formula in {@code formulae} evaluates to 253 * true. 254 * 255 * <p>Throws an {@link IllegalArgumentException} if formulae has less than two elements. 256 */ 257 @NonNull all(@onNull IntegrityFormula... formulae)258 public static IntegrityFormula all(@NonNull IntegrityFormula... formulae) { 259 return new CompoundFormula(CompoundFormula.AND, Arrays.asList(formulae)); 260 } 261 262 /** Returns a formula that evaluates to true when {@code formula} evaluates to false. */ 263 @NonNull not(@onNull IntegrityFormula formula)264 public static IntegrityFormula not(@NonNull IntegrityFormula formula) { 265 return new CompoundFormula(CompoundFormula.NOT, Arrays.asList(formula)); 266 } 267 268 // Constructor is package private so it cannot be inherited outside of this package. IntegrityFormula()269 IntegrityFormula() {} 270 } 271