1 /* 2 * Copyright (C) 2015 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.security.keymaster; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.os.Build; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 24 import java.math.BigInteger; 25 import java.util.ArrayList; 26 import java.util.Date; 27 import java.util.List; 28 29 /** 30 * Utility class for the java side of user specified Keymaster arguments. 31 * <p> 32 * Serialization code for this and subclasses must be kept in sync with system/security/keystore 33 * @hide 34 */ 35 public class KeymasterArguments implements Parcelable { 36 37 private static final long UINT32_RANGE = 1L << 32; 38 public static final long UINT32_MAX_VALUE = UINT32_RANGE - 1; 39 40 private static final BigInteger UINT64_RANGE = BigInteger.ONE.shiftLeft(64); 41 public static final BigInteger UINT64_MAX_VALUE = UINT64_RANGE.subtract(BigInteger.ONE); 42 43 private List<KeymasterArgument> mArguments; 44 45 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 46 public static final @android.annotation.NonNull Parcelable.Creator<KeymasterArguments> CREATOR = new 47 Parcelable.Creator<KeymasterArguments>() { 48 @Override 49 public KeymasterArguments createFromParcel(Parcel in) { 50 return new KeymasterArguments(in); 51 } 52 53 @Override 54 public KeymasterArguments[] newArray(int size) { 55 return new KeymasterArguments[size]; 56 } 57 }; 58 59 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) KeymasterArguments()60 public KeymasterArguments() { 61 mArguments = new ArrayList<KeymasterArgument>(); 62 } 63 KeymasterArguments(Parcel in)64 private KeymasterArguments(Parcel in) { 65 mArguments = in.createTypedArrayList(KeymasterArgument.CREATOR); 66 } 67 68 /** 69 * Adds an enum tag with the provided value. 70 * 71 * @throws IllegalArgumentException if {@code tag} is not an enum tag. 72 */ 73 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) addEnum(int tag, int value)74 public void addEnum(int tag, int value) { 75 int tagType = KeymasterDefs.getTagType(tag); 76 if ((tagType != KeymasterDefs.KM_ENUM) && (tagType != KeymasterDefs.KM_ENUM_REP)) { 77 throw new IllegalArgumentException("Not an enum or repeating enum tag: " + tag); 78 } 79 addEnumTag(tag, value); 80 } 81 82 /** 83 * Adds a repeated enum tag with the provided values. 84 * 85 * @throws IllegalArgumentException if {@code tag} is not a repeating enum tag. 86 */ addEnums(int tag, int... values)87 public void addEnums(int tag, int... values) { 88 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM_REP) { 89 throw new IllegalArgumentException("Not a repeating enum tag: " + tag); 90 } 91 for (int value : values) { 92 addEnumTag(tag, value); 93 } 94 } 95 96 /** 97 * Returns the value of the specified enum tag or {@code defaultValue} if the tag is not 98 * present. 99 * 100 * @throws IllegalArgumentException if {@code tag} is not an enum tag. 101 */ getEnum(int tag, int defaultValue)102 public int getEnum(int tag, int defaultValue) { 103 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM) { 104 throw new IllegalArgumentException("Not an enum tag: " + tag); 105 } 106 KeymasterArgument arg = getArgumentByTag(tag); 107 if (arg == null) { 108 return defaultValue; 109 } 110 return getEnumTagValue(arg); 111 } 112 113 /** 114 * Returns all values of the specified repeating enum tag. 115 * 116 * throws IllegalArgumentException if {@code tag} is not a repeating enum tag. 117 */ getEnums(int tag)118 public List<Integer> getEnums(int tag) { 119 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM_REP) { 120 throw new IllegalArgumentException("Not a repeating enum tag: " + tag); 121 } 122 List<Integer> values = new ArrayList<Integer>(); 123 for (KeymasterArgument arg : mArguments) { 124 if (arg.tag == tag) { 125 values.add(getEnumTagValue(arg)); 126 } 127 } 128 return values; 129 } 130 addEnumTag(int tag, int value)131 private void addEnumTag(int tag, int value) { 132 mArguments.add(new KeymasterIntArgument(tag, value)); 133 } 134 getEnumTagValue(KeymasterArgument arg)135 private int getEnumTagValue(KeymasterArgument arg) { 136 return ((KeymasterIntArgument) arg).value; 137 } 138 139 /** 140 * Adds an unsigned 32-bit int tag with the provided value. 141 * 142 * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag or if 143 * {@code value} is outside of the permitted range [0; 2^32). 144 */ 145 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) addUnsignedInt(int tag, long value)146 public void addUnsignedInt(int tag, long value) { 147 int tagType = KeymasterDefs.getTagType(tag); 148 if ((tagType != KeymasterDefs.KM_UINT) && (tagType != KeymasterDefs.KM_UINT_REP)) { 149 throw new IllegalArgumentException("Not an int or repeating int tag: " + tag); 150 } 151 // Keymaster's KM_UINT is unsigned 32 bit. 152 if ((value < 0) || (value > UINT32_MAX_VALUE)) { 153 throw new IllegalArgumentException("Int tag value out of range: " + value); 154 } 155 mArguments.add(new KeymasterIntArgument(tag, (int) value)); 156 } 157 158 /** 159 * Returns the value of the specified unsigned 32-bit int tag or {@code defaultValue} if the tag 160 * is not present. 161 * 162 * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag. 163 */ getUnsignedInt(int tag, long defaultValue)164 public long getUnsignedInt(int tag, long defaultValue) { 165 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_UINT) { 166 throw new IllegalArgumentException("Not an int tag: " + tag); 167 } 168 KeymasterArgument arg = getArgumentByTag(tag); 169 if (arg == null) { 170 return defaultValue; 171 } 172 // Keymaster's KM_UINT is unsigned 32 bit. 173 return ((KeymasterIntArgument) arg).value & 0xffffffffL; 174 } 175 176 /** 177 * Adds an unsigned 64-bit long tag with the provided value. 178 * 179 * @throws IllegalArgumentException if {@code tag} is not an unsigned 64-bit long tag or if 180 * {@code value} is outside of the permitted range [0; 2^64). 181 */ 182 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) addUnsignedLong(int tag, BigInteger value)183 public void addUnsignedLong(int tag, BigInteger value) { 184 int tagType = KeymasterDefs.getTagType(tag); 185 if ((tagType != KeymasterDefs.KM_ULONG) && (tagType != KeymasterDefs.KM_ULONG_REP)) { 186 throw new IllegalArgumentException("Not a long or repeating long tag: " + tag); 187 } 188 addLongTag(tag, value); 189 } 190 191 /** 192 * Returns all values of the specified repeating unsigned 64-bit long tag. 193 * 194 * @throws IllegalArgumentException if {@code tag} is not a repeating unsigned 64-bit long tag. 195 */ getUnsignedLongs(int tag)196 public List<BigInteger> getUnsignedLongs(int tag) { 197 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ULONG_REP) { 198 throw new IllegalArgumentException("Tag is not a repeating long: " + tag); 199 } 200 List<BigInteger> values = new ArrayList<BigInteger>(); 201 for (KeymasterArgument arg : mArguments) { 202 if (arg.tag == tag) { 203 values.add(getLongTagValue(arg)); 204 } 205 } 206 return values; 207 } 208 addLongTag(int tag, BigInteger value)209 private void addLongTag(int tag, BigInteger value) { 210 // Keymaster's KM_ULONG is unsigned 64 bit. 211 if ((value.signum() == -1) || (value.compareTo(UINT64_MAX_VALUE) > 0)) { 212 throw new IllegalArgumentException("Long tag value out of range: " + value); 213 } 214 mArguments.add(new KeymasterLongArgument(tag, value.longValue())); 215 } 216 getLongTagValue(KeymasterArgument arg)217 private BigInteger getLongTagValue(KeymasterArgument arg) { 218 // Keymaster's KM_ULONG is unsigned 64 bit. We're forced to use BigInteger for type safety 219 // because there's no unsigned long type. 220 return toUint64(((KeymasterLongArgument) arg).value); 221 } 222 223 /** 224 * Adds the provided boolean tag. Boolean tags are considered to be set to {@code true} if 225 * present and {@code false} if absent. 226 * 227 * @throws IllegalArgumentException if {@code tag} is not a boolean tag. 228 */ addBoolean(int tag)229 public void addBoolean(int tag) { 230 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) { 231 throw new IllegalArgumentException("Not a boolean tag: " + tag); 232 } 233 mArguments.add(new KeymasterBooleanArgument(tag)); 234 } 235 236 /** 237 * Returns {@code true} if the provided boolean tag is present, {@code false} if absent. 238 * 239 * @throws IllegalArgumentException if {@code tag} is not a boolean tag. 240 */ getBoolean(int tag)241 public boolean getBoolean(int tag) { 242 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) { 243 throw new IllegalArgumentException("Not a boolean tag: " + tag); 244 } 245 KeymasterArgument arg = getArgumentByTag(tag); 246 if (arg == null) { 247 return false; 248 } 249 return true; 250 } 251 252 /** 253 * Adds a bytes tag with the provided value. 254 * 255 * @throws IllegalArgumentException if {@code tag} is not a bytes tag. 256 */ addBytes(int tag, byte[] value)257 public void addBytes(int tag, byte[] value) { 258 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) { 259 throw new IllegalArgumentException("Not a bytes tag: " + tag); 260 } 261 if (value == null) { 262 throw new NullPointerException("value == nulll"); 263 } 264 mArguments.add(new KeymasterBlobArgument(tag, value)); 265 } 266 267 /** 268 * Returns the value of the specified bytes tag or {@code defaultValue} if the tag is not 269 * present. 270 * 271 * @throws IllegalArgumentException if {@code tag} is not a bytes tag. 272 */ getBytes(int tag, byte[] defaultValue)273 public byte[] getBytes(int tag, byte[] defaultValue) { 274 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) { 275 throw new IllegalArgumentException("Not a bytes tag: " + tag); 276 } 277 KeymasterArgument arg = getArgumentByTag(tag); 278 if (arg == null) { 279 return defaultValue; 280 } 281 return ((KeymasterBlobArgument) arg).blob; 282 } 283 284 /** 285 * Adds a date tag with the provided value. 286 * 287 * @throws IllegalArgumentException if {@code tag} is not a date tag or if {@code value} is 288 * before the start of Unix epoch. 289 */ addDate(int tag, Date value)290 public void addDate(int tag, Date value) { 291 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) { 292 throw new IllegalArgumentException("Not a date tag: " + tag); 293 } 294 if (value == null) { 295 throw new NullPointerException("value == nulll"); 296 } 297 // Keymaster's KM_DATE is unsigned, but java.util.Date is signed, thus preventing us from 298 // using values larger than 2^63 - 1. 299 if (value.getTime() < 0) { 300 throw new IllegalArgumentException("Date tag value out of range: " + value); 301 } 302 mArguments.add(new KeymasterDateArgument(tag, value)); 303 } 304 305 /** 306 * Adds a date tag with the provided value, if the value is not {@code null}. Does nothing if 307 * the {@code value} is null. 308 * 309 * @throws IllegalArgumentException if {@code tag} is not a date tag or if {@code value} is 310 * before the start of Unix epoch. 311 */ addDateIfNotNull(int tag, Date value)312 public void addDateIfNotNull(int tag, Date value) { 313 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) { 314 throw new IllegalArgumentException("Not a date tag: " + tag); 315 } 316 if (value != null) { 317 addDate(tag, value); 318 } 319 } 320 321 /** 322 * Returns the value of the specified date tag or {@code defaultValue} if the tag is not 323 * present. 324 * 325 * @throws IllegalArgumentException if {@code tag} is not a date tag or if the tag's value 326 * represents a time instant which is after {@code 2^63 - 1} milliseconds since Unix 327 * epoch. 328 */ getDate(int tag, Date defaultValue)329 public Date getDate(int tag, Date defaultValue) { 330 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) { 331 throw new IllegalArgumentException("Tag is not a date type: " + tag); 332 } 333 KeymasterArgument arg = getArgumentByTag(tag); 334 if (arg == null) { 335 return defaultValue; 336 } 337 Date result = ((KeymasterDateArgument) arg).date; 338 // Keymaster's KM_DATE is unsigned, but java.util.Date is signed, thus preventing us from 339 // using values larger than 2^63 - 1. 340 if (result.getTime() < 0) { 341 throw new IllegalArgumentException("Tag value too large. Tag: " + tag); 342 } 343 return result; 344 } 345 getArgumentByTag(int tag)346 private KeymasterArgument getArgumentByTag(int tag) { 347 for (KeymasterArgument arg : mArguments) { 348 if (arg.tag == tag) { 349 return arg; 350 } 351 } 352 return null; 353 } 354 containsTag(int tag)355 public boolean containsTag(int tag) { 356 return getArgumentByTag(tag) != null; 357 } 358 size()359 public int size() { 360 return mArguments.size(); 361 } 362 363 @Override writeToParcel(Parcel out, int flags)364 public void writeToParcel(Parcel out, int flags) { 365 out.writeTypedList(mArguments); 366 } 367 368 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) readFromParcel(Parcel in)369 public void readFromParcel(Parcel in) { 370 in.readTypedList(mArguments, KeymasterArgument.CREATOR); 371 } 372 373 @Override describeContents()374 public int describeContents() { 375 return 0; 376 } 377 378 /** 379 * Converts the provided value to non-negative {@link BigInteger}, treating the sign bit of the 380 * provided value as the most significant bit of the result. 381 */ toUint64(long value)382 public static BigInteger toUint64(long value) { 383 if (value >= 0) { 384 return BigInteger.valueOf(value); 385 } else { 386 return BigInteger.valueOf(value).add(UINT64_RANGE); 387 } 388 } 389 } 390