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