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