KeyChainSnapshot.java revision 86f5bb1a8cfe2d169767fb723d315955dda3a0e6
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