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