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