summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bo Zhu <bozhu@google.com> 2019-01-03 14:04:58 -0800
committer Bo Zhu <bozhu@google.com> 2019-01-17 17:38:19 -0800
commitc704834cb65eee08282ba2a53be2d15cecbdd866 (patch)
tree3388347d0b9537da4f7051311f62efa1146a32d6
parent252e8d0447eb64a1429a1198ff71f3785db2d2aa (diff)
Add an optional metadata blob for recoverable application keys
This metadata, if present, will be authenticated (but unencrypted) together with the application key material. Bug: 112191661 Test: atest FrameworksCoreTests:android.security.keystore.recovery atest FrameworksServicesTests:com.android.server.locksettings.recoverablekeystore atest -m RecoveryControllerHostTest RecoverableKeyStoreEndtoEndHostTest RecoverySessionHostTest Change-Id: I2846952758a2c1a7b1f0849e1adda1f05a3e305e
-rw-r--r--api/system-current.txt8
-rw-r--r--core/java/android/security/keystore/recovery/RecoveryController.java89
-rw-r--r--core/java/android/security/keystore/recovery/WrappedApplicationKey.java28
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl2
-rw-r--r--core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java4
-rw-r--r--core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java53
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java25
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java50
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java17
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java1
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java110
12 files changed, 358 insertions, 42 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 7f3c152364b0..4fece4057723 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5092,7 +5092,8 @@ package android.security.keystore.recovery {
public class RecoveryController {
method public android.security.keystore.recovery.RecoverySession createRecoverySession();
- method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+ method public deprecated java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+ method public java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
@@ -5100,7 +5101,8 @@ package android.security.keystore.recovery {
method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public java.util.Map<java.lang.String, java.security.cert.X509Certificate> getRootCertificates();
- method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+ method public deprecated java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+ method public java.security.Key importKey(java.lang.String, byte[], byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public static boolean isRecoverableKeyStoreEnabled(android.content.Context);
method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -5127,6 +5129,7 @@ package android.security.keystore.recovery {
method public int describeContents();
method public java.lang.String getAlias();
method public byte[] getEncryptedKeyMaterial();
+ method public byte[] getMetadata();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.WrappedApplicationKey> CREATOR;
}
@@ -5136,6 +5139,7 @@ package android.security.keystore.recovery {
method public android.security.keystore.recovery.WrappedApplicationKey build();
method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String);
method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]);
+ method public android.security.keystore.recovery.WrappedApplicationKey.Builder setMetadata(byte[]);
}
}
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 31a5962c7e9a..c43a6668b9c3 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -533,7 +533,10 @@ public class RecoveryController {
* service.
* @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
* screen is required to generate recoverable keys.
+ *
+ * @deprecated Use the method {@link #generateKey(String, byte[])} instead.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
public @NonNull Key generateKey(@NonNull String alias) throws InternalRecoveryServiceException,
LockScreenRequiredException {
@@ -556,6 +559,47 @@ public class RecoveryController {
}
/**
+ * Generates a recoverable key with the given {@code alias} and {@code metadata}.
+ *
+ * <p>The metadata should contain any data that needs to be cryptographically bound to the
+ * generated key, but does not need to be encrypted by the key. For example, the metadata can
+ * be a byte string describing the algorithms and non-secret parameters to be used with the
+ * key. The supplied metadata can later be obtained via
+ * {@link WrappedApplicationKey#getMetadata()}.
+ *
+ * <p>During the key recovery process, the same metadata has to be supplied via
+ * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process
+ * will fail due to the checking of the cryptographic binding. This can help prevent
+ * potential attacks that try to swap key materials on the backup server and trick the
+ * application to use keys with different algorithms or parameters.
+ *
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+ * screen is required to generate recoverable keys.
+ */
+ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+ public @NonNull Key generateKey(@NonNull String alias, @Nullable byte[] metadata)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ try {
+ String grantAlias = mBinder.generateKeyWithMetadata(alias, metadata);
+ if (grantAlias == null) {
+ throw new InternalRecoveryServiceException("null grant alias");
+ }
+ return getKeyFromGrant(grantAlias);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (UnrecoverableKeyException e) {
+ throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_INSECURE_USER) {
+ throw new LockScreenRequiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code
* keyBytes}.
*
@@ -564,7 +608,9 @@ public class RecoveryController {
* @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
* screen is required to generate recoverable keys.
*
+ * @deprecated Use the method {@link #importKey(String, byte[], byte[])} instead.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes)
throws InternalRecoveryServiceException, LockScreenRequiredException {
@@ -587,6 +633,49 @@ public class RecoveryController {
}
/**
+ * Imports a recoverable 256-bit AES key with the given {@code alias}, the raw bytes {@code
+ * keyBytes}, and the {@code metadata}.
+ *
+ * <p>The metadata should contain any data that needs to be cryptographically bound to the
+ * imported key, but does not need to be encrypted by the key. For example, the metadata can
+ * be a byte string describing the algorithms and non-secret parameters to be used with the
+ * key. The supplied metadata can later be obtained via
+ * {@link WrappedApplicationKey#getMetadata()}.
+ *
+ * <p>During the key recovery process, the same metadata has to be supplied via
+ * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process
+ * will fail due to the checking of the cryptographic binding. This can help prevent
+ * potential attacks that try to swap key materials on the backup server and trick the
+ * application to use keys with different algorithms or parameters.
+ *
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+ * screen is required to generate recoverable keys.
+ */
+ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+ public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes,
+ @Nullable byte[] metadata)
+ throws InternalRecoveryServiceException, LockScreenRequiredException {
+ try {
+ String grantAlias = mBinder.importKeyWithMetadata(alias, keyBytes, metadata);
+ if (grantAlias == null) {
+ throw new InternalRecoveryServiceException("Null grant alias");
+ }
+ return getKeyFromGrant(grantAlias);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (UnrecoverableKeyException e) {
+ throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == ERROR_INSECURE_USER) {
+ throw new LockScreenRequiredException(e.getMessage());
+ }
+ throw wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Gets a key called {@code alias} from the recoverable key store.
*
* @param alias The key alias.
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index ae4448f9c908..dbfd655953d6 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -17,6 +17,7 @@
package android.security.keystore.recovery;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +42,8 @@ public final class WrappedApplicationKey implements Parcelable {
private String mAlias;
// The only supported format is AES-256 symmetric key.
private byte[] mEncryptedKeyMaterial;
+ // The optional metadata that's authenticated (but unencrypted) with the key material.
+ private byte[] mMetadata;
// IMPORTANT! PLEASE READ!
// -----------------------
@@ -80,13 +83,23 @@ public final class WrappedApplicationKey implements Parcelable {
* @param encryptedKeyMaterial The key material
* @return This builder
*/
-
public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) {
mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial;
return this;
}
/**
+ * Sets the metadata that is authenticated (but unecrypted) with the key material.
+ *
+ * @param metadata The metadata
+ * @return This builder
+ */
+ public Builder setMetadata(@Nullable byte[] metadata) {
+ mInstance.mMetadata = metadata;
+ return this;
+ }
+
+ /**
* Creates a new {@link WrappedApplicationKey} instance.
*
* @return new instance
@@ -102,9 +115,10 @@ public final class WrappedApplicationKey implements Parcelable {
private WrappedApplicationKey() { }
/**
- * Deprecated - consider using Builder.
+ * @deprecated Use the builder instead.
* @hide
*/
+ @Deprecated
public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
mAlias = Preconditions.checkNotNull(alias);
mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
@@ -124,6 +138,11 @@ public final class WrappedApplicationKey implements Parcelable {
return mEncryptedKeyMaterial;
}
+ /** The metadata with the key. */
+ public @Nullable byte[] getMetadata() {
+ return mMetadata;
+ }
+
public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
new Parcelable.Creator<WrappedApplicationKey>() {
public WrappedApplicationKey createFromParcel(Parcel in) {
@@ -139,6 +158,7 @@ public final class WrappedApplicationKey implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeString(mAlias);
out.writeByteArray(mEncryptedKeyMaterial);
+ out.writeByteArray(mMetadata);
}
/**
@@ -147,6 +167,10 @@ public final class WrappedApplicationKey implements Parcelable {
protected WrappedApplicationKey(Parcel in) {
mAlias = in.readString();
mEncryptedKeyMaterial = in.createByteArray();
+ // Check if there is still data to be read.
+ if (in.dataAvail() > 0) {
+ mMetadata = in.createByteArray();
+ }
}
@Override
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 591f15fd5676..9a77802aa541 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -62,7 +62,9 @@ interface ILockSettings {
in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile);
KeyChainSnapshot getKeyChainSnapshot();
String generateKey(String alias);
+ String generateKeyWithMetadata(String alias, in byte[] metadata);
String importKey(String alias, in byte[] keyBytes);
+ String importKeyWithMetadata(String alias, in byte[] keyBytes, in byte[] metadata);
String getKey(String alias);
void removeKey(String alias);
void setSnapshotCreatedPendingIntent(in PendingIntent intent);
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
index 61ab1526abc4..f78b0e1348b0 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/KeyChainSnapshotTest.java
@@ -44,6 +44,7 @@ public class KeyChainSnapshotTest {
private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
private static final String KEY_ALIAS = "steph";
private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
+ private static final byte[] KEY_METADATA = new byte[] { 5, 3, 11, 13 };
private static final CertPath CERT_PATH = TestData.getThmCertPath();
@Test
@@ -99,6 +100,7 @@ public class KeyChainSnapshotTest {
WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
+ assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata());
}
@Test
@@ -165,6 +167,7 @@ public class KeyChainSnapshotTest {
WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
+ assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata());
}
private static KeyChainSnapshot createKeyChainSnapshot() throws Exception {
@@ -196,6 +199,7 @@ public class KeyChainSnapshotTest {
return new WrappedApplicationKey.Builder()
.setAlias(KEY_ALIAS)
.setEncryptedKeyMaterial(KEY_MATERIAL)
+ .setMetadata(KEY_METADATA)
.build();
}
diff --git a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
index 15afbddf6f02..fabc43271bc2 100644
--- a/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
+++ b/core/tests/coretests/src/android/security/keystore/recovery/WrappedApplicationKeyTest.java
@@ -34,6 +34,7 @@ public class WrappedApplicationKeyTest {
private static final String ALIAS = "karlin";
private static final byte[] KEY_MATERIAL = new byte[] { 0, 1, 2, 3, 4 };
+ private static final byte[] METADATA = new byte[] {3, 2, 1, 0};
private Parcel mParcel;
@@ -58,8 +59,18 @@ public class WrappedApplicationKeyTest {
}
@Test
+ public void build_setsMetadata_nonNull() {
+ assertArrayEquals(METADATA, buildTestKeyWithMetadata(METADATA).getMetadata());
+ }
+
+ @Test
+ public void build_setsMetadata_null() {
+ assertArrayEquals(null, buildTestKeyWithMetadata(null).getMetadata());
+ }
+
+ @Test
public void writeToParcel_writesAliasToParcel() {
- buildTestKey().writeToParcel(mParcel, /*flags=*/ 0);
+ buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0);
mParcel.setDataPosition(0);
WrappedApplicationKey readFromParcel =
@@ -69,7 +80,7 @@ public class WrappedApplicationKeyTest {
@Test
public void writeToParcel_writesKeyMaterial() {
- buildTestKey().writeToParcel(mParcel, /*flags=*/ 0);
+ buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0);
mParcel.setDataPosition(0);
WrappedApplicationKey readFromParcel =
@@ -77,10 +88,48 @@ public class WrappedApplicationKeyTest {
assertArrayEquals(KEY_MATERIAL, readFromParcel.getEncryptedKeyMaterial());
}
+ @Test
+ public void writeToParcel_writesMetadata_nonNull() {
+ buildTestKeyWithMetadata(METADATA).writeToParcel(mParcel, /*flags=*/ 0);
+
+ mParcel.setDataPosition(0);
+ WrappedApplicationKey readFromParcel =
+ WrappedApplicationKey.CREATOR.createFromParcel(mParcel);
+ assertArrayEquals(METADATA, readFromParcel.getMetadata());
+ }
+
+ @Test
+ public void writeToParcel_writesMetadata_null() {
+ buildTestKeyWithMetadata(/*metadata=*/ null).writeToParcel(mParcel, /*flags=*/ 0);
+
+ mParcel.setDataPosition(0);
+ WrappedApplicationKey readFromParcel =
+ WrappedApplicationKey.CREATOR.createFromParcel(mParcel);
+ assertArrayEquals(null, readFromParcel.getMetadata());
+ }
+
+ @Test
+ public void writeToParcel_writesMetadata_absent() {
+ buildTestKey().writeToParcel(mParcel, /*flags=*/ 0);
+
+ mParcel.setDataPosition(0);
+ WrappedApplicationKey readFromParcel =
+ WrappedApplicationKey.CREATOR.createFromParcel(mParcel);
+ assertArrayEquals(null, readFromParcel.getMetadata());
+ }
+
private WrappedApplicationKey buildTestKey() {
return new WrappedApplicationKey.Builder()
.setAlias(ALIAS)
.setEncryptedKeyMaterial(KEY_MATERIAL)
.build();
}
+
+ private WrappedApplicationKey buildTestKeyWithMetadata(byte[] metadata) {
+ return new WrappedApplicationKey.Builder()
+ .setAlias(ALIAS)
+ .setEncryptedKeyMaterial(KEY_MATERIAL)
+ .setMetadata(metadata)
+ .build();
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ea8c7922d831..8734ceb614a9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -21,10 +21,10 @@ import static android.Manifest.permission.READ_CONTACTS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
@@ -81,9 +81,9 @@ import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.UserNotAuthenticatedException;
import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.keystore.recovery.KeyChainSnapshot;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -109,9 +109,9 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
-import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.wm.WindowManagerInternal;
import libcore.util.HexEncoding;
@@ -130,8 +130,8 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
-import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -2103,11 +2103,24 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public @Nullable String importKey(@NonNull String alias, byte[] keyBytes) throws RemoteException {
+ public @Nullable String generateKeyWithMetadata(
+ @NonNull String alias, @Nullable byte[] metadata) throws RemoteException {
+ return mRecoverableKeyStoreManager.generateKeyWithMetadata(alias, metadata);
+ }
+
+ @Override
+ public @Nullable String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
+ throws RemoteException {
return mRecoverableKeyStoreManager.importKey(alias, keyBytes);
}
@Override
+ public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes,
+ @Nullable byte[] metadata) throws RemoteException {
+ return mRecoverableKeyStoreManager.importKeyWithMetadata(alias, keyBytes, metadata);
+ }
+
+ @Override
public @Nullable String getKey(@NonNull String alias) throws RemoteException {
return mRecoverableKeyStoreManager.getKey(alias);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index fc5184d1438f..81d219c2d626 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -20,8 +20,8 @@ import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CE
import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE;
import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
-import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
import static android.security.keystore.recovery.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
import static android.security.keystore.recovery.RecoveryController.ERROR_SESSION_EXPIRED;
@@ -35,12 +35,12 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
+import android.security.KeyStore;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.WrappedApplicationKey;
-import android.security.KeyStore;
import android.util.ArrayMap;
import android.util.Log;
@@ -59,7 +59,6 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnaps
import java.io.IOException;
import java.security.InvalidKeyException;
-import java.security.KeyFactory;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
@@ -70,7 +69,6 @@ import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -674,14 +672,36 @@ public class RecoverableKeyStoreManager {
* Generates a key named {@code alias} in caller's namespace.
* The key is stored in system service keystore namespace.
*
+ * @param alias the alias provided by caller as a reference to the key.
* @return grant alias, which caller can use to access the key.
+ * @throws RemoteException if certain internal errors occur.
+ *
+ * @deprecated Use {@link #generateKeyWithMetadata(String, byte[])} instead.
*/
+ @Deprecated
public String generateKey(@NonNull String alias) throws RemoteException {
+ return generateKeyWithMetadata(alias, /*metadata=*/ null);
+ }
+
+ /**
+ * Generates a key named {@code alias} with the {@code metadata} in caller's namespace.
+ * The key is stored in system service keystore namespace.
+ *
+ * @param alias the alias provided by caller as a reference to the key.
+ * @param metadata the optional metadata blob that will authenticated (but unencrypted) together
+ * with the key material when the key is uploaded to cloud.
+ * @return grant alias, which caller can use to access the key.
+ * @throws RemoteException if certain internal errors occur.
+ */
+ public String generateKeyWithMetadata(@NonNull String alias, @Nullable byte[] metadata)
+ throws RemoteException {
checkRecoverKeyStorePermission();
Preconditions.checkNotNull(alias, "alias is null");
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
+ // TODO: Include metadata in the processes of authentication and storage
+
PlatformEncryptionKey encryptionKey;
try {
encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
@@ -713,10 +733,30 @@ public class RecoverableKeyStoreManager {
* @return grant alias, which caller can use to access the key.
* @throws RemoteException if the given key is invalid or some internal errors occur.
*
+ * @deprecated Use {{@link #importKeyWithMetadata(String, byte[], byte[])}} instead.
+ *
* @hide
*/
+ @Deprecated
public @Nullable String importKey(@NonNull String alias, @NonNull byte[] keyBytes)
throws RemoteException {
+ return importKeyWithMetadata(alias, keyBytes, /*metadata=*/ null);
+ }
+
+ /**
+ * Imports a 256-bit AES-GCM key named {@code alias} with the given {@code metadata}. The key is
+ * stored in system service keystore namespace.
+ *
+ * @param alias the alias provided by caller as a reference to the key.
+ * @param keyBytes the raw bytes of the 256-bit AES key.
+ * @param metadata the metadata to be authenticated (but unencrypted) together with the key.
+ * @return grant alias, which caller can use to access the key.
+ * @throws RemoteException if the given key is invalid or some internal errors occur.
+ *
+ * @hide
+ */
+ public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes,
+ @Nullable byte[] metadata) throws RemoteException {
checkRecoverKeyStorePermission();
Preconditions.checkNotNull(alias, "alias is null");
Preconditions.checkNotNull(keyBytes, "keyBytes is null");
@@ -728,6 +768,8 @@ public class RecoverableKeyStoreManager {
+ " bits.");
}
+ // TODO: Include metadata in the processes of authentication and storage
+
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
index b486834235dc..8a19d62de0b9 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -23,19 +23,18 @@ import static com.android.server.locksettings.recoverablekeystore.serialization.
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
-
-import static com.android.server.locksettings.recoverablekeystore.serialization
- .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_METADATA;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
@@ -49,6 +48,9 @@ import android.security.keystore.recovery.WrappedApplicationKey;
import android.util.Base64;
import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -59,9 +61,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
/**
* Deserializes a {@link android.security.keystore.recovery.KeyChainSnapshot} instance from XML.
*/
@@ -191,6 +190,10 @@ public class KeyChainSnapshotDeserializer {
builder.setEncryptedKeyMaterial(readBlobTag(parser, TAG_KEY_MATERIAL));
break;
+ case TAG_KEY_METADATA:
+ builder.setMetadata(readBlobTag(parser, TAG_KEY_METADATA));
+ break;
+
default:
throw new KeyChainSnapshotParserException(String.format(
Locale.US, "Unexpected tag %s in wrappedApplicationKey", name));
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
index 0f2c2fc6c959..8f85a27d4690 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -52,6 +52,7 @@ class KeyChainSnapshotSchema {
static final String TAG_APPLICATION_KEY = "applicationKey";
static final String TAG_ALIAS = "alias";
static final String TAG_KEY_MATERIAL = "keyMaterial";
+ static final String TAG_KEY_METADATA = "keyMetadata";
// Statics only
private KeyChainSnapshotSchema() {}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
index 235df698a674..527e879a198b 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -24,25 +24,24 @@ import static com.android.server.locksettings.recoverablekeystore.serialization.
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
-
-import static com.android.server.locksettings.recoverablekeystore.serialization
- .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_METADATA;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
+import android.annotation.Nullable;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.KeyDerivationParams;
@@ -103,6 +102,7 @@ public class KeyChainSnapshotSerializer {
XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
+ writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata());
}
private static void writeKeyChainProtectionParams(
@@ -181,8 +181,11 @@ public class KeyChainSnapshotSerializer {
}
private static void writePropertyTag(
- XmlSerializer xmlSerializer, String propertyName, byte[] propertyValue)
+ XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue)
throws IOException {
+ if (propertyValue == null) {
+ return;
+ }
xmlSerializer.startTag(NAMESPACE, propertyName);
xmlSerializer.text(Base64.encodeToString(propertyValue, /*flags=*/ Base64.DEFAULT));
xmlSerializer.endTag(NAMESPACE, propertyName);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
index 880255d79eb7..9c03df8e4369 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
@@ -55,12 +55,15 @@ public class KeyChainSnapshotSerializerTest {
private static final String TEST_KEY_1_ALIAS = "key1";
private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 };
+ private static final byte[] TEST_KEY_1_METADATA = new byte[] { 89, 87 };
private static final String TEST_KEY_2_ALIAS = "key2";
private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 };
+ private static final byte[] TEST_KEY_2_METADATA = new byte[] {};
private static final String TEST_KEY_3_ALIAS = "key3";
private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 };
+ private static final byte[] TEST_KEY_3_METADATA = new byte[] { 121 };
@Test
public void roundTrip_persistsCounterId() throws Exception {
@@ -144,6 +147,17 @@ public class KeyChainSnapshotSerializerTest {
}
@Test
+ public void roundTripKeys_0_persistsKeyMetadata_absent() throws Exception {
+ assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(0).getMetadata()).isEqualTo(null);
+ }
+
+ @Test
+ public void roundTripKeys_0_persistsKeyMetadata_present() throws Exception {
+ assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(0).getMetadata())
+ .isEqualTo(TEST_KEY_1_METADATA);
+ }
+
+ @Test
public void roundTripKeys_1_persistsAlias() throws Exception {
assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS);
}
@@ -154,6 +168,17 @@ public class KeyChainSnapshotSerializerTest {
}
@Test
+ public void roundTripKeys_1_persistsKeyMetadata_absent() throws Exception {
+ assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(1).getMetadata()).isEqualTo(null);
+ }
+
+ @Test
+ public void roundTripKeys_1_persistsKeyMetadata_present() throws Exception {
+ assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(1).getMetadata())
+ .isEqualTo(TEST_KEY_2_METADATA);
+ }
+
+ @Test
public void roundTripKeys_2_persistsAlias() throws Exception {
assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS);
}
@@ -164,28 +189,74 @@ public class KeyChainSnapshotSerializerTest {
}
@Test
- public void serialize_doesNotThrowForTestSnapshot() throws Exception {
+ public void roundTripKeys_2_persistsKeyMetadata_absent() throws Exception {
+ assertThat(roundTripKeys(/*withKeyMetadata=*/ false).get(2).getMetadata()).isEqualTo(null);
+ }
+
+ @Test
+ public void roundTripKeys_2_persistsKeyMetadata_present() throws Exception {
+ assertThat(roundTripKeys(/*withKeyMetadata=*/ true).get(2).getMetadata())
+ .isEqualTo(TEST_KEY_3_METADATA);
+ }
+
+ @Test
+ public void serialize_doesNotThrowForTestSnapshotWithoutKeyMetadata() throws Exception {
KeyChainSnapshotSerializer.serialize(
- createTestKeyChainSnapshot(), new ByteArrayOutputStream());
+ createTestKeyChainSnapshot(/*withKeyMetadata=*/ false),
+ new ByteArrayOutputStream());
+ }
+
+ @Test
+ public void serialize_doesNotThrowForTestSnapshotWithKeyMetadata() throws Exception {
+ KeyChainSnapshotSerializer.serialize(
+ createTestKeyChainSnapshotWithKeyMetadata(), new ByteArrayOutputStream());
}
private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
return roundTrip().getWrappedApplicationKeys();
}
+ private static List<WrappedApplicationKey> roundTripKeys(boolean withKeyMetadata)
+ throws Exception {
+ return roundTrip(withKeyMetadata).getWrappedApplicationKeys();
+ }
+
private static KeyChainProtectionParams roundTripParams() throws Exception {
- return roundTrip().getKeyChainProtectionParams().get(0);
+ return roundTrip(/*withKeyMetadata=*/ false).getKeyChainProtectionParams().get(0);
}
public static KeyChainSnapshot roundTrip() throws Exception {
- KeyChainSnapshot snapshot = createTestKeyChainSnapshot();
+ return roundTrip(/*withKeyMetadata=*/ false);
+ }
+
+ public static KeyChainSnapshot roundTrip(boolean withKeyMetadata) throws Exception {
+ KeyChainSnapshot snapshot = createTestKeyChainSnapshot(withKeyMetadata);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream);
return KeyChainSnapshotDeserializer.deserialize(
new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
}
- private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
+ private static KeyChainSnapshot createTestKeyChainSnapshot(boolean withKeyMetadata)
+ throws Exception {
+ KeyChainSnapshot.Builder builder = new KeyChainSnapshot.Builder()
+ .setCounterId(COUNTER_ID)
+ .setSnapshotVersion(SNAPSHOT_VERSION)
+ .setServerParams(SERVER_PARAMS)
+ .setMaxAttempts(MAX_ATTEMPTS)
+ .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+ .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+ .setTrustedHardwareCertPath(CERT_PATH);
+ if (withKeyMetadata) {
+ builder.setWrappedApplicationKeys(createKeysWithMetadata());
+ } else {
+ builder.setWrappedApplicationKeys(createKeysWithoutMetadata());
+ }
+ return builder.build();
+ }
+
+ private static KeyChainSnapshot createTestKeyChainSnapshotWithKeyMetadata()
+ throws Exception {
return new KeyChainSnapshot.Builder()
.setCounterId(COUNTER_ID)
.setSnapshotVersion(SNAPSHOT_VERSION)
@@ -193,16 +264,24 @@ public class KeyChainSnapshotSerializerTest {
.setMaxAttempts(MAX_ATTEMPTS)
.setEncryptedRecoveryKeyBlob(KEY_BLOB)
.setKeyChainProtectionParams(createKeyChainProtectionParamsList())
- .setWrappedApplicationKeys(createKeys())
+ .setWrappedApplicationKeys(createKeysWithMetadata())
.setTrustedHardwareCertPath(CERT_PATH)
.build();
}
- private static List<WrappedApplicationKey> createKeys() {
+ private static List<WrappedApplicationKey> createKeysWithoutMetadata() {
ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
- keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
- keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
- keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+ keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES, /*metadata=*/ null));
+ keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES, /*metadata=*/ null));
+ keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES, /*metadata=*/ null));
+ return keyList;
+ }
+
+ private static List<WrappedApplicationKey> createKeysWithMetadata() {
+ ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+ keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES, TEST_KEY_1_METADATA));
+ keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES, TEST_KEY_2_METADATA));
+ keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES, TEST_KEY_3_METADATA));
return keyList;
}
@@ -221,10 +300,13 @@ public class KeyChainSnapshotSerializerTest {
return keyChainProtectionParamsList;
}
- private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
- return new WrappedApplicationKey.Builder()
+ private static WrappedApplicationKey createKey(String alias, byte[] bytes, byte[] metadata) {
+ WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder()
.setAlias(alias)
- .setEncryptedKeyMaterial(bytes)
- .build();
+ .setEncryptedKeyMaterial(bytes);
+ if (metadata != null) {
+ builder.setMetadata(metadata);
+ }
+ return builder.build();
}
}