blob: d2dbdec7f726005406767ffc417f126a6b889cfd [file] [log] [blame]
Alex Klyubinae6cb7a2015-06-22 18:09:35 -07001/*
Alex Klyubin8effa362015-06-24 16:06:55 -07002 * Copyright (C) 2015 The Android Open Source Project
Chad Brubaker45ff13e2015-01-21 14:00:55 -08003 *
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 *
Alex Klyubin8effa362015-06-24 16:06:55 -07008 * http://www.apache.org/licenses/LICENSE-2.0
Chad Brubaker45ff13e2015-01-21 14:00:55 -08009 *
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
Mathew Inwoode420f8b2018-08-16 18:40:47 +010019import android.annotation.UnsupportedAppUsage;
Chad Brubaker45ff13e2015-01-21 14:00:55 -080020import android.os.Parcel;
21import android.os.Parcelable;
22
Alex Klyubinae6cb7a2015-06-22 18:09:35 -070023import java.math.BigInteger;
Chad Brubaker45ff13e2015-01-21 14:00:55 -080024import java.util.ArrayList;
25import java.util.Date;
26import 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 */
34public class KeymasterArguments implements Parcelable {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -070035
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;
Chad Brubaker45ff13e2015-01-21 14:00:55 -080043
Mathew Inwoode420f8b2018-08-16 18:40:47 +010044 @UnsupportedAppUsage
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -070045 public static final @android.annotation.NonNull Parcelable.Creator<KeymasterArguments> CREATOR = new
Chad Brubaker45ff13e2015-01-21 14:00:55 -080046 Parcelable.Creator<KeymasterArguments>() {
Alex Klyubin5927c9f2015-04-10 13:28:03 -070047 @Override
Chad Brubaker45ff13e2015-01-21 14:00:55 -080048 public KeymasterArguments createFromParcel(Parcel in) {
49 return new KeymasterArguments(in);
50 }
Alex Klyubin5927c9f2015-04-10 13:28:03 -070051
52 @Override
Chad Brubaker45ff13e2015-01-21 14:00:55 -080053 public KeymasterArguments[] newArray(int size) {
54 return new KeymasterArguments[size];
55 }
56 };
57
Mathew Inwoode420f8b2018-08-16 18:40:47 +010058 @UnsupportedAppUsage
Chad Brubaker45ff13e2015-01-21 14:00:55 -080059 public KeymasterArguments() {
60 mArguments = new ArrayList<KeymasterArgument>();
61 }
62
63 private KeymasterArguments(Parcel in) {
64 mArguments = in.createTypedArrayList(KeymasterArgument.CREATOR);
65 }
66
Alex Klyubinae6cb7a2015-06-22 18:09:35 -070067 /**
68 * Adds an enum tag with the provided value.
69 *
70 * @throws IllegalArgumentException if {@code tag} is not an enum tag.
71 */
Mathew Inwoode420f8b2018-08-16 18:40:47 +010072 @UnsupportedAppUsage
Alex Klyubinae6cb7a2015-06-22 18:09:35 -070073 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 */
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 */
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 */
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
130 private void addEnumTag(int tag, int value) {
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800131 mArguments.add(new KeymasterIntArgument(tag, value));
132 }
133
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700134 private int getEnumTagValue(KeymasterArgument arg) {
135 return ((KeymasterIntArgument) arg).value;
Alex Klyubin5927c9f2015-04-10 13:28:03 -0700136 }
137
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700138 /**
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 */
Mathew Inwoode420f8b2018-08-16 18:40:47 +0100144 @UnsupportedAppUsage
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700145 public void addUnsignedInt(int tag, long value) {
146 int tagType = KeymasterDefs.getTagType(tag);
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700147 if ((tagType != KeymasterDefs.KM_UINT) && (tagType != KeymasterDefs.KM_UINT_REP)) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700148 throw new IllegalArgumentException("Not an int or repeating int tag: " + tag);
Chad Brubakerb543b3932015-04-16 14:30:30 -0700149 }
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700150 // Keymaster's KM_UINT is unsigned 32 bit.
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700151 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));
Chad Brubakerb543b3932015-04-16 14:30:30 -0700155 }
156
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700157 /**
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 */
163 public long getUnsignedInt(int tag, long defaultValue) {
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700164 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_UINT) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700165 throw new IllegalArgumentException("Not an int tag: " + tag);
166 }
167 KeymasterArgument arg = getArgumentByTag(tag);
168 if (arg == null) {
169 return defaultValue;
170 }
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700171 // Keymaster's KM_UINT is unsigned 32 bit.
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700172 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 */
Mathew Inwoode420f8b2018-08-16 18:40:47 +0100181 @UnsupportedAppUsage
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700182 public void addUnsignedLong(int tag, BigInteger value) {
183 int tagType = KeymasterDefs.getTagType(tag);
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700184 if ((tagType != KeymasterDefs.KM_ULONG) && (tagType != KeymasterDefs.KM_ULONG_REP)) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700185 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 */
195 public List<BigInteger> getUnsignedLongs(int tag) {
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700196 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_ULONG_REP) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700197 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
208 private void addLongTag(int tag, BigInteger value) {
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700209 // Keymaster's KM_ULONG is unsigned 64 bit.
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700210 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
216 private BigInteger getLongTagValue(KeymasterArgument arg) {
Alex Klyubin3e7a9e42015-06-24 15:46:45 -0700217 // Keymaster's KM_ULONG is unsigned 64 bit. We're forced to use BigInteger for type safety
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700218 // 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 */
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800228 public void addBoolean(int tag) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700229 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BOOL) {
230 throw new IllegalArgumentException("Not a boolean tag: " + tag);
231 }
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800232 mArguments.add(new KeymasterBooleanArgument(tag));
233 }
234
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700235 /**
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 */
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;
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800249 }
250
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700251 /**
252 * Adds a bytes tag with the provided value.
253 *
254 * @throws IllegalArgumentException if {@code tag} is not a bytes tag.
255 */
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 }
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800263 mArguments.add(new KeymasterBlobArgument(tag, value));
264 }
265
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700266 /**
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 */
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 */
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800289 public void addDate(int tag, Date value) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700290 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 }
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800301 mArguments.add(new KeymasterDateArgument(tag, value));
302 }
303
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700304 /**
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 */
Alex Klyubind6c77992015-06-23 12:06:27 -0700311 public void addDateIfNotNull(int tag, Date value) {
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700312 if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_DATE) {
313 throw new IllegalArgumentException("Not a date tag: " + tag);
Alex Klyubind6c77992015-06-23 12:06:27 -0700314 }
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700315 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 */
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;
Alex Klyubind6c77992015-06-23 12:06:27 -0700343 }
344
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800345 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
354 public boolean containsTag(int tag) {
355 return getArgumentByTag(tag) != null;
356 }
357
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800358 public int size() {
359 return mArguments.size();
360 }
361
362 @Override
363 public void writeToParcel(Parcel out, int flags) {
364 out.writeTypedList(mArguments);
365 }
366
Mathew Inwoode420f8b2018-08-16 18:40:47 +0100367 @UnsupportedAppUsage
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800368 public void readFromParcel(Parcel in) {
369 in.readTypedList(mArguments, KeymasterArgument.CREATOR);
370 }
371
372 @Override
373 public int describeContents() {
374 return 0;
375 }
Alex Klyubinae6cb7a2015-06-22 18:09:35 -0700376
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 */
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 }
Chad Brubaker45ff13e2015-01-21 14:00:55 -0800388}