KeyChainSnapshot.java revision c157e21249b01cca18e6712d69c719f245db51a7
1/* 2 * Copyright (C) 2017 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 android.security.keystore.recovery; 18 19import android.annotation.NonNull; 20import android.annotation.SystemApi; 21import android.os.BadParcelableException; 22import android.os.Parcel; 23import android.os.Parcelable; 24 25import com.android.internal.util.Preconditions; 26 27import java.security.cert.CertPath; 28import java.security.cert.CertificateException; 29import java.util.List; 30 31/** 32 * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot: 33 * 34 * <ul> 35 * <li>The user's lock screen changes. (A key derived from the user's lock screen is used to 36 * protected the keychain, which is why this forces a new snapshot.) 37 * <li>A key is added to or removed from the recoverable keychain. 38 * </ul> 39 * 40 * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even 41 * the recovery agent itself should not be able to decipher the data. The recovery agent sends an 42 * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a 43 * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then 44 * sends it to the framework, where it is decrypted using the user's lock screen from their previous 45 * device. 46 * 47 * @hide 48 */ 49@SystemApi 50public final class KeyChainSnapshot implements Parcelable { 51 52 // IMPORTANT! PLEASE READ! 53 // ----------------------- 54 // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following: 55 // - Update the #writeToParcel(Parcel) method below 56 // - Update the #(Parcel) constructor below 57 // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody 58 // accidentally breaks your fields in the Parcel in the future. 59 // - Update com.android.server.locksettings.recoverablekeystore.serialization 60 // .KeyChainSnapshotSerializer to correctly serialize your new field 61 // - Update com.android.server.locksettings.recoverablekeystore.serialization 62 // .KeyChainSnapshotSerializer to correctly deserialize your new field 63 // - Update com.android.server.locksettings.recoverablekeystore.serialization 64 // .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field 65 // in the future. 66 67 private static final int DEFAULT_MAX_ATTEMPTS = 10; 68 private static final long DEFAULT_COUNTER_ID = 1L; 69 70 private int mSnapshotVersion; 71 private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS; 72 private long mCounterId = DEFAULT_COUNTER_ID; 73 private byte[] mServerParams; 74 private byte[] mPublicKey; // The raw public key bytes used 75 private RecoveryCertPath mCertPath; // The cert path including necessary intermediate certs 76 private List<KeyChainProtectionParams> mKeyChainProtectionParams; 77 private List<WrappedApplicationKey> mEntryRecoveryData; 78 private byte[] mEncryptedRecoveryKeyBlob; 79 80 /** 81 * Use builder to create an instance of the class. 82 */ 83 private KeyChainSnapshot() { 84 85 } 86 87 /** 88 * Snapshot version for given recovery agent. It is incremented when user secret or list of 89 * application keys changes. 90 */ 91 public int getSnapshotVersion() { 92 return mSnapshotVersion; 93 } 94 95 /** 96 * Number of user secret guesses allowed during KeyChain recovery. 97 */ 98 public int getMaxAttempts() { 99 return mMaxAttempts; 100 } 101 102 /** 103 * CounterId which is rotated together with user secret. 104 */ 105 public long getCounterId() { 106 return mCounterId; 107 } 108 109 /** 110 * Server parameters. 111 */ 112 public @NonNull byte[] getServerParams() { 113 return mServerParams; 114 } 115 116 /** 117 * Public key used to encrypt {@code encryptedRecoveryKeyBlob}. 118 * 119 * See implementation for binary key format. 120 * 121 * @deprecated Use {@link #getTrustedHardwareCertPath} instead. 122 */ 123 @Deprecated 124 public @NonNull byte[] getTrustedHardwarePublicKey() { 125 return mPublicKey; 126 } 127 128 /** 129 * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}. 130 */ 131 public @NonNull CertPath getTrustedHardwareCertPath() { 132 try { 133 return mCertPath.getCertPath(); 134 } catch (CertificateException e) { 135 // Rethrow an unchecked exception as it should not happen. If such an issue exists, 136 // an exception should have been thrown during service initialization. 137 throw new BadParcelableException(e); 138 } 139 } 140 141 /** 142 * UI and key derivation parameters. Note that combination of secrets may be used. 143 */ 144 public @NonNull List<KeyChainProtectionParams> getKeyChainProtectionParams() { 145 return mKeyChainProtectionParams; 146 } 147 148 /** 149 * List of application keys, with key material encrypted by 150 * the recovery key ({@link #getEncryptedRecoveryKeyBlob}). 151 */ 152 public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() { 153 return mEntryRecoveryData; 154 } 155 156 /** 157 * Recovery key blob, encrypted by user secret and recovery service public key. 158 */ 159 public @NonNull byte[] getEncryptedRecoveryKeyBlob() { 160 return mEncryptedRecoveryKeyBlob; 161 } 162 163 public static final Creator<KeyChainSnapshot> CREATOR = 164 new Creator<KeyChainSnapshot>() { 165 public KeyChainSnapshot createFromParcel(Parcel in) { 166 return new KeyChainSnapshot(in); 167 } 168 169 public KeyChainSnapshot[] newArray(int length) { 170 return new KeyChainSnapshot[length]; 171 } 172 }; 173 174 /** 175 * Builder for creating {@link KeyChainSnapshot}. 176 * @hide 177 */ 178 public static class Builder { 179 private KeyChainSnapshot mInstance = new KeyChainSnapshot(); 180 181 /** 182 * Snapshot version for the recovery agent. 183 * 184 * @param snapshotVersion The snapshot version 185 * @return This builder. 186 */ 187 public Builder setSnapshotVersion(int snapshotVersion) { 188 mInstance.mSnapshotVersion = snapshotVersion; 189 return this; 190 } 191 192 /** 193 * Sets the number of user secret guesses allowed during Keychain recovery. 194 * 195 * @param maxAttempts The maximum number of guesses. 196 * @return This builder. 197 */ 198 public Builder setMaxAttempts(int maxAttempts) { 199 mInstance.mMaxAttempts = maxAttempts; 200 return this; 201 } 202 203 /** 204 * Sets counter id. 205 * 206 * @param counterId The counter id. 207 * @return This builder. 208 */ 209 public Builder setCounterId(long counterId) { 210 mInstance.mCounterId = counterId; 211 return this; 212 } 213 214 /** 215 * Sets server parameters. 216 * 217 * @param serverParams The server parameters 218 * @return This builder. 219 */ 220 public Builder setServerParams(byte[] serverParams) { 221 mInstance.mServerParams = serverParams; 222 return this; 223 } 224 225 /** 226 * Sets public key used to encrypt recovery blob. 227 * 228 * @param publicKey The public key 229 * @return This builder. 230 * @deprecated Use {@link #setTrustedHardwareCertPath} instead. 231 */ 232 @Deprecated 233 public Builder setTrustedHardwarePublicKey(byte[] publicKey) { 234 mInstance.mPublicKey = publicKey; 235 return this; 236 } 237 238 /** 239 * Sets CertPath used to validate the trusted hardware public key. The CertPath should 240 * contain a certificate of the trusted hardware public key and any necessary intermediate 241 * certificates. 242 * 243 * @param certPath The certificate path 244 * @throws CertificateException if the given certificate path cannot be encoded properly 245 * @return This builder. 246 */ 247 public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath) 248 throws CertificateException { 249 mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath); 250 return this; 251 } 252 253 /** 254 * Sets UI and key derivation parameters 255 * 256 * @param keyChainProtectionParams The UI and key derivation parameters 257 * @return This builder. 258 */ 259 public Builder setKeyChainProtectionParams( 260 @NonNull List<KeyChainProtectionParams> keyChainProtectionParams) { 261 mInstance.mKeyChainProtectionParams = keyChainProtectionParams; 262 return this; 263 } 264 265 /** 266 * List of application keys. 267 * 268 * @param entryRecoveryData List of application keys 269 * @return This builder. 270 */ 271 public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) { 272 mInstance.mEntryRecoveryData = entryRecoveryData; 273 return this; 274 } 275 276 /** 277 * Sets recovery key blob. 278 * 279 * @param encryptedRecoveryKeyBlob The recovery key blob. 280 * @return This builder. 281 */ 282 public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) { 283 mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob; 284 return this; 285 } 286 287 288 /** 289 * Creates a new {@link KeyChainSnapshot} instance. 290 * 291 * @return new instance 292 * @throws NullPointerException if some of the required fields were not set. 293 */ 294 @NonNull public KeyChainSnapshot build() { 295 Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams, 296 "keyChainProtectionParams"); 297 Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData, 298 "entryRecoveryData"); 299 Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob); 300 Preconditions.checkNotNull(mInstance.mServerParams); 301 Preconditions.checkNotNull(mInstance.mCertPath); 302 return mInstance; 303 } 304 } 305 306 @Override 307 public void writeToParcel(Parcel out, int flags) { 308 out.writeInt(mSnapshotVersion); 309 out.writeTypedList(mKeyChainProtectionParams); 310 out.writeByteArray(mEncryptedRecoveryKeyBlob); 311 out.writeTypedList(mEntryRecoveryData); 312 out.writeInt(mMaxAttempts); 313 out.writeLong(mCounterId); 314 out.writeByteArray(mServerParams); 315 out.writeByteArray(mPublicKey); 316 out.writeTypedObject(mCertPath, /* no flags */ 0); 317 } 318 319 /** 320 * @hide 321 */ 322 protected KeyChainSnapshot(Parcel in) { 323 mSnapshotVersion = in.readInt(); 324 mKeyChainProtectionParams = in.createTypedArrayList(KeyChainProtectionParams.CREATOR); 325 mEncryptedRecoveryKeyBlob = in.createByteArray(); 326 mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR); 327 mMaxAttempts = in.readInt(); 328 mCounterId = in.readLong(); 329 mServerParams = in.createByteArray(); 330 mPublicKey = in.createByteArray(); 331 mCertPath = in.readTypedObject(RecoveryCertPath.CREATOR); 332 } 333 334 @Override 335 public int describeContents() { 336 return 0; 337 } 338} 339