diff options
Diffstat (limited to 'core/java')
3 files changed, 305 insertions, 133 deletions
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java index 03248e5..c91e20c 100644 --- a/core/java/android/security/keymaster/KeyCharacteristics.java +++ b/core/java/android/security/keymaster/KeyCharacteristics.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +19,7 @@ package android.security.keymaster; import android.os.Parcel; import android.os.Parcelable; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -30,8 +31,8 @@ public class KeyCharacteristics implements Parcelable { public KeymasterArguments swEnforced; public KeymasterArguments hwEnforced; - public static final Parcelable.Creator<KeyCharacteristics> CREATOR = new - Parcelable.Creator<KeyCharacteristics>() { + public static final Parcelable.Creator<KeyCharacteristics> CREATOR = + new Parcelable.Creator<KeyCharacteristics>() { @Override public KeyCharacteristics createFromParcel(Parcel in) { return new KeyCharacteristics(in); @@ -65,73 +66,85 @@ public class KeyCharacteristics implements Parcelable { hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in); } - public Integer getInteger(int tag) { + /** + * Returns the value of the specified enum tag or {@code defaultValue} if the tag is not + * present. + * + * @throws IllegalArgumentException if {@code tag} is not an enum tag. + */ + public Integer getEnum(int tag) { if (hwEnforced.containsTag(tag)) { - return hwEnforced.getInt(tag, -1); + return hwEnforced.getEnum(tag, -1); } else if (swEnforced.containsTag(tag)) { - return swEnforced.getInt(tag, -1); + return swEnforced.getEnum(tag, -1); } else { return null; } } - public int getInt(int tag, int defaultValue) { - Integer result = getInteger(tag); - return (result != null) ? result : defaultValue; - } - - public List<Integer> getInts(int tag) { + /** + * Returns all values of the specified repeating enum tag. + * + * throws IllegalArgumentException if {@code tag} is not a repeating enum tag. + */ + public List<Integer> getEnums(int tag) { List<Integer> result = new ArrayList<Integer>(); - result.addAll(hwEnforced.getInts(tag)); - result.addAll(swEnforced.getInts(tag)); + result.addAll(hwEnforced.getEnums(tag)); + result.addAll(swEnforced.getEnums(tag)); return result; } - public Long getLong(int tag) { + /** + * Returns the value of the specified unsigned 32-bit int tag or {@code defaultValue} if the tag + * is not present. + * + * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag. + */ + public long getUnsignedInt(int tag, long defaultValue) { if (hwEnforced.containsTag(tag)) { - return hwEnforced.getLong(tag, -1); - } else if (swEnforced.containsTag(tag)) { - return swEnforced.getLong(tag, -1); + return hwEnforced.getUnsignedInt(tag, defaultValue); } else { - return null; + return swEnforced.getUnsignedInt(tag, defaultValue); } } - public long getLong(int tag, long defaultValue) { - Long result = getLong(tag); - return (result != null) ? result : defaultValue; - } - - public List<Long> getLongs(int tag) { - List<Long> result = new ArrayList<Long>(); - result.addAll(hwEnforced.getLongs(tag)); - result.addAll(swEnforced.getLongs(tag)); + /** + * Returns all values of the specified repeating unsigned 64-bit long tag. + * + * @throws IllegalArgumentException if {@code tag} is not a repeating unsigned 64-bit long tag. + */ + public List<BigInteger> getUnsignedLongs(int tag) { + List<BigInteger> result = new ArrayList<BigInteger>(); + result.addAll(hwEnforced.getUnsignedLongs(tag)); + result.addAll(swEnforced.getUnsignedLongs(tag)); return result; } + /** + * Returns the value of the specified date tag or {@code null} if the tag is not present. + * + * @throws IllegalArgumentException if {@code tag} is not a date tag or if the tag's value + * represents a time instant which is after {@code 2^63 - 1} milliseconds since Unix + * epoch. + */ public Date getDate(int tag) { - Date result = hwEnforced.getDate(tag, null); - if (result == null) { - result = swEnforced.getDate(tag, null); - } - return result; - } - - public Date getDate(int tag, Date defaultValue) { - if (hwEnforced.containsTag(tag)) { - return hwEnforced.getDate(tag, null); - } else if (hwEnforced.containsTag(tag)) { - return swEnforced.getDate(tag, null); - } else { - return defaultValue; + Date result = swEnforced.getDate(tag, null); + if (result != null) { + return result; } + return hwEnforced.getDate(tag, null); } + /** + * Returns {@code true} if the provided boolean tag is present, {@code false} if absent. + * + * @throws IllegalArgumentException if {@code tag} is not a boolean tag. + */ public boolean getBoolean(int tag) { if (hwEnforced.containsTag(tag)) { - return hwEnforced.getBoolean(tag, false); + return hwEnforced.getBoolean(tag); } else { - return swEnforced.getBoolean(tag, false); + return swEnforced.getBoolean(tag); } } } diff --git a/core/java/android/security/keymaster/KeymasterArgument.java b/core/java/android/security/keymaster/KeymasterArgument.java index 9adde35..d1d8371 100644 --- a/core/java/android/security/keymaster/KeymasterArgument.java +++ b/core/java/android/security/keymaster/KeymasterArgument.java @@ -32,6 +32,7 @@ abstract class KeymasterArgument implements Parcelable { public static final Parcelable.Creator<KeymasterArgument> CREATOR = new Parcelable.Creator<KeymasterArgument>() { + @Override public KeymasterArgument createFromParcel(Parcel in) { final int pos = in.dataPosition(); final int tag = in.readInt(); @@ -55,6 +56,8 @@ abstract class KeymasterArgument implements Parcelable { throw new ParcelFormatException("Bad tag: " + tag + " at " + pos); } } + + @Override public KeymasterArgument[] newArray(int size) { return new KeymasterArgument[size]; } diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java index 363376c..ee0ad6d 100644 --- a/core/java/android/security/keymaster/KeymasterArguments.java +++ b/core/java/android/security/keymaster/KeymasterArguments.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +19,7 @@ package android.security.keymaster; import android.os.Parcel; import android.os.Parcelable; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -30,7 +31,14 @@ import java.util.List; * @hide */ public class KeymasterArguments implements Parcelable { - List<KeymasterArgument> mArguments; + + private static final long UINT32_RANGE = 1L << 32; + public static final long UINT32_MAX_VALUE = UINT32_RANGE - 1; + + private static final BigInteger UINT64_RANGE = BigInteger.ONE.shiftLeft(64); + public static final BigInteger UINT64_MAX_VALUE = UINT64_RANGE.subtract(BigInteger.ONE); + + private List<KeymasterArgument> mArguments; public static final Parcelable.Creator<KeymasterArguments> CREATOR = new Parcelable.Creator<KeymasterArguments>() { @@ -53,156 +61,292 @@ public class KeymasterArguments implements Parcelable { mArguments = in.createTypedArrayList(KeymasterArgument.CREATOR); } - public void addInt(int tag, int value) { - mArguments.add(new KeymasterIntArgument(tag, value)); + /** + * Adds an enum tag with the provided value. + * + * @throws IllegalArgumentException if {@code tag} is not an enum tag. + */ + public void addEnum(int tag, int value) { + int tagType = KeymasterDefs.getTagType(tag); + if ((tagType != KeymasterDefs.KM_ENUM) && (tagType != KeymasterDefs.KM_ENUM_REP)) { + throw new IllegalArgumentException("Not an enum or repeating enum tag: " + tag); + } + addEnumTag(tag, value); } - public void addInts(int tag, int... values) { + /** + * Adds a repeated enum tag with the provided values. + * + * @throws IllegalArgumentException if {@code tag} is not a repeating enum tag. + */ + public void addEnums(int tag, int... values) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM_REP) { + throw new IllegalArgumentException("Not a repeating enum tag: " + tag); + } for (int value : values) { - addInt(tag, value); + addEnumTag(tag, value); } } - public void addLongs(int tag, long... values) { - for (long value : values) { - addLong(tag, value); + /** + * Returns the value of the specified enum tag or {@code defaultValue} if the tag is not + * present. + * + * @throws IllegalArgumentException if {@code tag} is not an enum tag. + */ + public int getEnum(int tag, int defaultValue) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM) { + throw new IllegalArgumentException("Not an enum tag: " + tag); } + KeymasterArgument arg = getArgumentByTag(tag); + if (arg == null) { + return defaultValue; + } + return getEnumTagValue(arg); } - public void addBoolean(int tag) { - mArguments.add(new KeymasterBooleanArgument(tag)); + /** + * Returns all values of the specified repeating enum tag. + * + * throws IllegalArgumentException if {@code tag} is not a repeating enum tag. + */ + public List<Integer> getEnums(int tag) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ENUM_REP) { + throw new IllegalArgumentException("Not a repeating enum tag: " + tag); + } + List<Integer> values = new ArrayList<Integer>(); + for (KeymasterArgument arg : mArguments) { + if (arg.tag == tag) { + values.add(getEnumTagValue(arg)); + } + } + return values; } - public void addLong(int tag, long value) { - mArguments.add(new KeymasterLongArgument(tag, value)); + private void addEnumTag(int tag, int value) { + mArguments.add(new KeymasterIntArgument(tag, value)); } - public void addBlob(int tag, byte[] value) { - mArguments.add(new KeymasterBlobArgument(tag, value)); + private int getEnumTagValue(KeymasterArgument arg) { + return ((KeymasterIntArgument) arg).value; } - public void addDate(int tag, Date value) { - mArguments.add(new KeymasterDateArgument(tag, value)); + /** + * Adds an unsigned 32-bit int tag with the provided value. + * + * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag or if + * {@code value} is outside of the permitted range [0; 2^32). + */ + public void addUnsignedInt(int tag, long value) { + int tagType = KeymasterDefs.getTagType(tag); + if ((tagType != KeymasterDefs.KM_INT) && (tagType != KeymasterDefs.KM_INT_REP)) { + throw new IllegalArgumentException("Not an int or repeating int tag: " + tag); + } + // Keymaster's KM_INT is unsigned 32 bit. + if ((value < 0) || (value > UINT32_MAX_VALUE)) { + throw new IllegalArgumentException("Int tag value out of range: " + value); + } + mArguments.add(new KeymasterIntArgument(tag, (int) value)); } - public void addDateIfNotNull(int tag, Date value) { - if (value != null) { - mArguments.add(new KeymasterDateArgument(tag, value)); + /** + * Returns the value of the specified unsigned 32-bit int tag or {@code defaultValue} if the tag + * is not present. + * + * @throws IllegalArgumentException if {@code tag} is not an unsigned 32-bit int tag. + */ + public long getUnsignedInt(int tag, long defaultValue) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_INT) { + throw new IllegalArgumentException("Not an int tag: " + tag); + } + KeymasterArgument arg = getArgumentByTag(tag); + if (arg == null) { + return defaultValue; } + // Keymaster's KM_INT is unsigned 32 bit. + return ((KeymasterIntArgument) arg).value & 0xffffffffL; } - private KeymasterArgument getArgumentByTag(int tag) { + /** + * Adds an unsigned 64-bit long tag with the provided value. + * + * @throws IllegalArgumentException if {@code tag} is not an unsigned 64-bit long tag or if + * {@code value} is outside of the permitted range [0; 2^64). + */ + public void addUnsignedLong(int tag, BigInteger value) { + int tagType = KeymasterDefs.getTagType(tag); + if ((tagType != KeymasterDefs.KM_LONG) && (tagType != KeymasterDefs.KM_LONG_REP)) { + throw new IllegalArgumentException("Not a long or repeating long tag: " + tag); + } + addLongTag(tag, value); + } + + /** + * Returns all values of the specified repeating unsigned 64-bit long tag. + * + * @throws IllegalArgumentException if {@code tag} is not a repeating unsigned 64-bit long tag. + */ + public List<BigInteger> getUnsignedLongs(int tag) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG_REP) { + throw new IllegalArgumentException("Tag is not a repeating long: " + tag); + } + List<BigInteger> values = new ArrayList<BigInteger>(); for (KeymasterArgument arg : mArguments) { if (arg.tag == tag) { - return arg; + values.add(getLongTagValue(arg)); } } - return null; + return values; } - public boolean containsTag(int tag) { - return getArgumentByTag(tag) != null; + private void addLongTag(int tag, BigInteger value) { + // Keymaster's KM_LONG is unsigned 64 bit. + if ((value.signum() == -1) || (value.compareTo(UINT64_MAX_VALUE) > 0)) { + throw new IllegalArgumentException("Long tag value out of range: " + value); + } + mArguments.add(new KeymasterLongArgument(tag, value.longValue())); + } + + private BigInteger getLongTagValue(KeymasterArgument arg) { + // Keymaster's KM_LONG is unsigned 64 bit. We're forced to use BigInteger for type safety + // because there's no unsigned long type. + return toUint64(((KeymasterLongArgument) arg).value); + } + + /** + * Adds the provided boolean tag. Boolean tags are considered to be set to {@code true} if + * present and {@code false} if absent. + * + * @throws IllegalArgumentException if {@code tag} is not a boolean tag. + */ + public void addBoolean(int tag) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) { + throw new IllegalArgumentException("Not a boolean tag: " + tag); + } + mArguments.add(new KeymasterBooleanArgument(tag)); } - public int getInt(int tag, int defaultValue) { - switch (KeymasterDefs.getTagType(tag)) { - case KeymasterDefs.KM_ENUM: - case KeymasterDefs.KM_INT: - break; // Accepted types - case KeymasterDefs.KM_INT_REP: - case KeymasterDefs.KM_ENUM_REP: - throw new IllegalArgumentException("Repeatable tags must use getInts: " + tag); - default: - throw new IllegalArgumentException("Tag is not an int type: " + tag); + /** + * Returns {@code true} if the provided boolean tag is present, {@code false} if absent. + * + * @throws IllegalArgumentException if {@code tag} is not a boolean tag. + */ + public boolean getBoolean(int tag) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) { + throw new IllegalArgumentException("Not a boolean tag: " + tag); } KeymasterArgument arg = getArgumentByTag(tag); if (arg == null) { - return defaultValue; + return false; } - return ((KeymasterIntArgument) arg).value; + return true; } - public long getLong(int tag, long defaultValue) { - switch (KeymasterDefs.getTagType(tag)) { - case KeymasterDefs.KM_LONG: - break; // Accepted type - case KeymasterDefs.KM_LONG_REP: - throw new IllegalArgumentException("Repeatable tags must use getLongs: " + tag); - default: - throw new IllegalArgumentException("Tag is not a long type: " + tag); + /** + * Adds a bytes tag with the provided value. + * + * @throws IllegalArgumentException if {@code tag} is not a bytes tag. + */ + public void addBytes(int tag, byte[] value) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) { + throw new IllegalArgumentException("Not a bytes tag: " + tag); + } + if (value == null) { + throw new NullPointerException("value == nulll"); + } + mArguments.add(new KeymasterBlobArgument(tag, value)); + } + + /** + * Returns the value of the specified bytes tag or {@code defaultValue} if the tag is not + * present. + * + * @throws IllegalArgumentException if {@code tag} is not a bytes tag. + */ + public byte[] getBytes(int tag, byte[] defaultValue) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BYTES) { + throw new IllegalArgumentException("Not a bytes tag: " + tag); } KeymasterArgument arg = getArgumentByTag(tag); if (arg == null) { return defaultValue; } - return ((KeymasterLongArgument) arg).value; + return ((KeymasterBlobArgument) arg).blob; } - public Date getDate(int tag, Date defaultValue) { + /** + * Adds a date tag with the provided value. + * + * @throws IllegalArgumentException if {@code tag} is not a date tag or if {@code value} is + * before the start of Unix epoch. + */ + public void addDate(int tag, Date value) { if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) { - throw new IllegalArgumentException("Tag is not a date type: " + tag); + throw new IllegalArgumentException("Not a date tag: " + tag); } - KeymasterArgument arg = getArgumentByTag(tag); - if (arg == null) { - return defaultValue; + if (value == null) { + throw new NullPointerException("value == nulll"); + } + // Keymaster's KM_DATE is unsigned, but java.util.Date is signed, thus preventing us from + // using values larger than 2^63 - 1. + if (value.getTime() < 0) { + throw new IllegalArgumentException("Date tag value out of range: " + value); } - return ((KeymasterDateArgument) arg).date; + mArguments.add(new KeymasterDateArgument(tag, value)); } - public boolean getBoolean(int tag, boolean defaultValue) { - if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) { - throw new IllegalArgumentException("Tag is not a boolean type: " + tag); + /** + * Adds a date tag with the provided value, if the value is not {@code null}. Does nothing if + * the {@code value} is null. + * + * @throws IllegalArgumentException if {@code tag} is not a date tag or if {@code value} is + * before the start of Unix epoch. + */ + public void addDateIfNotNull(int tag, Date value) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) { + throw new IllegalArgumentException("Not a date tag: " + tag); } - KeymasterArgument arg = getArgumentByTag(tag); - if (arg == null) { - return defaultValue; + if (value != null) { + addDate(tag, value); } - return true; } - public byte[] getBlob(int tag, byte[] defaultValue) { - switch (KeymasterDefs.getTagType(tag)) { - case KeymasterDefs.KM_BYTES: - case KeymasterDefs.KM_BIGNUM: - break; // Allowed types. - default: - throw new IllegalArgumentException("Tag is not a blob type: " + tag); + /** + * Returns the value of the specified date tag or {@code defaultValue} if the tag is not + * present. + * + * @throws IllegalArgumentException if {@code tag} is not a date tag or if the tag's value + * represents a time instant which is after {@code 2^63 - 1} milliseconds since Unix + * epoch. + */ + public Date getDate(int tag, Date defaultValue) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) { + throw new IllegalArgumentException("Tag is not a date type: " + tag); } KeymasterArgument arg = getArgumentByTag(tag); if (arg == null) { return defaultValue; } - return ((KeymasterBlobArgument) arg).blob; + Date result = ((KeymasterDateArgument) arg).date; + // Keymaster's KM_DATE is unsigned, but java.util.Date is signed, thus preventing us from + // using values larger than 2^63 - 1. + if (result.getTime() < 0) { + throw new IllegalArgumentException("Tag value too large. Tag: " + tag); + } + return result; } - public List<Integer> getInts(int tag) { - switch (KeymasterDefs.getTagType(tag)) { - case KeymasterDefs.KM_INT_REP: - case KeymasterDefs.KM_ENUM_REP: - break; // Allowed types. - default: - throw new IllegalArgumentException("Tag is not a repeating type: " + tag); - } - List<Integer> values = new ArrayList<Integer>(); + private KeymasterArgument getArgumentByTag(int tag) { for (KeymasterArgument arg : mArguments) { if (arg.tag == tag) { - values.add(((KeymasterIntArgument) arg).value); + return arg; } } - return values; + return null; } - public List<Long> getLongs(int tag) { - if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_LONG_REP) { - throw new IllegalArgumentException("Tag is not a repeating long: " + tag); - } - List<Long> values = new ArrayList<Long>(); - for (KeymasterArgument arg : mArguments) { - if (arg.tag == tag) { - values.add(((KeymasterLongArgument) arg).value); - } - } - return values; + public boolean containsTag(int tag) { + return getArgumentByTag(tag) != null; } public int size() { @@ -222,4 +366,16 @@ public class KeymasterArguments implements Parcelable { public int describeContents() { return 0; } + + /** + * Converts the provided value to non-negative {@link BigInteger}, treating the sign bit of the + * provided value as the most significant bit of the result. + */ + public static BigInteger toUint64(long value) { + if (value >= 0) { + return BigInteger.valueOf(value); + } else { + return BigInteger.valueOf(value).add(UINT64_RANGE); + } + } } |