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