RecoverySession.java revision 750b71c6512dad08e9c8eb59c2ad3c0d4fcfe79f
181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry/*
281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * Copyright (C) 2018 The Android Open Source Project
381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry *
481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * Licensed under the Apache License, Version 2.0 (the "License");
581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * you may not use this file except in compliance with the License.
681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * You may obtain a copy of the License at
781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry *
881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry *      http://www.apache.org/licenses/LICENSE-2.0
981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry *
1081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * Unless required by applicable law or agreed to in writing, software
1181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * distributed under the License is distributed on an "AS IS" BASIS,
1281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * See the License for the specific language governing permissions and
1481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * limitations under the License.
1581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry */
1681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
1781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berrypackage android.security.keystore.recovery;
1881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
194a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berryimport android.Manifest;
200916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport android.annotation.NonNull;
21f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyevimport android.annotation.RequiresPermission;
22f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyevimport android.annotation.SystemApi;
230916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport android.os.RemoteException;
240916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport android.os.ServiceSpecificException;
254a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berryimport android.util.ArrayMap;
260916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport android.util.Log;
270916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev
284a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berryimport java.security.Key;
2981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berryimport java.security.SecureRandom;
304a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berryimport java.security.UnrecoverableKeyException;
317c1972ff71080568b7288197e96e163d5a469e5fBo Zhuimport java.security.cert.CertPath;
320916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport java.security.cert.CertificateException;
330916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport java.util.List;
344a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berryimport java.util.Locale;
350916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyevimport java.util.Map;
3681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
3781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry/**
380916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev * Session to recover a {@link KeyChainSnapshot} from the remote trusted hardware, initiated by a
3981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * recovery agent.
4081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry *
4181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * @hide
4281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry */
43f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev@SystemApi
4481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berrypublic class RecoverySession implements AutoCloseable {
450916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    private static final String TAG = "RecoverySession";
4681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
4781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    private static final int SESSION_ID_LENGTH_BYTES = 16;
4881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
4981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    private final String mSessionId;
5081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    private final RecoveryController mRecoveryController;
5181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
5281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    private RecoverySession(RecoveryController recoveryController, String sessionId) {
5381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        mRecoveryController = recoveryController;
5481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        mSessionId = sessionId;
5581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
5681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
5781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    /**
58beafcb50d4f963421bac7e84a4f47f68a8b5e4b6Robert Berry     * A new session, started by the {@link RecoveryController}.
5981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     */
60f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
6181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    static RecoverySession newInstance(RecoveryController recoveryController) {
6281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        return new RecoverySession(recoveryController, newSessionId());
6381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
6481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
6581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    /**
6681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     * Returns a new random session ID.
6781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     */
6881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    private static String newSessionId() {
6981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        SecureRandom secureRandom = new SecureRandom();
7081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
7181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        secureRandom.nextBytes(sessionId);
7281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        StringBuilder sb = new StringBuilder();
7381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        for (byte b : sessionId) {
7481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry            sb.append(Byte.toHexString(b, /*upperCase=*/ false));
7581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        }
7681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        return sb.toString();
7781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
7881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
7981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    /**
80e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
810916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     */
827c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    @Deprecated
83f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
840916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    @NonNull public byte[] start(
850916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] verifierPublicKey,
860916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] vaultParams,
870916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] vaultChallenge,
880916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull List<KeyChainProtectionParams> secrets)
890916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throws CertificateException, InternalRecoveryServiceException {
900916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        try {
910916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            byte[] recoveryClaim =
920916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                    mRecoveryController.getBinder().startRecoverySession(
930916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                            mSessionId,
940916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                            verifierPublicKey,
950916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                            vaultParams,
960916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                            vaultChallenge,
970916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                            secrets);
980916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            return recoveryClaim;
990916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        } catch (RemoteException e) {
1000916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throw e.rethrowFromSystemServer();
1010916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        } catch (ServiceSpecificException e) {
1027f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
1037f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
1040916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                throw new CertificateException(e.getMessage());
1050916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            }
1060916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
1070916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        }
1080916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    }
1090916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev
1100916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    /**
111e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
1127c1972ff71080568b7288197e96e163d5a469e5fBo Zhu     */
113e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu    @Deprecated
1147c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
1157c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    @NonNull public byte[] start(
1167c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull CertPath verifierCertPath,
1177c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull byte[] vaultParams,
1187c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull byte[] vaultChallenge,
1197c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull List<KeyChainProtectionParams> secrets)
1207c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            throws CertificateException, InternalRecoveryServiceException {
1217c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
1227c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        RecoveryCertPath recoveryCertPath =
1237c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
1247c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        try {
1257c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            byte[] recoveryClaim =
1267c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
1277c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            mSessionId,
128b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            /*rootCertificateAlias=*/ "",  // Use the default root cert
129b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            recoveryCertPath,
130b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            vaultParams,
131b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            vaultChallenge,
132b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            secrets);
133b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            return recoveryClaim;
134b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        } catch (RemoteException e) {
135b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            throw e.rethrowFromSystemServer();
136b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        } catch (ServiceSpecificException e) {
137b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
138b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
139b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                throw new CertificateException(e.getMessage());
140b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            }
141b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
142b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        }
143b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    }
144b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu
145b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    /**
146b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * Starts a recovery session and returns a blob with proof of recovery secret possession.
147b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * The method generates a symmetric key for a session, which trusted remote device can use to
148b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * return recovery key.
149b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *
150b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param rootCertificateAlias The alias of the root certificate that is already in the Android
151b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     OS. The root certificate will be used for validating {@code verifierCertPath}.
152b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param verifierCertPath The certificate path used to create the recovery blob on the source
153b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     device. Keystore will verify the certificate path by using the root of trust.
154b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
155b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     Used to limit number of guesses.
156b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
157b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     replay attacks.
158b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param secrets Secrets provided by user, the method only uses type and secret fields.
159b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
160b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
161b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     key and parameters necessary to identify the counter with the number of failed recovery
162b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     attempts.
163b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @throws CertificateException if the {@code verifierCertPath} is invalid.
164b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
165b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     service.
166b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     */
167b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
168b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    @NonNull public byte[] start(
169b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull String rootCertificateAlias,
170b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull CertPath verifierCertPath,
171b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull byte[] vaultParams,
172b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull byte[] vaultChallenge,
173b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull List<KeyChainProtectionParams> secrets)
174b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            throws CertificateException, InternalRecoveryServiceException {
175b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
176b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        RecoveryCertPath recoveryCertPath =
177b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
178b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        try {
179b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            byte[] recoveryClaim =
180b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
181b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            mSessionId,
182b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            rootCertificateAlias,
1837c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            recoveryCertPath,
1847c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            vaultParams,
1857c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            vaultChallenge,
1867c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            secrets);
1877c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            return recoveryClaim;
1887c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        } catch (RemoteException e) {
1897c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            throw e.rethrowFromSystemServer();
1907c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        } catch (ServiceSpecificException e) {
1917f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
1927f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
1937c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                throw new CertificateException(e.getMessage());
1947c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            }
1957c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
1967c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        }
1977c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    }
1987c1972ff71080568b7288197e96e163d5a469e5fBo Zhu
1997c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    /**
200750b71c6512dad08e9c8eb59c2ad3c0d4fcfe79fRobert Berry     * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
2010916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     */
202750b71c6512dad08e9c8eb59c2ad3c0d4fcfe79fRobert Berry    @Deprecated
203f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
2040916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    public Map<String, byte[]> recoverKeys(
2050916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] recoveryKeyBlob,
2060916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull List<WrappedApplicationKey> applicationKeys)
2070916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throws SessionExpiredException, DecryptionFailedException,
2080916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            InternalRecoveryServiceException {
2090916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        try {
2100916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            return (Map<String, byte[]>) mRecoveryController.getBinder().recoverKeys(
2110916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                    mSessionId, recoveryKeyBlob, applicationKeys);
2120916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        } catch (RemoteException e) {
2130916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throw e.rethrowFromSystemServer();
2140916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        } catch (ServiceSpecificException e) {
2150916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
2160916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                throw new DecryptionFailedException(e.getMessage());
2170916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            }
2180916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
2190916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev                throw new SessionExpiredException(e.getMessage());
2200916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            }
2210916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
2220916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        }
2230916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    }
2240916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev
2250916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    /**
2264a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * Imports key chain snapshot recovered from a remote vault.
2274a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     *
2284a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
2294a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
2304a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     *     and session.
2314a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @throws SessionExpiredException if {@code session} has since been closed.
2324a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @throws DecryptionFailedException if unable to decrypt the snapshot.
2334a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
2344a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     */
2354a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE)
2364a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    public Map<String, Key> recoverKeyChainSnapshot(
2374a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            @NonNull byte[] recoveryKeyBlob,
2384a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            @NonNull List<WrappedApplicationKey> applicationKeys
2394a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException {
2404a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        try {
2414a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            Map<String, String> grantAliases = mRecoveryController
2424a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                    .getBinder()
2434a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                    .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys);
2444a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            return getKeysFromGrants(grantAliases);
2454a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        } catch (RemoteException e) {
2464a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            throw e.rethrowFromSystemServer();
2474a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        } catch (ServiceSpecificException e) {
2484a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
2494a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                throw new DecryptionFailedException(e.getMessage());
2504a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            }
2514a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
2524a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                throw new SessionExpiredException(e.getMessage());
2534a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            }
2544a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
2554a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        }
2564a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    }
2574a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry
2584a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */
2594a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases)
2604a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            throws InternalRecoveryServiceException {
2614a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size());
2624a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        for (String alias : grantAliases.keySet()) {
2634a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            String grantAlias = grantAliases.get(alias);
2644a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            Key key;
2654a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            try {
2664a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                key = mRecoveryController.getKeyFromGrant(grantAlias);
2674a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            } catch (UnrecoverableKeyException e) {
2684a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                throw new InternalRecoveryServiceException(
2694a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                        String.format(
2704a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                Locale.US,
2714a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                "Failed to get key '%s' from grant '%s'",
2724a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                alias,
2734a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                grantAlias), e);
2744a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            }
2754a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            keysByAlias.put(alias, key);
2764a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        }
2774a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        return keysByAlias;
2784a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    }
2794a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry
2804a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    /**
28181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     * An internal session ID, used by the framework to match recovery claims to snapshot responses.
2820916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     *
2830916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     * @hide
28481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     */
28581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    String getSessionId() {
28681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        return mSessionId;
28781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
28881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
2890916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    /**
2900916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     * Deletes all data associated with {@code session}. Should not be invoked directly but via
2910916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     * {@link RecoverySession#close()}.
2920916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     */
293f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
29481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    @Override
29581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    public void close() {
2960916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        try {
2970916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            mRecoveryController.getBinder().closeSession(mSessionId);
2980916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        } catch (RemoteException | ServiceSpecificException e) {
2990916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            Log.e(TAG, "Unexpected error trying to close session", e);
3000916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        }
30181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
30281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry}
303