diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java new file mode 100644 index 0000000000000..38f5b45ea1907 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 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.locksettings.recoverablekeystore; + +import android.security.keystore.AndroidKeyStoreSecretKey; + +/** + * Private key stored in AndroidKeyStore. Used to wrap recoverable keys before writing them to disk. + * + *
Identified by a generation ID, which increments whenever a new platform key is generated. A + * new key must be generated whenever the user disables their lock screen, as the decryption key is + * tied to lock-screen authentication. + * + *
One current platform key exists per profile on the device. (As each must be tied to a
+ * different user's lock screen.)
+ *
+ * @hide
+ */
+public class PlatformEncryptionKey {
+
+ private final int mGenerationId;
+ private final AndroidKeyStoreSecretKey mKey;
+
+ /**
+ * A new instance.
+ *
+ * @param generationId The generation ID of the key.
+ * @param key The secret key handle. Can be used to encrypt WITHOUT requiring screen unlock.
+ */
+ public PlatformEncryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+ mGenerationId = generationId;
+ mKey = key;
+ }
+
+ /**
+ * Returns the generation ID of the key.
+ */
+ public int getGenerationId() {
+ return mGenerationId;
+ }
+
+ /**
+ * Returns the actual key, which can only be used to encrypt.
+ */
+ public AndroidKeyStoreSecretKey getKey() {
+ return mKey;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
index 40c788997ba5a..b22ba4ec8bded 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
@@ -16,7 +16,6 @@
package com.android.server.locksettings.recoverablekeystore;
-import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.util.Log;
@@ -56,7 +55,7 @@ public class RecoverableKeyGenerator {
* @hide
*/
public static RecoverableKeyGenerator newInstance(
- AndroidKeyStoreSecretKey platformKey, RecoverableKeyStorage recoverableKeyStorage)
+ PlatformEncryptionKey platformKey, RecoverableKeyStorage recoverableKeyStorage)
throws NoSuchAlgorithmException {
// NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
// material, so that it can be synced to disk in encrypted form.
@@ -66,11 +65,11 @@ public class RecoverableKeyGenerator {
private final KeyGenerator mKeyGenerator;
private final RecoverableKeyStorage mRecoverableKeyStorage;
- private final AndroidKeyStoreSecretKey mPlatformKey;
+ private final PlatformEncryptionKey mPlatformKey;
private RecoverableKeyGenerator(
KeyGenerator keyGenerator,
- AndroidKeyStoreSecretKey platformKey,
+ PlatformEncryptionKey platformKey,
RecoverableKeyStorage recoverableKeyStorage) {
mKeyGenerator = keyGenerator;
mRecoverableKeyStorage = recoverableKeyStorage;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index f18e7961de5f1..a0e34c35b3147 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -44,6 +44,7 @@ public class WrappedKey {
private static final String APPLICATION_KEY_ALGORITHM = "AES";
private static final int GCM_TAG_LENGTH_BITS = 128;
+ private final int mPlatformKeyGenerationId;
private final byte[] mNonce;
private final byte[] mKeyMaterial;
@@ -55,8 +56,8 @@ public class WrappedKey {
* {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
* not expose its key material.
*/
- public static WrappedKey fromSecretKey(
- SecretKey wrappingKey, SecretKey key) throws InvalidKeyException, KeyStoreException {
+ public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key)
+ throws InvalidKeyException, KeyStoreException {
if (key.getEncoded() == null) {
throw new InvalidKeyException(
"key does not expose encoded material. It cannot be wrapped.");
@@ -70,7 +71,7 @@ public class WrappedKey {
"Android does not support AES/GCM/NoPadding. This should never happen.");
}
- cipher.init(Cipher.WRAP_MODE, wrappingKey);
+ cipher.init(Cipher.WRAP_MODE, wrappingKey.getKey());
byte[] encryptedKeyMaterial;
try {
encryptedKeyMaterial = cipher.wrap(key);
@@ -90,7 +91,10 @@ public class WrappedKey {
}
}
- return new WrappedKey(/*mNonce=*/ cipher.getIV(), /*mKeyMaterial=*/ encryptedKeyMaterial);
+ return new WrappedKey(
+ /*nonce=*/ cipher.getIV(),
+ /*keyMaterial=*/ encryptedKeyMaterial,
+ /*platformKeyGenerationId=*/ wrappingKey.getGenerationId());
}
/**
@@ -98,12 +102,14 @@ public class WrappedKey {
*
* @param nonce The nonce with which the key material was encrypted.
* @param keyMaterial The encrypted bytes of the key material.
+ * @param platformKeyGenerationId The generation ID of the key used to wrap this key.
*
* @hide
*/
- public WrappedKey(byte[] nonce, byte[] keyMaterial) {
+ public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
mNonce = nonce;
mKeyMaterial = keyMaterial;
+ mPlatformKeyGenerationId = platformKeyGenerationId;
}
/**
@@ -124,15 +130,13 @@ public class WrappedKey {
return mKeyMaterial;
}
-
/**
* Returns the generation ID of the platform key, with which this key was wrapped.
*
* @hide
*/
public int getPlatformKeyGenerationId() {
- // TODO(robertberry) Implement. See ag/3362855.
- return 1;
+ return mPlatformKeyGenerationId;
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
index 298a98822caa1..c13c779f29345 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -48,24 +48,24 @@ import javax.crypto.SecretKey;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecoverableKeyGeneratorTest {
+ private static final int TEST_GENERATION_ID = 3;
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String KEY_ALGORITHM = "AES";
private static final String TEST_ALIAS = "karlin";
private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey";
- @Mock
- RecoverableKeyStorage mRecoverableKeyStorage;
+ @Mock RecoverableKeyStorage mRecoverableKeyStorage;
@Captor ArgumentCaptor