Home | History | Annotate | Download | only in storage
      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.Credentials;
     24 import android.security.keystore.KeyProperties;
     25 import android.security.keystore.KeyProtection;
     26 import android.security.KeyStore;
     27 import android.util.Log;
     28 
     29 import com.android.internal.annotations.VisibleForTesting;
     30 import com.android.server.locksettings.recoverablekeystore.KeyStoreProxy;
     31 import com.android.server.locksettings.recoverablekeystore.KeyStoreProxyImpl;
     32 
     33 import java.security.KeyStore.SecretKeyEntry;
     34 import java.security.KeyStoreException;
     35 import java.util.Locale;
     36 
     37 import javax.crypto.spec.SecretKeySpec;
     38 
     39 /**
     40  * Storage for Application keys in LockSettings service KeyStore namespace.
     41  *
     42  * <p> Uses KeyStore's grant mechanism to make keys usable by application process without
     43  * revealing key material
     44  */
     45 public class ApplicationKeyStorage {
     46     private static final String TAG = "RecoverableAppKeyStore";
     47 
     48     private static final String APPLICATION_KEY_ALIAS_PREFIX =
     49             "com.android.server.locksettings.recoverablekeystore/application/";
     50 
     51     private final KeyStoreProxy mKeyStore;
     52     private final KeyStore mKeystoreService;
     53 
     54     public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
     55             throws KeyStoreException {
     56         return new ApplicationKeyStorage(
     57                 new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()),
     58                 keystoreService);
     59     }
     60 
     61     @VisibleForTesting
     62     ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) {
     63         mKeyStore = keyStore;
     64         mKeystoreService = keystoreService;
     65     }
     66 
     67     /**
     68      * Returns grant alias, valid in Applications namespace.
     69      */
     70     public @Nullable String getGrantAlias(int userId, int uid, String alias) {
     71         // Aliases used by {@link KeyStore} are different than used by public API.
     72         // {@code USER_PRIVATE_KEY} prefix is used secret keys.
     73         Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
     74         String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
     75         return mKeystoreService.grant(keystoreAlias, uid);
     76     }
     77 
     78     public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
     79             throws KeyStoreException {
     80         Log.i(TAG, String.format(Locale.US, "Set %d/%d/%s: %d bytes of key material",
     81                 userId, uid, alias, secretKey.length));
     82         try {
     83             mKeyStore.setEntry(
     84                 getInternalAlias(userId, uid, alias),
     85                 new SecretKeyEntry(
     86                     new SecretKeySpec(secretKey, KeyProperties.KEY_ALGORITHM_AES)),
     87                 new KeyProtection.Builder(
     88                         KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
     89                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
     90                     .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
     91                     .build());
     92         } catch (KeyStoreException e) {
     93             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
     94         }
     95     }
     96 
     97     public void deleteEntry(int userId, int uid, String alias) {
     98         Log.i(TAG, String.format(Locale.US, "Del %d/%d/%s", userId, uid, alias));
     99         try {
    100             mKeyStore.deleteEntry(getInternalAlias(userId, uid, alias));
    101         } catch (KeyStoreException e) {
    102             throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
    103         }
    104     }
    105 
    106     /**
    107      * Returns the alias in locksettins service's KeyStore namespace used for given application key.
    108      *
    109      * <p>These IDs look as follows:
    110      * {@code com.security.recoverablekeystore/application/<userId>/<uid>/<alias>}
    111      *
    112      * @param userId The ID of the user
    113      * @param uid The uid
    114      * @param alias - alias in application's namespace
    115      * @return The alias.
    116      */
    117     private String getInternalAlias(int userId, int uid, String alias) {
    118         return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
    119     }
    120 }
    121