1 /*
2  * Copyright (C) 2017 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 com.android.server.locksettings.recoverablekeystore.storage;
18 
19 import static android.security.keystore.recovery.RecoveryController.ERROR_KEY_NOT_FOUND;
20 import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
21 
22 import android.annotation.Nullable;
23 import android.os.ServiceSpecificException;
24 import android.security.KeyStore2;
25 import android.security.keystore.KeyProperties;
26 import android.security.keystore.KeyProtection;
27 import android.system.keystore2.Domain;
28 import android.system.keystore2.KeyDescriptor;
29 import android.system.keystore2.KeyPermission;
30 import android.util.Log;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
34 import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl;
35 
36 import java.security.KeyStore.SecretKeyEntry;
37 import java.security.KeyStoreException;
38 import java.util.Locale;
39 
40 import javax.crypto.spec.SecretKeySpec;
41 
42 /**
43  * Storage for Application keys in LockSettings service KeyStore namespace.
44  *
45  * <p> Uses KeyStore's grant mechanism to make keys usable by application process without
46  * revealing key material
47  */
48 public class ApplicationKeyStorage {
49     private static final String TAG = "RecoverableAppKeyStore";
50 
51     private static final String APPLICATION_KEY_ALIAS_PREFIX =
52             "com.android.server.locksettings.recoverablekeystore/application/";
53     private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
54 
55     private final KeyStoreProxy mKeyStore;
56 
57     /**
58      * Creates a new instance.
59      */
getInstance()60     public static ApplicationKeyStorage getInstance()
61             throws KeyStoreException {
62         return new ApplicationKeyStorage(
63                 new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()));
64     }
65 
66     @VisibleForTesting
ApplicationKeyStorage(KeyStoreProxy keyStore)67     ApplicationKeyStorage(KeyStoreProxy keyStore) {
68         mKeyStore = keyStore;
69     }
70 
71     /**
72      * Returns String representation of {@code KeyDescriptor} valid in application's namespace.
73      */
getGrantAlias(int userId, int uid, String alias)74     public @Nullable String getGrantAlias(int userId, int uid, String alias) {
75         Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
76         String keystoreAlias = getInternalAlias(userId, uid, alias);
77         return makeKeystoreEngineGrantString(uid, keystoreAlias);
78     }
79 
setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)80     public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
81             throws KeyStoreException {
82         Log.i(TAG, String.format(Locale.US, "Set %d/%d/%s: %d bytes of key material",
83                 userId, uid, alias, secretKey.length));
84         try {
85             mKeyStore.setEntry(
86                 getInternalAlias(userId, uid, alias),
87                 new SecretKeyEntry(
88                     new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)),
89                 new KeyProtection.Builder(
90                         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
91                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
92                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
93                     .build());
94         } catch (KeyStoreException e) {
95             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
96         }
97     }
98 
deleteEntry(int userId, int uid, String alias)99     public void deleteEntry(int userId, int uid, String alias) {
100         Log.i(TAG, String.format(Locale.US, "Del %d/%d/%s", userId, uid, alias));
101         try {
102             mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
103         } catch (KeyStoreException e) {
104             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
105         }
106     }
107 
108     /**
109      * Returns the alias in locksettins service's KeyStore namespace used for given application key.
110      *
111      * <p>These IDs look as follows:
112      * {@code com.security.recoverablekeystore/application/<userId>/<uid>/<alias>}
113      *
114      * @param userId The ID of the user
115      * @param uid The uid
116      * @param alias - alias in application's namespace
117      * @return The alias.
118      */
getInternalAlias(int userId, int uid, String alias)119     private String getInternalAlias(int userId, int uid, String alias) {
120         return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
121     }
122 
makeKeystoreEngineGrantString(int uid, String alias)123     private String makeKeystoreEngineGrantString(int uid, String alias) {
124         if (alias == null) {
125             return null;
126         }
127 
128         KeyDescriptor key = new KeyDescriptor();
129         key.domain = Domain.APP;
130         key.nspace = KeyProperties.NAMESPACE_APPLICATION;
131         key.alias = alias;
132         key.blob = null;
133 
134         int grantAccessVector = KeyPermission.USE | KeyPermission.GET_INFO | KeyPermission.DELETE;
135 
136         try {
137             key = KeyStore2.getInstance().grant(key, uid, grantAccessVector);
138         } catch (android.security.KeyStoreException e) {
139             if (e.getNumericErrorCode()
140                     == android.security.KeyStoreException.ERROR_KEY_DOES_NOT_EXIST) {
141                 Log.w(TAG, "Failed to get grant for KeyStore key - key not found");
142                 throw new ServiceSpecificException(ERROR_KEY_NOT_FOUND, e.getMessage());
143             }
144             Log.e(TAG, "Failed to get grant for KeyStore key.", e);
145             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
146         }
147         return String.format("%s%016X", APPLICATION_KEY_GRANT_PREFIX, key.nspace);
148     }
149 }
150