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