userId,
credentialType,
credential,
- () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb, userId));
+ () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb));
}
/**
throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
- PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey();
+ PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
mUserId, decryptKey.getGenerationId());
return WrappedKey.unwrapKeys(decryptKey, wrappedKeys);
private final Context mContext;
private final KeyStoreProxy mKeyStore;
private final RecoverableKeyStoreDb mDatabase;
- private final int mUserId;
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
* defined by {@code context}.
*
* @param context This should be the context of the RecoverableKeyStoreLoader service.
- * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws KeyStoreException if failed to initialize AndroidKeyStore.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
- * @throws InsecureUserException if the user does not have a lock screen set.
* @throws SecurityException if the caller does not have permission to write to /data/system.
*
* @hide
*/
- public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database,
- int userId)
- throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
- context = context.getApplicationContext();
- PlatformKeyManager keyManager = new PlatformKeyManager(
- userId,
- context,
+ public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database)
+ throws KeyStoreException, NoSuchAlgorithmException {
+ return new PlatformKeyManager(
+ context.getApplicationContext(),
new KeyStoreProxyImpl(getAndLoadAndroidKeyStore()),
database);
- keyManager.init();
- return keyManager;
}
@VisibleForTesting
PlatformKeyManager(
- int userId,
Context context,
KeyStoreProxy keyStore,
RecoverableKeyStoreDb database) {
- mUserId = userId;
mKeyStore = keyStore;
mContext = context;
mDatabase = database;
* key has to be replaced. (e.g., because the user has removed and then re-added their lock
* screen). Returns -1 if no key has been generated yet.
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
+ *
* @hide
*/
- public int getGenerationId() {
- return mDatabase.getPlatformKeyGenerationId(mUserId);
+ public int getGenerationId(int userId) {
+ return mDatabase.getPlatformKeyGenerationId(userId);
}
/**
* Returns {@code true} if the platform key is available. A platform key won't be available if
* the user has not set up a lock screen.
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
+ *
* @hide
*/
- public boolean isAvailable() {
- return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(mUserId);
+ public boolean isAvailable(int userId) {
+ return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId);
}
/**
* Generates a new key and increments the generation ID. Should be invoked if the platform key
* is corrupted and needs to be rotated.
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
* @throws KeyStoreException if there is an error in AndroidKeyStore.
+ * @throws InsecureUserException if the user does not have a lock screen set.
*
* @hide
*/
- public void regenerate() throws NoSuchAlgorithmException, KeyStoreException {
- int nextId = getGenerationId() + 1;
- generateAndLoadKey(nextId);
+ public void regenerate(int userId)
+ throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException {
+ if (!isAvailable(userId)) {
+ throw new InsecureUserException(String.format(
+ Locale.US, "%d does not have a lock screen set.", userId));
+ }
+
+ int generationId = getGenerationId(userId);
+ int nextId;
+ if (generationId == -1) {
+ nextId = 1;
+ } else {
+ nextId = generationId + 1;
+ }
+ generateAndLoadKey(userId, nextId);
}
/**
* Returns the platform key used for encryption.
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws KeyStoreException if there was an AndroidKeyStore error.
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+ * @throws InsecureUserException if the user does not have a lock screen set.
*
* @hide
*/
- public PlatformEncryptionKey getEncryptKey()
- throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
- int generationId = getGenerationId();
+ public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException,
+ UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+ init(userId);
+ int generationId = getGenerationId(userId);
AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
- getEncryptAlias(generationId), /*password=*/ null);
+ getEncryptAlias(userId, generationId), /*password=*/ null);
return new PlatformEncryptionKey(generationId, key);
}
/**
* Returns the platform key used for decryption. Only works after a recent screen unlock.
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws KeyStoreException if there was an AndroidKeyStore error.
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+ * @throws InsecureUserException if the user does not have a lock screen set.
*
* @hide
*/
- public PlatformDecryptionKey getDecryptKey()
- throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
- int generationId = getGenerationId();
+ public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
+ UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+ init(userId);
+ int generationId = getGenerationId(userId);
AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
- getDecryptAlias(generationId), /*password=*/ null);
+ getDecryptAlias(userId, generationId), /*password=*/ null);
return new PlatformDecryptionKey(generationId, key);
}
* Initializes the class. If there is no current platform key, and the user has a lock screen
* set, will create the platform key and set the generation ID.
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws KeyStoreException if there was an error in AndroidKeyStore.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
*
* @hide
*/
- public void init() throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
- if (!isAvailable()) {
+ void init(int userId)
+ throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
+ if (!isAvailable(userId)) {
throw new InsecureUserException(String.format(
- Locale.US, "%d does not have a lock screen set.", mUserId));
+ Locale.US, "%d does not have a lock screen set.", userId));
}
- int generationId = getGenerationId();
- if (isKeyLoaded(generationId)) {
+ int generationId = getGenerationId(userId);
+ if (isKeyLoaded(userId, generationId)) {
Log.i(TAG, String.format(
Locale.US, "Platform key generation %d exists already.", generationId));
return;
}
if (generationId == -1) {
Log.i(TAG, "Generating initial platform ID.");
+ generationId = 1;
} else {
Log.w(TAG, String.format(Locale.US, "Platform generation ID was %d but no "
+ "entry was present in AndroidKeyStore. Generating fresh key.", generationId));
- }
-
- if (generationId == -1) {
- generationId = 1;
- } else {
// Had to generate a fresh key, bump the generation id
generationId++;
}
- generateAndLoadKey(generationId);
+ generateAndLoadKey(userId, generationId);
}
/**
* <p>These IDs look as follows:
* {@code com.security.recoverablekeystore/platform/<user id>/<generation id>/encrypt}
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @param generationId The generation ID.
* @return The alias.
*/
- private String getEncryptAlias(int generationId) {
- return KEY_ALIAS_PREFIX + mUserId + "/" + generationId + "/" + ENCRYPT_KEY_ALIAS_SUFFIX;
+ private String getEncryptAlias(int userId, int generationId) {
+ return KEY_ALIAS_PREFIX + userId + "/" + generationId + "/" + ENCRYPT_KEY_ALIAS_SUFFIX;
}
/**
* <p>These IDs look as follows:
* {@code com.security.recoverablekeystore/platform/<user id>/<generation id>/decrypt}
*
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
* @param generationId The generation ID.
* @return The alias.
*/
- private String getDecryptAlias(int generationId) {
- return KEY_ALIAS_PREFIX + mUserId + "/" + generationId + "/" + DECRYPT_KEY_ALIAS_SUFFIX;
+ private String getDecryptAlias(int userId, int generationId) {
+ return KEY_ALIAS_PREFIX + userId + "/" + generationId + "/" + DECRYPT_KEY_ALIAS_SUFFIX;
}
/**
* Sets the current generation ID to {@code generationId}.
*/
- private void setGenerationId(int generationId) {
- mDatabase.setPlatformKeyGenerationId(mUserId, generationId);
+ private void setGenerationId(int userId, int generationId) {
+ mDatabase.setPlatformKeyGenerationId(userId, generationId);
}
/**
*
* @throws KeyStoreException if there was an error checking AndroidKeyStore.
*/
- private boolean isKeyLoaded(int generationId) throws KeyStoreException {
- return mKeyStore.containsAlias(getEncryptAlias(generationId))
- && mKeyStore.containsAlias(getDecryptAlias(generationId));
+ private boolean isKeyLoaded(int userId, int generationId) throws KeyStoreException {
+ return mKeyStore.containsAlias(getEncryptAlias(userId, generationId))
+ && mKeyStore.containsAlias(getDecryptAlias(userId, generationId));
}
/**
* available since API version 1.
* @throws KeyStoreException if there was an issue loading the keys into AndroidKeyStore.
*/
- private void generateAndLoadKey(int generationId)
+ private void generateAndLoadKey(int userId, int generationId)
throws NoSuchAlgorithmException, KeyStoreException {
- String encryptAlias = getEncryptAlias(generationId);
- String decryptAlias = getDecryptAlias(generationId);
+ String encryptAlias = getEncryptAlias(userId, generationId);
+ String decryptAlias = getDecryptAlias(userId, generationId);
SecretKey secretKey = generateAesKey();
mKeyStore.setEntry(
USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .setBoundToSpecificSecureUserId(mUserId)
+ .setBoundToSpecificSecureUserId(userId)
.build());
- setGenerationId(generationId);
+ setGenerationId(userId, generationId);
try {
secretKey.destroy();
private final RecoverySnapshotListenersStorage mListenersStorage;
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
+ private final PlatformKeyManager mPlatformKeyManager;
/**
* Returns a new or existing instance.
*
* @hide
*/
- public static synchronized RecoverableKeyStoreManager getInstance(Context mContext) {
+ public static synchronized RecoverableKeyStoreManager getInstance(Context context) {
if (mInstance == null) {
- RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(mContext);
+ RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
+ PlatformKeyManager platformKeyManager;
+ try {
+ platformKeyManager = PlatformKeyManager.getInstance(context, db);
+ } catch (NoSuchAlgorithmException e) {
+ // Impossible: all algorithms must be supported by AOSP
+ throw new RuntimeException(e);
+ } catch (KeyStoreException e) {
+ throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
+ }
+
mInstance = new RecoverableKeyStoreManager(
- mContext.getApplicationContext(),
+ context.getApplicationContext(),
db,
new RecoverySessionStorage(),
Executors.newSingleThreadExecutor(),
new RecoverySnapshotStorage(),
- new RecoverySnapshotListenersStorage());
+ new RecoverySnapshotListenersStorage(),
+ platformKeyManager);
}
return mInstance;
}
RecoverySessionStorage recoverySessionStorage,
ExecutorService executorService,
RecoverySnapshotStorage snapshotStorage,
- RecoverySnapshotListenersStorage listenersStorage) {
+ RecoverySnapshotListenersStorage listenersStorage,
+ PlatformKeyManager platformKeyManager) {
mContext = context;
mDatabase = recoverableKeyStoreDb;
mRecoverySessionStorage = recoverySessionStorage;
mExecutorService = executorService;
mListenersStorage = listenersStorage;
mSnapshotStorage = snapshotStorage;
+ mPlatformKeyManager = platformKeyManager;
+
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
} catch (NoSuchAlgorithmException e) {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
- PlatformEncryptionKey encryptionKey;
+ PlatformEncryptionKey encryptionKey;
try {
- PlatformKeyManager platformKeyManager = PlatformKeyManager.getInstance(
- mContext, mDatabase, userId);
- encryptionKey = platformKeyManager.getEncryptKey();
+ encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
} catch (NoSuchAlgorithmException e) {
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);
mWrappingKey = generateAndroidKeyStoreKey();
mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
- when(mPlatformKeyManager.getDecryptKey()).thenReturn(
+ when(mPlatformKeyManager.getDecryptKey(TEST_USER_ID)).thenReturn(
new PlatformDecryptionKey(TEST_GENERATION_ID, mWrappingKey));
}
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
mPlatformKeyManager = new PlatformKeyManager(
- USER_ID_FIXTURE, mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
+ mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
when(mContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
when(mContext.getSystemServiceName(any())).thenReturn("test");
@Test
public void init_createsEncryptKeyWithCorrectAlias() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
@Test
public void init_createsEncryptKeyWithCorrectPurposes() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertEquals(KeyProperties.PURPOSE_ENCRYPT, getEncryptKeyProtection().getPurposes());
}
@Test
public void init_createsEncryptKeyWithCorrectPaddings() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertArrayEquals(
new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
@Test
public void init_createsEncryptKeyWithCorrectBlockModes() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertArrayEquals(
new String[] { KeyProperties.BLOCK_MODE_GCM },
@Test
public void init_createsEncryptKeyWithoutAuthenticationRequired() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertFalse(getEncryptKeyProtection().isUserAuthenticationRequired());
}
@Test
public void init_createsDecryptKeyWithCorrectAlias() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
@Test
public void init_createsDecryptKeyWithCorrectPurposes() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertEquals(KeyProperties.PURPOSE_DECRYPT, getDecryptKeyProtection().getPurposes());
}
@Test
public void init_createsDecryptKeyWithCorrectPaddings() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertArrayEquals(
new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
@Test
public void init_createsDecryptKeyWithCorrectBlockModes() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertArrayEquals(
new String[] { KeyProperties.BLOCK_MODE_GCM },
@Test
public void init_createsDecryptKeyWithAuthenticationRequired() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired());
}
@Test
public void init_createsDecryptKeyWithAuthenticationValidFor15Seconds() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertEquals(
USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS,
@Test
public void init_createsDecryptKeyBoundToTheUsersAuthentication() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertEquals(
USER_ID_FIXTURE,
@Test
public void init_createsBothKeysWithSameMaterial() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
verify(mKeyStoreProxy, times(2)).setEntry(any(), mEntryArgumentCaptor.capture(), any());
List<KeyStore.Entry> entries = mEntryArgumentCaptor.getAllValues();
@Test
public void init_savesGenerationIdToDatabase() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
assertEquals(1,
mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE));
@Test
public void init_setsGenerationIdTo1() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(1, mPlatformKeyManager.getGenerationId());
+ assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
public void init_incrementsGenerationIdIfKeyIsUnavailable() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(2, mPlatformKeyManager.getGenerationId());
+ assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/1/decrypt")).thenReturn(true);
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/1/encrypt")).thenReturn(true);
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(1, mPlatformKeyManager.getGenerationId());
+ assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
public void getGenerationId_returnsMinusOneIfNotInitialized() throws Exception {
- assertEquals(-1, mPlatformKeyManager.getGenerationId());
+ assertEquals(-1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
- mPlatformKeyManager.init();
-
- mPlatformKeyManager.getDecryptKey();
+ mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).getKey(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
@Test
public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
- mPlatformKeyManager.init();
-
- mPlatformKeyManager.getEncryptKey();
+ mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).getKey(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
@Test
public void regenerate_incrementsTheGenerationId() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- mPlatformKeyManager.regenerate();
+ mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
- assertEquals(2, mPlatformKeyManager.getGenerationId());
+ assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
public void regenerate_generatesANewEncryptKeyWithTheCorrectAlias() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- mPlatformKeyManager.regenerate();
+ mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
@Test
public void regenerate_generatesANewDecryptKeyWithTheCorrectAlias() throws Exception {
- mPlatformKeyManager.init();
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
- mPlatformKeyManager.regenerate();
+ mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
-import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.security.recoverablekeystore.KeyDerivationParameters;
import android.security.recoverablekeystore.KeyEntryRecoveryData;
import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
import java.util.Map;
import java.util.Random;
-import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
@SmallTest
public class RecoverableKeyStoreManagerTest {
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
- private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
private static final String TEST_SESSION_ID = "karlin";
private static final byte[] TEST_PUBLIC_KEY = new byte[] {
(byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
private static final byte[] TEST_SECRET = getUtf8Bytes("password1234");
private static final byte[] TEST_VAULT_CHALLENGE = getUtf8Bytes("vault_challenge");
private static final byte[] TEST_VAULT_PARAMS = getUtf8Bytes("vault_params");
+ private static final int TEST_GENERATION_ID = 2;
+ private static final int TEST_USER_ID = 10009;
private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
private static final byte[] RECOVERY_RESPONSE_HEADER =
"V1 reencrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
private static final int GENERATION_ID = 1;
private static final byte[] NONCE = getUtf8Bytes("nonce");
private static final byte[] KEY_MATERIAL = getUtf8Bytes("keymaterial");
- private static final int GCM_TAG_SIZE_BITS = 128;
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
@Mock private Context mMockContext;
@Mock private RecoverySnapshotListenersStorage mMockListenersStorage;
@Mock private KeyguardManager mKeyguardManager;
+ @Mock private PlatformKeyManager mPlatformKeyManager;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
private RecoverySessionStorage mRecoverySessionStorage;
private RecoverySnapshotStorage mRecoverySnapshotStorage;
+ private PlatformEncryptionKey mPlatformEncryptionKey;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
when(mMockContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
when(mMockContext.getSystemServiceName(any())).thenReturn("test");
when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
- when(mKeyguardManager.isDeviceSecure(anyInt())).thenReturn(true);
+ when(mKeyguardManager.isDeviceSecure(TEST_USER_ID)).thenReturn(true);
+
+ mPlatformEncryptionKey =
+ new PlatformEncryptionKey(TEST_GENERATION_ID, generateAndroidKeyStoreKey());
+ when(mPlatformKeyManager.getEncryptKey(anyInt())).thenReturn(mPlatformEncryptionKey);
mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
mMockContext,
mRecoverySessionStorage,
Executors.newSingleThreadExecutor(),
mRecoverySnapshotStorage,
- mMockListenersStorage);
+ mMockListenersStorage,
+ mPlatformKeyManager);
}
@After
TEST_VAULT_CHALLENGE,
ImmutableList.of());
fail("should have thrown");
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
assertEquals("Only a single KeyStoreRecoveryMetadata is supported", e.getMessage());
}
}
KeyDerivationParameters.createSHA256Parameters(TEST_SALT),
TEST_SECRET)));
fail("should have thrown");
- } catch (RemoteException e) {
+ } catch (ServiceSpecificException e) {
assertEquals("Not a valid X509 key", e.getMessage());
}
}
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).startsWith("Failed to decrypt recovery key");
- //assertEquals("Failed to decrypt recovery key", e.getMessage());
}
}
/*encryptedRecoveryKey=*/ encryptedClaimResponse,
/*applicationKeys=*/ ImmutableList.of(badApplicationKey));
fail("should have thrown");
- } catch (RemoteException e) {
- assertEquals("Failed to recover key with alias 'nick'", e.getMessage());
+ } catch (ServiceSpecificException e) {
+ assertThat(e.getMessage()).startsWith("Failed to recover key with alias 'nick'");
}
}
new Random().nextBytes(bytes);
return bytes;
}
+
+ private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KEY_ALGORITHM,
+ ANDROID_KEY_STORE_PROVIDER);
+ keyGenerator.init(new KeyGenParameterSpec.Builder(
+ WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ }
}