/**
* 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);
}
}
* @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;
}
}
/**
- * Sets recovery key blob
+ * Sets recovery key blob.
*
* @param encryptedRecoveryKeyBlob The recovery key blob.
* @return This builder.
* 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,
"entryRecoveryData");
Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
Preconditions.checkNotNull(mInstance.mServerParams);
+ Preconditions.checkNotNull(mInstance.mCertPath);
return mInstance;
}
}
+++ /dev/null
-/*
- * 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;
- }
-}
-
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;
private RecoverySnapshotStorage mRecoverySnapshotStorage;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
- private KeyPair mKeyPair;
private AndroidKeyStoreSecretKey mWrappingKey;
private PlatformEncryptionKey mEncryptKey;
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});
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();
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();
@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);
lockScreenHash,
keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
/*vaultParams=*/ KeySyncUtils.packVaultParams(
- mKeyPair.getPublic(),
+ TestData.CERT_1_PUBLIC_KEY,
counterId,
/*maxAttempts=*/ 10,
TEST_VAULT_HANDLE));
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());
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);
@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);
/*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);
/*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);
/*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);
@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);
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);
@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);
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
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 {
+ " </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);
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));
+ }
}
+++ /dev/null
-/*
- * 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));
- }
-
-}
-