• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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