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
17package android.security.keymaster;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.math.BigInteger;
23import java.util.ArrayList;
24import java.util.Date;
25import 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 */
33public 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