Home | History | Annotate | Download | only in recovery
      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 
     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
     50 public 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 RecoveryCertPath mCertPath;  // The cert path including necessary intermediate certs
     75     private List<KeyChainProtectionParams> mKeyChainProtectionParams;
     76     private List<WrappedApplicationKey> mEntryRecoveryData;
     77     private byte[] mEncryptedRecoveryKeyBlob;
     78 
     79     /**
     80      * Use builder to create an instance of the class.
     81      */
     82     private KeyChainSnapshot() {
     83 
     84     }
     85 
     86     /**
     87      * Snapshot version for given recovery agent. It is incremented when user secret or list of
     88      * application keys changes.
     89      */
     90     public int getSnapshotVersion() {
     91         return mSnapshotVersion;
     92     }
     93 
     94     /**
     95      * Number of user secret guesses allowed during KeyChain recovery.
     96      */
     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     /**
    116      * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
    117      *
    118      * See implementation for binary key format.
    119      *
    120      * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
    121      * @removed
    122      */
    123     @Deprecated
    124     public @NonNull byte[] getTrustedHardwarePublicKey() {
    125         throw new UnsupportedOperationException();
    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          * @removed Use {@link #setTrustedHardwareCertPath} instead.
    231          */
    232         @Deprecated
    233         public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
    234             throw new UnsupportedOperationException();
    235         }
    236 
    237         /**
    238          * Sets CertPath used to validate the trusted hardware public key. The CertPath should
    239          * contain a certificate of the trusted hardware public key and any necessary intermediate
    240          * certificates.
    241          *
    242          * @param certPath The certificate path
    243          * @throws CertificateException if the given certificate path cannot be encoded properly
    244          * @return This builder.
    245          */
    246         public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
    247                 throws CertificateException {
    248             mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
    249             return this;
    250         }
    251 
    252         /**
    253          * Sets UI and key derivation parameters
    254          *
    255          * @param keyChainProtectionParams The UI and key derivation parameters
    256          * @return This builder.
    257          */
    258         public Builder setKeyChainProtectionParams(
    259                 @NonNull List<KeyChainProtectionParams> keyChainProtectionParams) {
    260             mInstance.mKeyChainProtectionParams = keyChainProtectionParams;
    261             return this;
    262         }
    263 
    264         /**
    265          * List of application keys.
    266          *
    267          * @param entryRecoveryData List of application keys
    268          * @return This builder.
    269          */
    270         public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) {
    271             mInstance.mEntryRecoveryData = entryRecoveryData;
    272             return this;
    273         }
    274 
    275         /**
    276          * Sets recovery key blob.
    277          *
    278          * @param encryptedRecoveryKeyBlob The recovery key blob.
    279          * @return This builder.
    280          */
    281         public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) {
    282             mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
    283             return this;
    284         }
    285 
    286 
    287         /**
    288          * Creates a new {@link KeyChainSnapshot} instance.
    289          *
    290          * @return new instance
    291          * @throws NullPointerException if some of the required fields were not set.
    292          */
    293         @NonNull public KeyChainSnapshot build() {
    294             Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
    295                     "keyChainProtectionParams");
    296             Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
    297                     "entryRecoveryData");
    298             Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
    299             Preconditions.checkNotNull(mInstance.mServerParams);
    300             Preconditions.checkNotNull(mInstance.mCertPath);
    301             return mInstance;
    302         }
    303     }
    304 
    305     @Override
    306     public void writeToParcel(Parcel out, int flags) {
    307         out.writeInt(mSnapshotVersion);
    308         out.writeTypedList(mKeyChainProtectionParams);
    309         out.writeByteArray(mEncryptedRecoveryKeyBlob);
    310         out.writeTypedList(mEntryRecoveryData);
    311         out.writeInt(mMaxAttempts);
    312         out.writeLong(mCounterId);
    313         out.writeByteArray(mServerParams);
    314         out.writeTypedObject(mCertPath, /* no flags */ 0);
    315     }
    316 
    317     /**
    318      * @hide
    319      */
    320     protected KeyChainSnapshot(Parcel in) {
    321         mSnapshotVersion = in.readInt();
    322         mKeyChainProtectionParams = in.createTypedArrayList(KeyChainProtectionParams.CREATOR);
    323         mEncryptedRecoveryKeyBlob = in.createByteArray();
    324         mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
    325         mMaxAttempts = in.readInt();
    326         mCounterId = in.readLong();
    327         mServerParams = in.createByteArray();
    328         mCertPath = in.readTypedObject(RecoveryCertPath.CREATOR);
    329     }
    330 
    331     @Override
    332     public int describeContents() {
    333         return 0;
    334     }
    335 }
    336