Merge "Prepare KeyChainSnapshot to removing deprecated getTrustedHardwarePublicKey method." into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
95f2965a74
@@ -127,18 +127,13 @@ public final class KeyChainSnapshot implements Parcelable {
|
||||
/**
|
||||
* CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
|
||||
*/
|
||||
// TODO: Change to @NonNull
|
||||
public CertPath getTrustedHardwareCertPath() {
|
||||
if (mCertPath == null) {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return mCertPath.getCertPath();
|
||||
} catch (CertificateException e) {
|
||||
// Rethrow an unchecked exception as it should not happen. If such an issue exists,
|
||||
// an exception should have been thrown during service initialization.
|
||||
throw new BadParcelableException(e);
|
||||
}
|
||||
public @NonNull CertPath getTrustedHardwareCertPath() {
|
||||
try {
|
||||
return mCertPath.getCertPath();
|
||||
} catch (CertificateException e) {
|
||||
// Rethrow an unchecked exception as it should not happen. If such an issue exists,
|
||||
// an exception should have been thrown during service initialization.
|
||||
throw new BadParcelableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,13 +243,9 @@ public final class KeyChainSnapshot implements Parcelable {
|
||||
* @throws CertificateException if the given certificate path cannot be encoded properly
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setTrustedHardwareCertPath(CertPath certPath) throws CertificateException {
|
||||
// TODO: Make it NonNull when the caller code is all updated
|
||||
if (certPath == null) {
|
||||
mInstance.mCertPath = null;
|
||||
} else {
|
||||
mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
|
||||
}
|
||||
public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
|
||||
throws CertificateException {
|
||||
mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -282,7 +273,7 @@ public final class KeyChainSnapshot implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets recovery key blob
|
||||
* Sets recovery key blob.
|
||||
*
|
||||
* @param encryptedRecoveryKeyBlob The recovery key blob.
|
||||
* @return This builder.
|
||||
@@ -297,7 +288,7 @@ public final class KeyChainSnapshot implements Parcelable {
|
||||
* Creates a new {@link KeyChainSnapshot} instance.
|
||||
*
|
||||
* @return new instance
|
||||
* @throws NullPointerException if some required fields were not set.
|
||||
* @throws NullPointerException if some of the required fields were not set.
|
||||
*/
|
||||
@NonNull public KeyChainSnapshot build() {
|
||||
Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
|
||||
@@ -306,6 +297,7 @@ public final class KeyChainSnapshot implements Parcelable {
|
||||
"entryRecoveryData");
|
||||
Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
|
||||
Preconditions.checkNotNull(mInstance.mServerParams);
|
||||
Preconditions.checkNotNull(mInstance.mCertPath);
|
||||
return mInstance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.locksettings.recoverablekeystore.storage;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.KeyDerivationParams;
|
||||
import android.security.keystore.recovery.WrappedApplicationKey;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class provides helper methods serialize and deserialize {@link KeyChainSnapshot}.
|
||||
*
|
||||
* <p> It is necessary since {@link android.os.Parcelable} is not designed for persistent storage.
|
||||
*
|
||||
* <p> For every list, length is stored before the elements.
|
||||
*
|
||||
*/
|
||||
public class PersistentKeyChainSnapshot {
|
||||
private static final int VERSION = 1;
|
||||
private static final int NULL_LIST_LENGTH = -1;
|
||||
|
||||
private DataInputStream mInput;
|
||||
private DataOutputStream mOut;
|
||||
private ByteArrayOutputStream mOutStream;
|
||||
|
||||
@VisibleForTesting
|
||||
PersistentKeyChainSnapshot() {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initReader(byte[] input) {
|
||||
mInput = new DataInputStream(new ByteArrayInputStream(input));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initWriter() {
|
||||
mOutStream = new ByteArrayOutputStream();
|
||||
mOut = new DataOutputStream(mOutStream);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
byte[] getOutput() {
|
||||
return mOutStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link KeyChainSnapshot} to its binary representation.
|
||||
*
|
||||
* @param snapshot The snapshot.
|
||||
*
|
||||
* @throws IOException if serialization failed.
|
||||
*/
|
||||
public static byte[] serialize(@NonNull KeyChainSnapshot snapshot) throws IOException {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
writer.writeInt(VERSION);
|
||||
writer.writeKeyChainSnapshot(snapshot);
|
||||
return writer.getOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* deserializes {@link KeyChainSnapshot}.
|
||||
*
|
||||
* @input input - byte array produced by {@link serialize} method.
|
||||
* @throws IOException if parsing failed.
|
||||
*/
|
||||
public static @NonNull KeyChainSnapshot deserialize(@NonNull byte[] input)
|
||||
throws IOException {
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(input);
|
||||
try {
|
||||
int version = reader.readInt();
|
||||
if (version != VERSION) {
|
||||
throw new IOException("Unsupported version " + version);
|
||||
}
|
||||
return reader.readKeyChainSnapshot();
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Malformed KeyChainSnapshot", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be in sync with {@link KeyChainSnapshot.writeToParcel}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void writeKeyChainSnapshot(KeyChainSnapshot snapshot) throws IOException {
|
||||
writeInt(snapshot.getSnapshotVersion());
|
||||
writeProtectionParamsList(snapshot.getKeyChainProtectionParams());
|
||||
writeBytes(snapshot.getEncryptedRecoveryKeyBlob());
|
||||
writeKeysList(snapshot.getWrappedApplicationKeys());
|
||||
|
||||
writeInt(snapshot.getMaxAttempts());
|
||||
writeLong(snapshot.getCounterId());
|
||||
writeBytes(snapshot.getServerParams());
|
||||
writeBytes(snapshot.getTrustedHardwarePublicKey());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
KeyChainSnapshot readKeyChainSnapshot() throws IOException {
|
||||
int snapshotVersion = readInt();
|
||||
List<KeyChainProtectionParams> protectionParams = readProtectionParamsList();
|
||||
byte[] encryptedRecoveryKey = readBytes();
|
||||
List<WrappedApplicationKey> keysList = readKeysList();
|
||||
|
||||
int maxAttempts = readInt();
|
||||
long conterId = readLong();
|
||||
byte[] serverParams = readBytes();
|
||||
byte[] trustedHardwarePublicKey = readBytes();
|
||||
|
||||
return new KeyChainSnapshot.Builder()
|
||||
.setSnapshotVersion(snapshotVersion)
|
||||
.setKeyChainProtectionParams(protectionParams)
|
||||
.setEncryptedRecoveryKeyBlob(encryptedRecoveryKey)
|
||||
.setWrappedApplicationKeys(keysList)
|
||||
.setMaxAttempts(maxAttempts)
|
||||
.setCounterId(conterId)
|
||||
.setServerParams(serverParams)
|
||||
.setTrustedHardwarePublicKey(trustedHardwarePublicKey)
|
||||
.build();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void writeProtectionParamsList(
|
||||
@NonNull List<KeyChainProtectionParams> ProtectionParamsList) throws IOException {
|
||||
writeInt(ProtectionParamsList.size());
|
||||
for (KeyChainProtectionParams protectionParams : ProtectionParamsList) {
|
||||
writeProtectionParams(protectionParams);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<KeyChainProtectionParams> readProtectionParamsList() throws IOException {
|
||||
int length = readInt();
|
||||
List<KeyChainProtectionParams> result = new ArrayList<>(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
result.add(readProtectionParams());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be in sync with {@link KeyChainProtectionParams.writeToParcel}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void writeProtectionParams(@NonNull KeyChainProtectionParams protectionParams)
|
||||
throws IOException {
|
||||
if (!ArrayUtils.isEmpty(protectionParams.getSecret())) {
|
||||
// Extra security check.
|
||||
throw new RuntimeException("User generated secret should not be stored");
|
||||
}
|
||||
writeInt(protectionParams.getUserSecretType());
|
||||
writeInt(protectionParams.getLockScreenUiFormat());
|
||||
writeKeyDerivationParams(protectionParams.getKeyDerivationParams());
|
||||
writeBytes(protectionParams.getSecret());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
KeyChainProtectionParams readProtectionParams() throws IOException {
|
||||
int userSecretType = readInt();
|
||||
int lockScreenUiFormat = readInt();
|
||||
KeyDerivationParams derivationParams = readKeyDerivationParams();
|
||||
byte[] secret = readBytes();
|
||||
return new KeyChainProtectionParams.Builder()
|
||||
.setUserSecretType(userSecretType)
|
||||
.setLockScreenUiFormat(lockScreenUiFormat)
|
||||
.setKeyDerivationParams(derivationParams)
|
||||
.setSecret(secret)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be in sync with {@link KeyDerivationParams.writeToParcel}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void writeKeyDerivationParams(@NonNull KeyDerivationParams Params) throws IOException {
|
||||
writeInt(Params.getAlgorithm());
|
||||
writeBytes(Params.getSalt());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
KeyDerivationParams readKeyDerivationParams() throws IOException {
|
||||
int algorithm = readInt();
|
||||
byte[] salt = readBytes();
|
||||
return KeyDerivationParams.createSha256Params(salt);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void writeKeysList(@NonNull List<WrappedApplicationKey> applicationKeys) throws IOException {
|
||||
writeInt(applicationKeys.size());
|
||||
for (WrappedApplicationKey keyEntry : applicationKeys) {
|
||||
writeKeyEntry(keyEntry);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<WrappedApplicationKey> readKeysList() throws IOException {
|
||||
int length = readInt();
|
||||
List<WrappedApplicationKey> result = new ArrayList<>(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
result.add(readKeyEntry());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be in sync with {@link WrappedApplicationKey.writeToParcel}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void writeKeyEntry(@NonNull WrappedApplicationKey keyEntry) throws IOException {
|
||||
mOut.writeUTF(keyEntry.getAlias());
|
||||
writeBytes(keyEntry.getEncryptedKeyMaterial());
|
||||
writeBytes(keyEntry.getAccount());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
WrappedApplicationKey readKeyEntry() throws IOException {
|
||||
String alias = mInput.readUTF();
|
||||
byte[] keyMaterial = readBytes();
|
||||
byte[] account = readBytes();
|
||||
return new WrappedApplicationKey.Builder()
|
||||
.setAlias(alias)
|
||||
.setEncryptedKeyMaterial(keyMaterial)
|
||||
.setAccount(account)
|
||||
.build();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void writeInt(int value) throws IOException {
|
||||
mOut.writeInt(value);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int readInt() throws IOException {
|
||||
return mInput.readInt();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void writeLong(long value) throws IOException {
|
||||
mOut.writeLong(value);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
long readLong() throws IOException {
|
||||
return mInput.readLong();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void writeBytes(@Nullable byte[] value) throws IOException {
|
||||
if (value == null) {
|
||||
writeInt(NULL_LIST_LENGTH);
|
||||
return;
|
||||
}
|
||||
writeInt(value.length);
|
||||
mOut.write(value, 0, value.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads @code{byte[]} from current position. Converts {@code null} to an empty array.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@NonNull byte[] readBytes() throws IOException {
|
||||
int length = readInt();
|
||||
if (length == NULL_LIST_LENGTH) {
|
||||
return new byte[]{};
|
||||
}
|
||||
byte[] result = new byte[length];
|
||||
mInput.read(result, 0, result.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@@ -95,7 +94,6 @@ public class KeySyncTaskTest {
|
||||
private RecoverySnapshotStorage mRecoverySnapshotStorage;
|
||||
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
|
||||
private File mDatabaseFile;
|
||||
private KeyPair mKeyPair;
|
||||
private AndroidKeyStoreSecretKey mWrappingKey;
|
||||
private PlatformEncryptionKey mEncryptKey;
|
||||
|
||||
@@ -108,7 +106,6 @@ public class KeySyncTaskTest {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
|
||||
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
|
||||
mKeyPair = SecureBox.genKeyPair();
|
||||
|
||||
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
|
||||
new int[] {TYPE_LOCKSCREEN});
|
||||
@@ -249,8 +246,8 @@ public class KeySyncTaskTest {
|
||||
TEST_RECOVERY_AGENT_UID,
|
||||
TEST_APP_KEY_ALIAS,
|
||||
WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
|
||||
mKeySyncTask.run();
|
||||
@@ -265,8 +262,8 @@ public class KeySyncTaskTest {
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
|
||||
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
|
||||
mKeySyncTask.run();
|
||||
|
||||
@@ -275,8 +272,8 @@ public class KeySyncTaskTest {
|
||||
|
||||
@Test
|
||||
public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
|
||||
mRecoverableKeyStoreDb.setServerParams(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
|
||||
@@ -301,7 +298,7 @@ public class KeySyncTaskTest {
|
||||
lockScreenHash,
|
||||
keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
|
||||
/*vaultParams=*/ KeySyncUtils.packVaultParams(
|
||||
mKeyPair.getPublic(),
|
||||
TestData.CERT_1_PUBLIC_KEY,
|
||||
counterId,
|
||||
/*maxAttempts=*/ 10,
|
||||
TEST_VAULT_HANDLE));
|
||||
@@ -309,8 +306,8 @@ public class KeySyncTaskTest {
|
||||
assertThat(applicationKeys).hasSize(1);
|
||||
assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
|
||||
assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
|
||||
assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
|
||||
.isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic()));
|
||||
assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
|
||||
.isEqualTo(TestData.CERT_PATH_1);
|
||||
assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
|
||||
WrappedApplicationKey keyData = applicationKeys.get(0);
|
||||
assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
|
||||
@@ -335,15 +332,14 @@ public class KeySyncTaskTest {
|
||||
verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
|
||||
List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
|
||||
assertThat(applicationKeys).hasSize(1);
|
||||
assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
|
||||
.isEqualTo(SecureBox.encodePublicKey(
|
||||
TestData.CERT_PATH_1.getCertificates().get(0).getPublicKey()));
|
||||
assertThat(keyChainSnapshot.getTrustedHardwareCertPath())
|
||||
.isEqualTo(TestData.CERT_PATH_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void run_setsCorrectSnapshotVersion() throws Exception {
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
|
||||
@@ -361,8 +357,8 @@ public class KeySyncTaskTest {
|
||||
|
||||
@Test
|
||||
public void run_recreatesMissingSnapshot() throws Exception {
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
|
||||
@@ -391,8 +387,8 @@ public class KeySyncTaskTest {
|
||||
/*credentialUpdated=*/ false,
|
||||
mPlatformKeyManager);
|
||||
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
SecretKey applicationKey =
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
@@ -417,8 +413,8 @@ public class KeySyncTaskTest {
|
||||
/*credentialUpdated=*/ false,
|
||||
mPlatformKeyManager);
|
||||
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
SecretKey applicationKey =
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
@@ -444,8 +440,8 @@ public class KeySyncTaskTest {
|
||||
/*credentialUpdated=*/ false,
|
||||
mPlatformKeyManager);
|
||||
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
SecretKey applicationKey =
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
@@ -460,10 +456,10 @@ public class KeySyncTaskTest {
|
||||
|
||||
@Test
|
||||
public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
@@ -482,10 +478,10 @@ public class KeySyncTaskTest {
|
||||
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
|
||||
new int[] {1000});
|
||||
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
@@ -499,10 +495,10 @@ public class KeySyncTaskTest {
|
||||
|
||||
@Test
|
||||
public void run_notifiesNonregisteredAgent() throws Exception {
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, mKeyPair.getPublic());
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
|
||||
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
|
||||
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
|
||||
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
|
||||
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
|
||||
@@ -562,7 +558,7 @@ public class KeySyncTaskTest {
|
||||
private byte[] decryptThmEncryptedKey(
|
||||
byte[] lockScreenHash, byte[] encryptedKey, byte[] vaultParams) throws Exception {
|
||||
byte[] locallyEncryptedKey = SecureBox.decrypt(
|
||||
mKeyPair.getPrivate(),
|
||||
TestData.CERT_1_PRIVATE_KEY,
|
||||
/*sharedSecret=*/ KeySyncUtils.calculateThmKfHash(lockScreenHash),
|
||||
/*header=*/ KeySyncUtils.concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams),
|
||||
encryptedKey
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
package com.android.server.locksettings.recoverablekeystore;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.spec.ECPrivateKeySpec;
|
||||
|
||||
public final class TestData {
|
||||
|
||||
@@ -213,6 +220,44 @@ public final class TestData {
|
||||
+ " </value>\n"
|
||||
+ "</signature>\n";
|
||||
|
||||
public static final PublicKey CERT_1_PUBLIC_KEY;
|
||||
public static final PrivateKey CERT_1_PRIVATE_KEY;
|
||||
|
||||
static {
|
||||
try {
|
||||
CERT_1_PUBLIC_KEY =
|
||||
SecureBox.decodePublicKey(
|
||||
new byte[] {
|
||||
(byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18,
|
||||
(byte) 0x98, (byte) 0x1d, (byte) 0xf0, (byte) 0x6e, (byte) 0xb4,
|
||||
(byte) 0x94, (byte) 0xfe, (byte) 0x86, (byte) 0xda, (byte) 0x1c,
|
||||
(byte) 0x07, (byte) 0x8d, (byte) 0x01, (byte) 0xb4, (byte) 0x3a,
|
||||
(byte) 0xf6, (byte) 0x8d, (byte) 0xdc, (byte) 0x61, (byte) 0xd0,
|
||||
(byte) 0x46, (byte) 0x49, (byte) 0x95, (byte) 0x0f, (byte) 0x10,
|
||||
(byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0,
|
||||
(byte) 0x3f, (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79,
|
||||
(byte) 0x20, (byte) 0x1d, (byte) 0x91, (byte) 0x55, (byte) 0xb0,
|
||||
(byte) 0xe5, (byte) 0xbd, (byte) 0x7a, (byte) 0x8b, (byte) 0x32,
|
||||
(byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2, (byte) 0xfc,
|
||||
(byte) 0xa5, (byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21,
|
||||
(byte) 0x44, (byte) 0x7e, (byte) 0x78, (byte) 0x52, (byte) 0xfa
|
||||
});
|
||||
CERT_1_PRIVATE_KEY =
|
||||
decodePrivateKey(
|
||||
new byte[] {
|
||||
(byte) 0x70, (byte) 0x01, (byte) 0xc7, (byte) 0x87, (byte) 0x32,
|
||||
(byte) 0x2f, (byte) 0x1c, (byte) 0x9a, (byte) 0x6e, (byte) 0xb1,
|
||||
(byte) 0x91, (byte) 0xca, (byte) 0x4e, (byte) 0xb5, (byte) 0x44,
|
||||
(byte) 0xba, (byte) 0xc8, (byte) 0x68, (byte) 0xc6, (byte) 0x0a,
|
||||
(byte) 0x76, (byte) 0xcb, (byte) 0xd3, (byte) 0x63, (byte) 0x67,
|
||||
(byte) 0x7c, (byte) 0xb0, (byte) 0x11, (byte) 0x82, (byte) 0x65,
|
||||
(byte) 0x77, (byte) 0x01
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getCertPath1Bytes() {
|
||||
try {
|
||||
return CertUtils.decodeBase64(CERT_PATH_1_BASE64);
|
||||
@@ -256,4 +301,11 @@ public final class TestData {
|
||||
public static byte[] getSigXml() {
|
||||
return THM_SIG_XML.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static PrivateKey decodePrivateKey(byte[] keyBytes) throws Exception {
|
||||
assertThat(keyBytes.length).isEqualTo(32);
|
||||
BigInteger priv = new BigInteger(/*signum=*/ 1, keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.locksettings.recoverablekeystore.storage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.security.keystore.recovery.KeyDerivationParams;
|
||||
import android.security.keystore.recovery.WrappedApplicationKey;
|
||||
import android.security.keystore.recovery.KeyChainSnapshot;
|
||||
import android.security.keystore.recovery.KeyChainProtectionParams;
|
||||
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PersistentKeyChainSnapshotTest {
|
||||
|
||||
private static final String ALIAS = "some_key";
|
||||
private static final String ALIAS2 = "another_key";
|
||||
private static final byte[] RECOVERY_KEY_MATERIAL = "recovery_key_data"
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
private static final byte[] KEY_MATERIAL = "app_key_data".getBytes(StandardCharsets.UTF_8);
|
||||
private static final byte[] PUBLIC_KEY = "public_key_data".getBytes(StandardCharsets.UTF_8);
|
||||
private static final byte[] SALT = "salt".getBytes(StandardCharsets.UTF_8);
|
||||
private static final int SNAPSHOT_VERSION = 2;
|
||||
private static final int MAX_ATTEMPTS = 10;
|
||||
private static final long COUNTER_ID = 123456789L;
|
||||
private static final byte[] SERVER_PARAMS = "server_params".getBytes(StandardCharsets.UTF_8);
|
||||
private static final byte[] ZERO_BYTES = new byte[0];
|
||||
private static final byte[] ONE_BYTE = new byte[]{(byte) 11};
|
||||
private static final byte[] TWO_BYTES = new byte[]{(byte) 222,(byte) 222};
|
||||
|
||||
@Test
|
||||
public void testWriteInt() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
writer.writeInt(Integer.MIN_VALUE);
|
||||
writer.writeInt(Integer.MAX_VALUE);
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
assertThat(reader.readInt()).isEqualTo(Integer.MIN_VALUE);
|
||||
assertThat(reader.readInt()).isEqualTo(Integer.MAX_VALUE);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteLong() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
writer.writeLong(Long.MIN_VALUE);
|
||||
writer.writeLong(Long.MAX_VALUE);
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
assertThat(reader.readLong()).isEqualTo(Long.MIN_VALUE);
|
||||
assertThat(reader.readLong()).isEqualTo(Long.MAX_VALUE);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteBytes() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
writer.writeBytes(ZERO_BYTES);
|
||||
writer.writeBytes(ONE_BYTE);
|
||||
writer.writeBytes(TWO_BYTES);
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
assertThat(reader.readBytes()).isEqualTo(ZERO_BYTES);
|
||||
assertThat(reader.readBytes()).isEqualTo(ONE_BYTE);
|
||||
assertThat(reader.readBytes()).isEqualTo(TWO_BYTES);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBytes_returnsNullArrayAsEmpty() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
writer.writeBytes(null);
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
assertThat(reader.readBytes()).isEqualTo(new byte[]{}); // null -> empty array
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteKeyEntry() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
WrappedApplicationKey entry = new WrappedApplicationKey.Builder()
|
||||
.setAlias(ALIAS)
|
||||
.setEncryptedKeyMaterial(KEY_MATERIAL)
|
||||
.build();
|
||||
writer.writeKeyEntry(entry);
|
||||
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
|
||||
WrappedApplicationKey copy = reader.readKeyEntry();
|
||||
assertThat(copy.getAlias()).isEqualTo(ALIAS);
|
||||
assertThat(copy.getEncryptedKeyMaterial()).isEqualTo(KEY_MATERIAL);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readKeyEntry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteProtectionParams() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
|
||||
KeyChainProtectionParams protectionParams = new KeyChainProtectionParams.Builder()
|
||||
.setUserSecretType(1)
|
||||
.setLockScreenUiFormat(2)
|
||||
.setKeyDerivationParams(derivationParams)
|
||||
.build();
|
||||
writer.writeProtectionParams(protectionParams);
|
||||
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
|
||||
KeyChainProtectionParams copy = reader.readProtectionParams();
|
||||
assertThat(copy.getUserSecretType()).isEqualTo(1);
|
||||
assertThat(copy.getLockScreenUiFormat()).isEqualTo(2);
|
||||
assertThat(copy.getKeyDerivationParams().getSalt()).isEqualTo(SALT);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readProtectionParams());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyChainSnapshot() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
|
||||
KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
|
||||
|
||||
ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
|
||||
protectionParamsList.add(new KeyChainProtectionParams.Builder()
|
||||
.setUserSecretType(1)
|
||||
.setLockScreenUiFormat(2)
|
||||
.setKeyDerivationParams(derivationParams)
|
||||
.build());
|
||||
|
||||
ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
|
||||
appKeysList.add(new WrappedApplicationKey.Builder()
|
||||
.setAlias(ALIAS)
|
||||
.setEncryptedKeyMaterial(KEY_MATERIAL)
|
||||
.build());
|
||||
|
||||
KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
|
||||
.setSnapshotVersion(SNAPSHOT_VERSION)
|
||||
.setKeyChainProtectionParams(protectionParamsList)
|
||||
.setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
|
||||
.setWrappedApplicationKeys(appKeysList)
|
||||
.setMaxAttempts(MAX_ATTEMPTS)
|
||||
.setCounterId(COUNTER_ID)
|
||||
.setServerParams(SERVER_PARAMS)
|
||||
.setTrustedHardwarePublicKey(PUBLIC_KEY)
|
||||
.build();
|
||||
|
||||
writer.writeKeyChainSnapshot(snapshot);
|
||||
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
|
||||
KeyChainSnapshot copy = reader.readKeyChainSnapshot();
|
||||
assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
|
||||
assertThat(copy.getKeyChainProtectionParams()).hasSize(1);
|
||||
assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
|
||||
assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
|
||||
assertThat(copy.getWrappedApplicationKeys()).hasSize(1);
|
||||
assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
|
||||
assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
|
||||
assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
|
||||
assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
|
||||
assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readKeyChainSnapshot());
|
||||
|
||||
verifyDeserialize(snapshot);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyChainSnapshot_withManyKeysAndProtectionParams() throws Exception {
|
||||
PersistentKeyChainSnapshot writer = new PersistentKeyChainSnapshot();
|
||||
writer.initWriter();
|
||||
|
||||
KeyDerivationParams derivationParams = KeyDerivationParams.createSha256Params(SALT);
|
||||
|
||||
ArrayList<KeyChainProtectionParams> protectionParamsList = new ArrayList<>();
|
||||
protectionParamsList.add(new KeyChainProtectionParams.Builder()
|
||||
.setUserSecretType(1)
|
||||
.setLockScreenUiFormat(2)
|
||||
.setKeyDerivationParams(derivationParams)
|
||||
.build());
|
||||
protectionParamsList.add(new KeyChainProtectionParams.Builder()
|
||||
.setUserSecretType(2)
|
||||
.setLockScreenUiFormat(3)
|
||||
.setKeyDerivationParams(derivationParams)
|
||||
.build());
|
||||
ArrayList<WrappedApplicationKey> appKeysList = new ArrayList<>();
|
||||
appKeysList.add(new WrappedApplicationKey.Builder()
|
||||
.setAlias(ALIAS)
|
||||
.setEncryptedKeyMaterial(KEY_MATERIAL)
|
||||
.build());
|
||||
appKeysList.add(new WrappedApplicationKey.Builder()
|
||||
.setAlias(ALIAS2)
|
||||
.setEncryptedKeyMaterial(KEY_MATERIAL)
|
||||
.build());
|
||||
|
||||
|
||||
KeyChainSnapshot snapshot = new KeyChainSnapshot.Builder()
|
||||
.setSnapshotVersion(SNAPSHOT_VERSION)
|
||||
.setKeyChainProtectionParams(protectionParamsList)
|
||||
.setEncryptedRecoveryKeyBlob(RECOVERY_KEY_MATERIAL)
|
||||
.setWrappedApplicationKeys(appKeysList)
|
||||
.setMaxAttempts(MAX_ATTEMPTS)
|
||||
.setCounterId(COUNTER_ID)
|
||||
.setServerParams(SERVER_PARAMS)
|
||||
.setTrustedHardwarePublicKey(PUBLIC_KEY)
|
||||
.build();
|
||||
|
||||
writer.writeKeyChainSnapshot(snapshot);
|
||||
|
||||
byte[] result = writer.getOutput();
|
||||
|
||||
PersistentKeyChainSnapshot reader = new PersistentKeyChainSnapshot();
|
||||
reader.initReader(result);
|
||||
|
||||
KeyChainSnapshot copy = reader.readKeyChainSnapshot();
|
||||
assertThat(copy.getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
|
||||
assertThat(copy.getKeyChainProtectionParams().get(0).getUserSecretType()).isEqualTo(1);
|
||||
assertThat(copy.getEncryptedRecoveryKeyBlob()).isEqualTo(RECOVERY_KEY_MATERIAL);
|
||||
assertThat(copy.getWrappedApplicationKeys().get(0).getAlias()).isEqualTo(ALIAS);
|
||||
assertThat(copy.getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
|
||||
assertThat(copy.getCounterId()).isEqualTo(COUNTER_ID);
|
||||
assertThat(copy.getServerParams()).isEqualTo(SERVER_PARAMS);
|
||||
assertThat(copy.getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY);
|
||||
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> reader.readKeyChainSnapshot());
|
||||
|
||||
verifyDeserialize(snapshot);
|
||||
}
|
||||
|
||||
private void verifyDeserialize(KeyChainSnapshot snapshot) throws Exception {
|
||||
byte[] serialized = PersistentKeyChainSnapshot.serialize(snapshot);
|
||||
KeyChainSnapshot copy = PersistentKeyChainSnapshot.deserialize(serialized);
|
||||
assertThat(copy.getSnapshotVersion())
|
||||
.isEqualTo(snapshot.getSnapshotVersion());
|
||||
assertThat(copy.getKeyChainProtectionParams().size())
|
||||
.isEqualTo(copy.getKeyChainProtectionParams().size());
|
||||
assertThat(copy.getEncryptedRecoveryKeyBlob())
|
||||
.isEqualTo(snapshot.getEncryptedRecoveryKeyBlob());
|
||||
assertThat(copy.getWrappedApplicationKeys().size())
|
||||
.isEqualTo(snapshot.getWrappedApplicationKeys().size());
|
||||
assertThat(copy.getMaxAttempts()).isEqualTo(snapshot.getMaxAttempts());
|
||||
assertThat(copy.getCounterId()).isEqualTo(snapshot.getCounterId());
|
||||
assertThat(copy.getServerParams()).isEqualTo(snapshot.getServerParams());
|
||||
assertThat(copy.getTrustedHardwarePublicKey())
|
||||
.isEqualTo(snapshot.getTrustedHardwarePublicKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeserialize_failsForNewerVersion() throws Exception {
|
||||
byte[] newVersion = new byte[]{(byte) 2, (byte) 0, (byte) 0, (byte) 0};
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> PersistentKeyChainSnapshot.deserialize(newVersion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeserialize_failsForEmptyData() throws Exception {
|
||||
byte[] empty = new byte[]{};
|
||||
assertThrows(
|
||||
IOException.class,
|
||||
() -> PersistentKeyChainSnapshot.deserialize(empty));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user