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
This commit is contained in:
@@ -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[]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -555,6 +558,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 {
|
||||
@@ -586,6 +632,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.
|
||||
*
|
||||
|
||||
@@ -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,12 +83,22 @@ 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.
|
||||
*
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -57,9 +58,19 @@ public class WrappedApplicationKeyTest {
|
||||
assertArrayEquals(KEY_MATERIAL, buildTestKey().getEncryptedKeyMaterial());
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,10 +2103,23 @@ 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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
@@ -143,6 +146,17 @@ public class KeyChainSnapshotSerializerTest {
|
||||
assertThat(roundTripKeys().get(0).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_1_BYTES);
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -153,6 +167,17 @@ public class KeyChainSnapshotSerializerTest {
|
||||
assertThat(roundTripKeys().get(1).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_2_BYTES);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user