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