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