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