blob: 2f584717ec6fd90f4c7c348f70849e5d3be66458 [file] [log] [blame]
Robert Berry81ee34b2018-01-23 11:59:59 +00001/*
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;
Dmitry Dementyevf8ae5de2018-01-08 18:08:23 -080020import android.annotation.SystemApi;
Bo Zhu63610802018-03-09 12:32:13 -080021import android.os.BadParcelableException;
Robert Berry81ee34b2018-01-23 11:59:59 +000022import android.os.Parcel;
23import android.os.Parcelable;
24
25import com.android.internal.util.Preconditions;
26
Bo Zhu7c1972f2018-02-22 21:43:52 -080027import java.security.cert.CertPath;
Bo Zhu63610802018-03-09 12:32:13 -080028import java.security.cert.CertificateException;
Robert Berry81ee34b2018-01-23 11:59:59 +000029import 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 */
Dmitry Dementyevf8ae5de2018-01-08 18:08:23 -080049@SystemApi
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080050public final class KeyChainSnapshot implements Parcelable {
Robert Berry52c15f12018-03-29 10:21:50 +010051
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
Robert Berry81ee34b2018-01-23 11:59:59 +000067 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;
Bo Zhu63610802018-03-09 12:32:13 -080074 private RecoveryCertPath mCertPath; // The cert path including necessary intermediate certs
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080075 private List<KeyChainProtectionParams> mKeyChainProtectionParams;
Robert Berry81ee34b2018-01-23 11:59:59 +000076 private List<WrappedApplicationKey> mEntryRecoveryData;
77 private byte[] mEncryptedRecoveryKeyBlob;
78
79 /**
Dmitry Dementyev86f5bb12018-03-27 16:58:50 -070080 * Use builder to create an instance of the class.
Robert Berry81ee34b2018-01-23 11:59:59 +000081 */
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -080082 private KeyChainSnapshot() {
Robert Berry81ee34b2018-01-23 11:59:59 +000083
84 }
85
86 /**
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -070087 * Snapshot version for given recovery agent. It is incremented when user secret or list of
88 * application keys changes.
Robert Berry81ee34b2018-01-23 11:59:59 +000089 */
90 public int getSnapshotVersion() {
91 return mSnapshotVersion;
92 }
93
94 /**
Dmitry Dementyev86f5bb12018-03-27 16:58:50 -070095 * Number of user secret guesses allowed during KeyChain recovery.
Robert Berry81ee34b2018-01-23 11:59:59 +000096 */
97 public int getMaxAttempts() {
98 return mMaxAttempts;
99 }
100
101 /**
102 * CounterId which is rotated together with user secret.
103 */
104 public long getCounterId() {
105 return mCounterId;
106 }
107
108 /**
109 * Server parameters.
110 */
111 public @NonNull byte[] getServerParams() {
112 return mServerParams;
113 }
114
115 /**
Bo Zhu7c1972f2018-02-22 21:43:52 -0800116 * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
117 */
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700118 public @NonNull CertPath getTrustedHardwareCertPath() {
119 try {
120 return mCertPath.getCertPath();
121 } catch (CertificateException e) {
122 // Rethrow an unchecked exception as it should not happen. If such an issue exists,
123 // an exception should have been thrown during service initialization.
124 throw new BadParcelableException(e);
Bo Zhu63610802018-03-09 12:32:13 -0800125 }
Bo Zhu7c1972f2018-02-22 21:43:52 -0800126 }
127
128 /**
Robert Berry81ee34b2018-01-23 11:59:59 +0000129 * UI and key derivation parameters. Note that combination of secrets may be used.
130 */
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800131 public @NonNull List<KeyChainProtectionParams> getKeyChainProtectionParams() {
132 return mKeyChainProtectionParams;
Robert Berry81ee34b2018-01-23 11:59:59 +0000133 }
134
135 /**
136 * List of application keys, with key material encrypted by
137 * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
138 */
139 public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() {
140 return mEntryRecoveryData;
141 }
142
143 /**
144 * Recovery key blob, encrypted by user secret and recovery service public key.
145 */
146 public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
147 return mEncryptedRecoveryKeyBlob;
148 }
149
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800150 public static final @NonNull Creator<KeyChainSnapshot> CREATOR =
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800151 new Creator<KeyChainSnapshot>() {
152 public KeyChainSnapshot createFromParcel(Parcel in) {
153 return new KeyChainSnapshot(in);
Robert Berry81ee34b2018-01-23 11:59:59 +0000154 }
155
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800156 public KeyChainSnapshot[] newArray(int length) {
157 return new KeyChainSnapshot[length];
Robert Berry81ee34b2018-01-23 11:59:59 +0000158 }
159 };
160
161 /**
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800162 * Builder for creating {@link KeyChainSnapshot}.
Dmitry Dementyevf8ae5de2018-01-08 18:08:23 -0800163 * @hide
Robert Berry81ee34b2018-01-23 11:59:59 +0000164 */
165 public static class Builder {
Dmitry Dementyevf8ae5de2018-01-08 18:08:23 -0800166 private KeyChainSnapshot mInstance = new KeyChainSnapshot();
Robert Berry81ee34b2018-01-23 11:59:59 +0000167
168 /**
Dmitry Dementyevfd4ae0b2018-03-23 11:06:24 -0700169 * Snapshot version for the recovery agent.
Robert Berry81ee34b2018-01-23 11:59:59 +0000170 *
171 * @param snapshotVersion The snapshot version
172 * @return This builder.
173 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800174 public @NonNull Builder setSnapshotVersion(int snapshotVersion) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000175 mInstance.mSnapshotVersion = snapshotVersion;
176 return this;
177 }
178
179 /**
180 * Sets the number of user secret guesses allowed during Keychain recovery.
181 *
182 * @param maxAttempts The maximum number of guesses.
183 * @return This builder.
184 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800185 public @NonNull Builder setMaxAttempts(int maxAttempts) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000186 mInstance.mMaxAttempts = maxAttempts;
187 return this;
188 }
189
190 /**
191 * Sets counter id.
192 *
193 * @param counterId The counter id.
194 * @return This builder.
195 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800196 public @NonNull Builder setCounterId(long counterId) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000197 mInstance.mCounterId = counterId;
198 return this;
199 }
200
201 /**
202 * Sets server parameters.
203 *
204 * @param serverParams The server parameters
205 * @return This builder.
206 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800207 public @NonNull Builder setServerParams(byte[] serverParams) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000208 mInstance.mServerParams = serverParams;
209 return this;
210 }
211
212 /**
Bo Zhu7c1972f2018-02-22 21:43:52 -0800213 * Sets CertPath used to validate the trusted hardware public key. The CertPath should
214 * contain a certificate of the trusted hardware public key and any necessary intermediate
215 * certificates.
216 *
Bo Zhu63610802018-03-09 12:32:13 -0800217 * @param certPath The certificate path
218 * @throws CertificateException if the given certificate path cannot be encoded properly
Bo Zhu7c1972f2018-02-22 21:43:52 -0800219 * @return This builder.
220 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800221 public @NonNull Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700222 throws CertificateException {
223 mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
Bo Zhu7c1972f2018-02-22 21:43:52 -0800224 return this;
225 }
226
227 /**
Robert Berry81ee34b2018-01-23 11:59:59 +0000228 * Sets UI and key derivation parameters
229 *
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700230 * @param keyChainProtectionParams The UI and key derivation parameters
Robert Berry81ee34b2018-01-23 11:59:59 +0000231 * @return This builder.
232 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800233 public @NonNull Builder setKeyChainProtectionParams(
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700234 @NonNull List<KeyChainProtectionParams> keyChainProtectionParams) {
235 mInstance.mKeyChainProtectionParams = keyChainProtectionParams;
Robert Berry81ee34b2018-01-23 11:59:59 +0000236 return this;
237 }
238
239 /**
240 * List of application keys.
241 *
242 * @param entryRecoveryData List of application keys
243 * @return This builder.
244 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800245 public @NonNull Builder setWrappedApplicationKeys(
246 @NonNull List<WrappedApplicationKey> entryRecoveryData) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000247 mInstance.mEntryRecoveryData = entryRecoveryData;
248 return this;
249 }
250
251 /**
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700252 * Sets recovery key blob.
Robert Berry81ee34b2018-01-23 11:59:59 +0000253 *
254 * @param encryptedRecoveryKeyBlob The recovery key blob.
255 * @return This builder.
256 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800257 public @NonNull Builder setEncryptedRecoveryKeyBlob(
258 @NonNull byte[] encryptedRecoveryKeyBlob) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000259 mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
260 return this;
261 }
262
263
264 /**
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800265 * Creates a new {@link KeyChainSnapshot} instance.
Robert Berry81ee34b2018-01-23 11:59:59 +0000266 *
267 * @return new instance
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700268 * @throws NullPointerException if some of the required fields were not set.
Robert Berry81ee34b2018-01-23 11:59:59 +0000269 */
Dmitry Dementyevebe53272019-03-05 13:33:24 -0800270 public @NonNull KeyChainSnapshot build() {
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800271 Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
Dmitry Dementyev16d9db52018-03-26 11:31:46 -0700272 "keyChainProtectionParams");
Robert Berry81ee34b2018-01-23 11:59:59 +0000273 Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
274 "entryRecoveryData");
275 Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
276 Preconditions.checkNotNull(mInstance.mServerParams);
Dmitry Dementyev3b67e062018-03-22 17:55:27 -0700277 Preconditions.checkNotNull(mInstance.mCertPath);
Robert Berry81ee34b2018-01-23 11:59:59 +0000278 return mInstance;
279 }
280 }
281
Robert Berry81ee34b2018-01-23 11:59:59 +0000282 @Override
283 public void writeToParcel(Parcel out, int flags) {
284 out.writeInt(mSnapshotVersion);
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800285 out.writeTypedList(mKeyChainProtectionParams);
Robert Berry81ee34b2018-01-23 11:59:59 +0000286 out.writeByteArray(mEncryptedRecoveryKeyBlob);
287 out.writeTypedList(mEntryRecoveryData);
288 out.writeInt(mMaxAttempts);
289 out.writeLong(mCounterId);
290 out.writeByteArray(mServerParams);
Bo Zhu63610802018-03-09 12:32:13 -0800291 out.writeTypedObject(mCertPath, /* no flags */ 0);
Robert Berry81ee34b2018-01-23 11:59:59 +0000292 }
293
294 /**
295 * @hide
296 */
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800297 protected KeyChainSnapshot(Parcel in) {
Robert Berry81ee34b2018-01-23 11:59:59 +0000298 mSnapshotVersion = in.readInt();
Dmitry Dementyev0916e7c2018-01-23 13:02:08 -0800299 mKeyChainProtectionParams = in.createTypedArrayList(KeyChainProtectionParams.CREATOR);
Robert Berry81ee34b2018-01-23 11:59:59 +0000300 mEncryptedRecoveryKeyBlob = in.createByteArray();
301 mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
302 mMaxAttempts = in.readInt();
303 mCounterId = in.readLong();
304 mServerParams = in.createByteArray();
Bo Zhu63610802018-03-09 12:32:13 -0800305 mCertPath = in.readTypedObject(RecoveryCertPath.CREATOR);
Robert Berry81ee34b2018-01-23 11:59:59 +0000306 }
307
308 @Override
309 public int describeContents() {
310 return 0;
311 }
312}