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