blob: c89076b9928f08d41de03026ebca8f0e1962024c [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 android.content.Context;
import android.security.keystore.recovery.InternalRecoveryServiceException;
import android.security.keystore.recovery.LockScreenRequiredException;
import android.security.keystore.recovery.RecoveryController;
import com.android.internal.annotations.VisibleForTesting;
import libcore.util.HexEncoding;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.util.Optional;
import javax.crypto.SecretKey;
/**
* Manages generating, deleting, and retrieving secondary keys through {@link RecoveryController}.
*
* <p>The recoverable key store will be synced remotely via the {@link RecoveryController}, allowing
* recovery of keys on other devices owned by the user.
*/
public class RecoverableKeyStoreSecondaryKeyManager {
private static final String BACKUP_KEY_ALIAS_PREFIX =
"com.android.server.backup/recoverablekeystore/";
private static final int BACKUP_KEY_SUFFIX_LENGTH_BITS = 128;
private static final int BITS_PER_BYTE = 8;
/** A new instance. */
public static RecoverableKeyStoreSecondaryKeyManager getInstance(Context context) {
return new RecoverableKeyStoreSecondaryKeyManager(
RecoveryController.getInstance(context), new SecureRandom());
}
private final RecoveryController mRecoveryController;
private final SecureRandom mSecureRandom;
@VisibleForTesting
public RecoverableKeyStoreSecondaryKeyManager(
RecoveryController recoveryController, SecureRandom secureRandom) {
mRecoveryController = recoveryController;
mSecureRandom = secureRandom;
}
/**
* Generates a new recoverable key using the {@link RecoveryController}.
*
* @throws InternalRecoveryServiceException if an unexpected error occurred generating the key.
* @throws LockScreenRequiredException if the user does not have a lock screen. A lock screen is
* required to generate a recoverable key.
*/
public RecoverableKeyStoreSecondaryKey generate()
throws InternalRecoveryServiceException, LockScreenRequiredException,
UnrecoverableKeyException {
String alias = generateId();
mRecoveryController.generateKey(alias);
SecretKey key = (SecretKey) mRecoveryController.getKey(alias);
if (key == null) {
throw new InternalRecoveryServiceException(
String.format(
"Generated key %s but could not get it back immediately afterwards.",
alias));
}
return new RecoverableKeyStoreSecondaryKey(alias, key);
}
/**
* Removes the secondary key. This means the key will no longer be recoverable.
*
* @param alias The alias of the key.
* @throws InternalRecoveryServiceException if there was a {@link RecoveryController} error.
*/
public void remove(String alias) throws InternalRecoveryServiceException {
mRecoveryController.removeKey(alias);
}
/**
* Returns the {@link RecoverableKeyStoreSecondaryKey} with {@code alias} if it is in the {@link
* RecoveryController}. Otherwise, {@link Optional#empty()}.
*/
public Optional<RecoverableKeyStoreSecondaryKey> get(String alias)
throws InternalRecoveryServiceException, UnrecoverableKeyException {
SecretKey secretKey = (SecretKey) mRecoveryController.getKey(alias);
return Optional.ofNullable(secretKey)
.map(key -> new RecoverableKeyStoreSecondaryKey(alias, key));
}
/**
* Generates a new key alias. This has more entropy than a UUID - it can be considered
* universally unique.
*/
private String generateId() {
byte[] id = new byte[BACKUP_KEY_SUFFIX_LENGTH_BITS / BITS_PER_BYTE];
mSecureRandom.nextBytes(id);
return BACKUP_KEY_ALIAS_PREFIX + HexEncoding.encodeToString(id);
}
/** Constructs a {@link RecoverableKeyStoreSecondaryKeyManager}. */
public interface RecoverableKeyStoreSecondaryKeyManagerProvider {
/** Returns a newly constructed {@link RecoverableKeyStoreSecondaryKeyManager}. */
RecoverableKeyStoreSecondaryKeyManager get();
}
}