1/*
2 * Copyright (C) 2009 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;
18
19import com.android.org.conscrypt.NativeCrypto;
20
21import android.os.RemoteException;
22import android.os.ServiceManager;
23import android.util.Log;
24
25import java.util.Locale;
26
27/**
28 * @hide This should not be made public in its present form because it
29 * assumes that private and secret key bytes are available and would
30 * preclude the use of hardware crypto.
31 */
32public class KeyStore {
33    private static final String TAG = "KeyStore";
34
35    // ResponseCodes
36    public static final int NO_ERROR = 1;
37    public static final int LOCKED = 2;
38    public static final int UNINITIALIZED = 3;
39    public static final int SYSTEM_ERROR = 4;
40    public static final int PROTOCOL_ERROR = 5;
41    public static final int PERMISSION_DENIED = 6;
42    public static final int KEY_NOT_FOUND = 7;
43    public static final int VALUE_CORRUPTED = 8;
44    public static final int UNDEFINED_ACTION = 9;
45    public static final int WRONG_PASSWORD = 10;
46
47    // Used for UID field to indicate the calling UID.
48    public static final int UID_SELF = -1;
49
50    // Flags for "put" "import" and "generate"
51    public static final int FLAG_NONE = 0;
52    public static final int FLAG_ENCRYPTED = 1;
53
54    // States
55    public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
56
57    private int mError = NO_ERROR;
58
59    private final IKeystoreService mBinder;
60
61    private KeyStore(IKeystoreService binder) {
62        mBinder = binder;
63    }
64
65    public static KeyStore getInstance() {
66        IKeystoreService keystore = IKeystoreService.Stub.asInterface(ServiceManager
67                .getService("android.security.keystore"));
68        return new KeyStore(keystore);
69    }
70
71    static int getKeyTypeForAlgorithm(String keyType) throws IllegalArgumentException {
72        if ("RSA".equalsIgnoreCase(keyType)) {
73            return NativeCrypto.EVP_PKEY_RSA;
74        } else if ("DSA".equalsIgnoreCase(keyType)) {
75            return NativeCrypto.EVP_PKEY_DSA;
76        } else if ("EC".equalsIgnoreCase(keyType)) {
77            return NativeCrypto.EVP_PKEY_EC;
78        } else {
79            throw new IllegalArgumentException("Unsupported key type: " + keyType);
80        }
81    }
82
83    public State state() {
84        final int ret;
85        try {
86            ret = mBinder.test();
87        } catch (RemoteException e) {
88            Log.w(TAG, "Cannot connect to keystore", e);
89            throw new AssertionError(e);
90        }
91
92        switch (ret) {
93            case NO_ERROR: return State.UNLOCKED;
94            case LOCKED: return State.LOCKED;
95            case UNINITIALIZED: return State.UNINITIALIZED;
96            default: throw new AssertionError(mError);
97        }
98    }
99
100    public boolean isUnlocked() {
101        return state() == State.UNLOCKED;
102    }
103
104    public byte[] get(String key) {
105        try {
106            return mBinder.get(key);
107        } catch (RemoteException e) {
108            Log.w(TAG, "Cannot connect to keystore", e);
109            return null;
110        }
111    }
112
113    public boolean put(String key, byte[] value, int uid, int flags) {
114        try {
115            return mBinder.insert(key, value, uid, flags) == NO_ERROR;
116        } catch (RemoteException e) {
117            Log.w(TAG, "Cannot connect to keystore", e);
118            return false;
119        }
120    }
121
122    public boolean delete(String key, int uid) {
123        try {
124            return mBinder.del(key, uid) == NO_ERROR;
125        } catch (RemoteException e) {
126            Log.w(TAG, "Cannot connect to keystore", e);
127            return false;
128        }
129    }
130
131    public boolean delete(String key) {
132        return delete(key, UID_SELF);
133    }
134
135    public boolean contains(String key, int uid) {
136        try {
137            return mBinder.exist(key, uid) == NO_ERROR;
138        } catch (RemoteException e) {
139            Log.w(TAG, "Cannot connect to keystore", e);
140            return false;
141        }
142    }
143
144    public boolean contains(String key) {
145        return contains(key, UID_SELF);
146    }
147
148    public String[] saw(String prefix, int uid) {
149        try {
150            return mBinder.saw(prefix, uid);
151        } catch (RemoteException e) {
152            Log.w(TAG, "Cannot connect to keystore", e);
153            return null;
154        }
155    }
156
157    public String[] saw(String prefix) {
158        return saw(prefix, UID_SELF);
159    }
160
161    public boolean reset() {
162        try {
163            return mBinder.reset() == NO_ERROR;
164        } catch (RemoteException e) {
165            Log.w(TAG, "Cannot connect to keystore", e);
166            return false;
167        }
168    }
169
170    public boolean password(String password) {
171        try {
172            return mBinder.password(password) == NO_ERROR;
173        } catch (RemoteException e) {
174            Log.w(TAG, "Cannot connect to keystore", e);
175            return false;
176        }
177    }
178
179    public boolean lock() {
180        try {
181            return mBinder.lock() == NO_ERROR;
182        } catch (RemoteException e) {
183            Log.w(TAG, "Cannot connect to keystore", e);
184            return false;
185        }
186    }
187
188    public boolean unlock(String password) {
189        try {
190            mError = mBinder.unlock(password);
191            return mError == NO_ERROR;
192        } catch (RemoteException e) {
193            Log.w(TAG, "Cannot connect to keystore", e);
194            return false;
195        }
196    }
197
198    public boolean isEmpty() {
199        try {
200            return mBinder.zero() == KEY_NOT_FOUND;
201        } catch (RemoteException e) {
202            Log.w(TAG, "Cannot connect to keystore", e);
203            return false;
204        }
205    }
206
207    public boolean generate(String key, int uid, int keyType, int keySize, int flags,
208            byte[][] args) {
209        try {
210            return mBinder.generate(key, uid, keyType, keySize, flags, args) == NO_ERROR;
211        } catch (RemoteException e) {
212            Log.w(TAG, "Cannot connect to keystore", e);
213            return false;
214        }
215    }
216
217    public boolean importKey(String keyName, byte[] key, int uid, int flags) {
218        try {
219            return mBinder.import_key(keyName, key, uid, flags) == NO_ERROR;
220        } catch (RemoteException e) {
221            Log.w(TAG, "Cannot connect to keystore", e);
222            return false;
223        }
224    }
225
226    public byte[] getPubkey(String key) {
227        try {
228            return mBinder.get_pubkey(key);
229        } catch (RemoteException e) {
230            Log.w(TAG, "Cannot connect to keystore", e);
231            return null;
232        }
233    }
234
235    public boolean delKey(String key, int uid) {
236        try {
237            return mBinder.del_key(key, uid) == NO_ERROR;
238        } catch (RemoteException e) {
239            Log.w(TAG, "Cannot connect to keystore", e);
240            return false;
241        }
242    }
243
244    public boolean delKey(String key) {
245        return delKey(key, UID_SELF);
246    }
247
248    public byte[] sign(String key, byte[] data) {
249        try {
250            return mBinder.sign(key, data);
251        } catch (RemoteException e) {
252            Log.w(TAG, "Cannot connect to keystore", e);
253            return null;
254        }
255    }
256
257    public boolean verify(String key, byte[] data, byte[] signature) {
258        try {
259            return mBinder.verify(key, data, signature) == NO_ERROR;
260        } catch (RemoteException e) {
261            Log.w(TAG, "Cannot connect to keystore", e);
262            return false;
263        }
264    }
265
266    public boolean grant(String key, int uid) {
267        try {
268            return mBinder.grant(key, uid) == NO_ERROR;
269        } catch (RemoteException e) {
270            Log.w(TAG, "Cannot connect to keystore", e);
271            return false;
272        }
273    }
274
275    public boolean ungrant(String key, int uid) {
276        try {
277            return mBinder.ungrant(key, uid) == NO_ERROR;
278        } catch (RemoteException e) {
279            Log.w(TAG, "Cannot connect to keystore", e);
280            return false;
281        }
282    }
283
284    /**
285     * Returns the last modification time of the key in milliseconds since the
286     * epoch. Will return -1L if the key could not be found or other error.
287     */
288    public long getmtime(String key) {
289        try {
290            final long millis = mBinder.getmtime(key);
291            if (millis == -1L) {
292                return -1L;
293            }
294
295            return millis * 1000L;
296        } catch (RemoteException e) {
297            Log.w(TAG, "Cannot connect to keystore", e);
298            return -1L;
299        }
300    }
301
302    public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
303        try {
304            return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR;
305        } catch (RemoteException e) {
306            Log.w(TAG, "Cannot connect to keystore", e);
307            return false;
308        }
309    }
310
311    // TODO remove this when it's removed from Settings
312    public boolean isHardwareBacked() {
313        return isHardwareBacked("RSA");
314    }
315
316    public boolean isHardwareBacked(String keyType) {
317        try {
318            return mBinder.is_hardware_backed(keyType.toUpperCase(Locale.US)) == NO_ERROR;
319        } catch (RemoteException e) {
320            Log.w(TAG, "Cannot connect to keystore", e);
321            return false;
322        }
323    }
324
325    public boolean clearUid(int uid) {
326        try {
327            return mBinder.clear_uid(uid) == NO_ERROR;
328        } catch (RemoteException e) {
329            Log.w(TAG, "Cannot connect to keystore", e);
330            return false;
331        }
332    }
333
334    public boolean resetUid(int uid) {
335        try {
336            mError = mBinder.reset_uid(uid);
337            return mError == NO_ERROR;
338        } catch (RemoteException e) {
339            Log.w(TAG, "Cannot connect to keystore", e);
340            return false;
341        }
342    }
343
344    public boolean syncUid(int sourceUid, int targetUid) {
345        try {
346            mError = mBinder.sync_uid(sourceUid, targetUid);
347            return mError == NO_ERROR;
348        } catch (RemoteException e) {
349            Log.w(TAG, "Cannot connect to keystore", e);
350            return false;
351        }
352    }
353
354    public boolean passwordUid(String password, int uid) {
355        try {
356            mError = mBinder.password_uid(password, uid);
357            return mError == NO_ERROR;
358        } catch (RemoteException e) {
359            Log.w(TAG, "Cannot connect to keystore", e);
360            return false;
361        }
362    }
363
364    public int getLastError() {
365        return mError;
366    }
367}
368