Home | History | Annotate | Download | only in keymaster
      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 
     56     public KeymasterArguments() {
     57         mArguments = new ArrayList<KeymasterArgument>();
     58     }
     59 
     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      */
     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      */
     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      */
     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      */
    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 
    126     private void addEnumTag(int tag, int value) {
    127         mArguments.add(new KeymasterIntArgument(tag, value));
    128     }
    129 
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    348     public boolean containsTag(int tag) {
    349         return getArgumentByTag(tag) != null;
    350     }
    351 
    352     public int size() {
    353         return mArguments.size();
    354     }
    355 
    356     @Override
    357     public void writeToParcel(Parcel out, int flags) {
    358         out.writeTypedList(mArguments);
    359     }
    360 
    361     public void readFromParcel(Parcel in) {
    362         in.readTypedList(mArguments, KeymasterArgument.CREATOR);
    363     }
    364 
    365     @Override
    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      */
    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