1/* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.locksettings.recoverablekeystore.serialization; 18 19import static com.google.common.truth.Truth.assertThat; 20 21import android.security.keystore.recovery.KeyChainProtectionParams; 22import android.security.keystore.recovery.KeyChainSnapshot; 23import android.security.keystore.recovery.KeyDerivationParams; 24import android.security.keystore.recovery.WrappedApplicationKey; 25import android.support.test.InstrumentationRegistry; 26import android.support.test.filters.SmallTest; 27import android.support.test.runner.AndroidJUnit4; 28 29import com.android.server.locksettings.recoverablekeystore.TestData; 30 31import org.junit.Test; 32import org.junit.runner.RunWith; 33 34import java.io.ByteArrayInputStream; 35import java.io.ByteArrayOutputStream; 36import java.security.cert.CertPath; 37import java.util.ArrayList; 38import java.util.List; 39 40@SmallTest 41@RunWith(AndroidJUnit4.class) 42public class KeyChainSnapshotSerializerTest { 43 private static final int COUNTER_ID = 2134; 44 private static final int SNAPSHOT_VERSION = 125; 45 private static final int MAX_ATTEMPTS = 21; 46 private static final byte[] SERVER_PARAMS = new byte[] { 8, 2, 4 }; 47 private static final byte[] KEY_BLOB = new byte[] { 124, 53, 53, 53 }; 48 private static final CertPath CERT_PATH = TestData.CERT_PATH_1; 49 private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN; 50 private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PASSWORD; 51 private static final byte[] SALT = new byte[] { 5, 4, 3, 2, 1 }; 52 private static final int MEMORY_DIFFICULTY = 45; 53 private static final int ALGORITHM = KeyDerivationParams.ALGORITHM_SCRYPT; 54 private static final byte[] SECRET = new byte[] { 1, 2, 3, 4 }; 55 56 private static final String TEST_KEY_1_ALIAS = "key1"; 57 private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 }; 58 59 private static final String TEST_KEY_2_ALIAS = "key2"; 60 private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 }; 61 62 private static final String TEST_KEY_3_ALIAS = "key3"; 63 private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 }; 64 65 @Test 66 public void roundTrip_persistsCounterId() throws Exception { 67 assertThat(roundTrip().getCounterId()).isEqualTo(COUNTER_ID); 68 } 69 70 @Test 71 public void roundTrip_persistsSnapshotVersion() throws Exception { 72 assertThat(roundTrip().getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION); 73 } 74 75 @Test 76 public void roundTrip_persistsMaxAttempts() throws Exception { 77 assertThat(roundTrip().getMaxAttempts()).isEqualTo(MAX_ATTEMPTS); 78 } 79 80 @Test 81 public void roundTrip_persistsRecoveryKey() throws Exception { 82 assertThat(roundTrip().getEncryptedRecoveryKeyBlob()).isEqualTo(KEY_BLOB); 83 } 84 85 @Test 86 public void roundTrip_persistsServerParams() throws Exception { 87 assertThat(roundTrip().getServerParams()).isEqualTo(SERVER_PARAMS); 88 } 89 90 @Test 91 public void roundTrip_persistsCertPath() throws Exception { 92 assertThat(roundTrip().getTrustedHardwareCertPath()).isEqualTo(CERT_PATH); 93 } 94 95 @Test 96 public void roundTrip_persistsParamsList() throws Exception { 97 assertThat(roundTrip().getKeyChainProtectionParams()).hasSize(1); 98 } 99 100 @Test 101 public void roundTripParams_persistsUserSecretType() throws Exception { 102 assertThat(roundTripParams().getUserSecretType()).isEqualTo(SECRET_TYPE); 103 } 104 105 @Test 106 public void roundTripParams_persistsLockScreenUi() throws Exception { 107 assertThat(roundTripParams().getLockScreenUiFormat()).isEqualTo(LOCK_SCREEN_UI); 108 } 109 110 @Test 111 public void roundTripParams_persistsSalt() throws Exception { 112 assertThat(roundTripParams().getKeyDerivationParams().getSalt()).isEqualTo(SALT); 113 } 114 115 @Test 116 public void roundTripParams_persistsAlgorithm() throws Exception { 117 assertThat(roundTripParams().getKeyDerivationParams().getAlgorithm()).isEqualTo(ALGORITHM); 118 } 119 120 @Test 121 public void roundTripParams_persistsMemoryDifficulty() throws Exception { 122 assertThat(roundTripParams().getKeyDerivationParams().getMemoryDifficulty()) 123 .isEqualTo(MEMORY_DIFFICULTY); 124 } 125 126 @Test 127 public void roundTripParams_doesNotPersistSecret() throws Exception { 128 assertThat(roundTripParams().getSecret()).isEmpty(); 129 } 130 131 @Test 132 public void roundTripKeys_hasCorrectLength() throws Exception { 133 assertThat(roundTripKeys()).hasSize(3); 134 } 135 136 @Test 137 public void roundTripKeys_0_persistsAlias() throws Exception { 138 assertThat(roundTripKeys().get(0).getAlias()).isEqualTo(TEST_KEY_1_ALIAS); 139 } 140 141 @Test 142 public void roundTripKeys_0_persistsKeyBytes() throws Exception { 143 assertThat(roundTripKeys().get(0).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_1_BYTES); 144 } 145 146 @Test 147 public void roundTripKeys_1_persistsAlias() throws Exception { 148 assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS); 149 } 150 151 @Test 152 public void roundTripKeys_1_persistsKeyBytes() throws Exception { 153 assertThat(roundTripKeys().get(1).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_2_BYTES); 154 } 155 156 @Test 157 public void roundTripKeys_2_persistsAlias() throws Exception { 158 assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS); 159 } 160 161 @Test 162 public void roundTripKeys_2_persistsKeyBytes() throws Exception { 163 assertThat(roundTripKeys().get(2).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_3_BYTES); 164 } 165 166 @Test 167 public void serialize_doesNotThrowForTestSnapshot() throws Exception { 168 KeyChainSnapshotSerializer.serialize( 169 createTestKeyChainSnapshot(), new ByteArrayOutputStream()); 170 } 171 172 private static List<WrappedApplicationKey> roundTripKeys() throws Exception { 173 return roundTrip().getWrappedApplicationKeys(); 174 } 175 176 private static KeyChainProtectionParams roundTripParams() throws Exception { 177 return roundTrip().getKeyChainProtectionParams().get(0); 178 } 179 180 public static KeyChainSnapshot roundTrip() throws Exception { 181 KeyChainSnapshot snapshot = createTestKeyChainSnapshot(); 182 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 183 KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream); 184 return KeyChainSnapshotDeserializer.deserialize( 185 new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); 186 } 187 188 private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception { 189 return new KeyChainSnapshot.Builder() 190 .setCounterId(COUNTER_ID) 191 .setSnapshotVersion(SNAPSHOT_VERSION) 192 .setServerParams(SERVER_PARAMS) 193 .setMaxAttempts(MAX_ATTEMPTS) 194 .setEncryptedRecoveryKeyBlob(KEY_BLOB) 195 .setKeyChainProtectionParams(createKeyChainProtectionParamsList()) 196 .setWrappedApplicationKeys(createKeys()) 197 .setTrustedHardwareCertPath(CERT_PATH) 198 .build(); 199 } 200 201 private static List<WrappedApplicationKey> createKeys() { 202 ArrayList<WrappedApplicationKey> keyList = new ArrayList<>(); 203 keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES)); 204 keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES)); 205 keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES)); 206 return keyList; 207 } 208 209 private static List<KeyChainProtectionParams> createKeyChainProtectionParamsList() { 210 KeyDerivationParams keyDerivationParams = 211 KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY); 212 KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder() 213 .setKeyDerivationParams(keyDerivationParams) 214 .setUserSecretType(SECRET_TYPE) 215 .setLockScreenUiFormat(LOCK_SCREEN_UI) 216 .setSecret(SECRET) 217 .build(); 218 ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList = 219 new ArrayList<>(1); 220 keyChainProtectionParamsList.add(keyChainProtectionParams); 221 return keyChainProtectionParamsList; 222 } 223 224 private static WrappedApplicationKey createKey(String alias, byte[] bytes) { 225 return new WrappedApplicationKey.Builder() 226 .setAlias(alias) 227 .setEncryptedKeyMaterial(bytes) 228 .build(); 229 } 230} 231