blob: f356b4f102e2b820fc27566dfd9827615ff048e0 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.backup.encryption.keys;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.content.Context;
import android.security.keystore.recovery.InternalRecoveryServiceException;
import android.security.keystore.recovery.RecoveryController;
import android.util.Slog;
import javax.crypto.SecretKey;
/**
* Wraps a {@link RecoveryController}'s {@link SecretKey}. These are kept in "AndroidKeyStore" (a
* provider for {@link java.security.KeyStore} and {@link javax.crypto.KeyGenerator}. They are also
* synced with the recoverable key store, wrapped by the primary key. This allows them to be
* recovered on a user's subsequent device through providing their lock screen secret.
*/
public class RecoverableKeyStoreSecondaryKey {
private static final String TAG = "RecoverableKeyStoreSecondaryKey";
private final String mAlias;
private final SecretKey mSecretKey;
/**
* A new instance.
*
* @param alias The alias. It is keyed with this in AndroidKeyStore and the recoverable key
* store.
* @param secretKey The key.
*/
public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
mAlias = checkNotNull(alias);
mSecretKey = checkNotNull(secretKey);
}
/**
* The ID, as stored in the recoverable {@link java.security.KeyStore}, and as used to identify
* wrapped tertiary keys on the backup server.
*/
public String getAlias() {
return mAlias;
}
/** The secret key, to be used to wrap tertiary keys. */
public SecretKey getSecretKey() {
return mSecretKey;
}
/**
* The status of the key. i.e., whether it's been synced to remote trusted hardware.
*
* @param context The application context.
* @return One of {@link Status#SYNCED}, {@link Status#NOT_SYNCED} or {@link Status#DESTROYED}.
*/
public @Status int getStatus(Context context) {
try {
return getStatusInternal(context);
} catch (InternalRecoveryServiceException e) {
Slog.wtf(TAG, "Internal error getting recovery status", e);
// Return NOT_SYNCED by default, as we do not want the backups to fail or to repeatedly
// attempt to reinitialize.
return Status.NOT_SYNCED;
}
}
private @Status int getStatusInternal(Context context) throws InternalRecoveryServiceException {
int status = RecoveryController.getInstance(context).getRecoveryStatus(mAlias);
switch (status) {
case RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE:
return Status.DESTROYED;
case RecoveryController.RECOVERY_STATUS_SYNCED:
return Status.SYNCED;
case RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS:
return Status.NOT_SYNCED;
default:
// Throw an exception if we encounter a status that doesn't match any of the above.
throw new InternalRecoveryServiceException(
"Unexpected status from getRecoveryStatus: " + status);
}
}
/** Status of a key in the recoverable key store. */
@IntDef({Status.NOT_SYNCED, Status.SYNCED, Status.DESTROYED})
public @interface Status {
/**
* The key has not yet been synced to remote trusted hardware. This may be because the user
* has not yet unlocked their device.
*/
int NOT_SYNCED = 1;
/**
* The key has been synced with remote trusted hardware. It should now be recoverable on
* another device.
*/
int SYNCED = 2;
/** The key has been lost forever. This can occur if the user disables their lock screen. */
int DESTROYED = 3;
}
}