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
521e6a9dcecb92b4a9a8d3c60372821ba7cd830873Dmitry Dementyev    private RecoverySession(@NonNull RecoveryController recoveryController,
531e6a9dcecb92b4a9a8d3c60372821ba7cd830873Dmitry Dementyev            @NonNull String sessionId) {
5481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        mRecoveryController = recoveryController;
5581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        mSessionId = sessionId;
5681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
5781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
5881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    /**
59beafcb50d4f963421bac7e84a4f47f68a8b5e4b6Robert Berry     * A new session, started by the {@link RecoveryController}.
6081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     */
61f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
621e6a9dcecb92b4a9a8d3c60372821ba7cd830873Dmitry Dementyev    static @NonNull RecoverySession newInstance(RecoveryController recoveryController) {
6381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        return new RecoverySession(recoveryController, newSessionId());
6481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
6581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
6681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    /**
6781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     * Returns a new random session ID.
6881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     */
691e6a9dcecb92b4a9a8d3c60372821ba7cd830873Dmitry Dementyev    private static @NonNull String newSessionId() {
7081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        SecureRandom secureRandom = new SecureRandom();
7181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
7281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        secureRandom.nextBytes(sessionId);
7381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        StringBuilder sb = new StringBuilder();
7481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        for (byte b : sessionId) {
7581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry            sb.append(Byte.toHexString(b, /*upperCase=*/ false));
7681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        }
7781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        return sb.toString();
7881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
7981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
8081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    /**
81c157e21249b01cca18e6712d69c719f245db51a7Robert Berry     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
823990ee1c9fcd8f801220edec94e6bef3009809b5Jeff Sharkey     * @removed
830916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     */
847c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    @Deprecated
85f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
860916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    @NonNull public byte[] start(
870916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] verifierPublicKey,
880916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] vaultParams,
890916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] vaultChallenge,
900916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull List<KeyChainProtectionParams> secrets)
910916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throws CertificateException, InternalRecoveryServiceException {
92745d2c98f9467f1befb7ec3a6c485333d4f1b437Dmitry Dementyev        throw new UnsupportedOperationException();
930916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    }
940916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev
950916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    /**
96c157e21249b01cca18e6712d69c719f245db51a7Robert Berry     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
973990ee1c9fcd8f801220edec94e6bef3009809b5Jeff Sharkey     * @removed
987c1972ff71080568b7288197e96e163d5a469e5fBo Zhu     */
99e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu    @Deprecated
1007c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
1017c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    @NonNull public byte[] start(
1027c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull CertPath verifierCertPath,
1037c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull byte[] vaultParams,
1047c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull byte[] vaultChallenge,
1057c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            @NonNull List<KeyChainProtectionParams> secrets)
1067c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            throws CertificateException, InternalRecoveryServiceException {
107745d2c98f9467f1befb7ec3a6c485333d4f1b437Dmitry Dementyev        throw new UnsupportedOperationException();
108b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    }
109b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu
110b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    /**
111b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * Starts a recovery session and returns a blob with proof of recovery secret possession.
112b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * The method generates a symmetric key for a session, which trusted remote device can use to
113b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * return recovery key.
114b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *
115b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param rootCertificateAlias The alias of the root certificate that is already in the Android
116b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     OS. The root certificate will be used for validating {@code verifierCertPath}.
117b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param verifierCertPath The certificate path used to create the recovery blob on the source
118b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     device. Keystore will verify the certificate path by using the root of trust.
119b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
120b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     Used to limit number of guesses.
121b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
122b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     replay attacks.
123b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @param secrets Secrets provided by user, the method only uses type and secret fields.
12486f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev     * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey
12586f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev     * and contains a proof of user secrets possession, session symmetric
126b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     key and parameters necessary to identify the counter with the number of failed recovery
127b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     attempts.
128b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @throws CertificateException if the {@code verifierCertPath} is invalid.
129b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
130b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     *     service.
131b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu     */
132b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
133b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu    @NonNull public byte[] start(
134b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull String rootCertificateAlias,
135b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull CertPath verifierCertPath,
136b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull byte[] vaultParams,
137b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull byte[] vaultChallenge,
138b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            @NonNull List<KeyChainProtectionParams> secrets)
139b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            throws CertificateException, InternalRecoveryServiceException {
140b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
141b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        RecoveryCertPath recoveryCertPath =
142b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
143b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu        try {
144b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu            byte[] recoveryClaim =
145b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
146b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            mSessionId,
147b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu                            rootCertificateAlias,
1487c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            recoveryCertPath,
1497c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            vaultParams,
1507c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            vaultChallenge,
1517c1972ff71080568b7288197e96e163d5a469e5fBo Zhu                            secrets);
1527c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            return recoveryClaim;
1537c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        } catch (RemoteException e) {
1547c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            throw e.rethrowFromSystemServer();
1557c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        } catch (ServiceSpecificException e) {
1567f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
1577f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
15841d2dd2f266eb8dc50afcda253f04f1c7e9ccc0eBo Zhu                throw new CertificateException("Invalid certificate for recovery session", e);
1597c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            }
1607c1972ff71080568b7288197e96e163d5a469e5fBo Zhu            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
1617c1972ff71080568b7288197e96e163d5a469e5fBo Zhu        }
1627c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    }
1637c1972ff71080568b7288197e96e163d5a469e5fBo Zhu
1647c1972ff71080568b7288197e96e163d5a469e5fBo Zhu    /**
165c157e21249b01cca18e6712d69c719f245db51a7Robert Berry     * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
1663990ee1c9fcd8f801220edec94e6bef3009809b5Jeff Sharkey     * @removed
1670916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     */
168750b71c6512dad08e9c8eb59c2ad3c0d4fcfe79fRobert Berry    @Deprecated
169f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
1700916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    public Map<String, byte[]> recoverKeys(
1710916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull byte[] recoveryKeyBlob,
1720916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            @NonNull List<WrappedApplicationKey> applicationKeys)
1730916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            throws SessionExpiredException, DecryptionFailedException,
1740916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            InternalRecoveryServiceException {
175745d2c98f9467f1befb7ec3a6c485333d4f1b437Dmitry Dementyev        throw new UnsupportedOperationException();
1760916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    }
1770916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev
1780916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    /**
1794a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * Imports key chain snapshot recovered from a remote vault.
1804a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     *
1814a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
1824a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
18386f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev     *     and session key generated by {@link #start}.
18486f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev     * @return {@code Map} from recovered keys aliases to their references.
1854a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @throws SessionExpiredException if {@code session} has since been closed.
1864a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @throws DecryptionFailedException if unable to decrypt the snapshot.
1874a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
1884a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry     */
1894a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE)
190fd4ae0b2ddd58f6acbb19632f20e40024e3d85b1Dmitry Dementyev    @NonNull public Map<String, Key> recoverKeyChainSnapshot(
1914a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            @NonNull byte[] recoveryKeyBlob,
1924a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            @NonNull List<WrappedApplicationKey> applicationKeys
1934a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException {
1944a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        try {
1954a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            Map<String, String> grantAliases = mRecoveryController
1964a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                    .getBinder()
1974a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                    .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys);
1984a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            return getKeysFromGrants(grantAliases);
1994a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        } catch (RemoteException e) {
2004a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            throw e.rethrowFromSystemServer();
2014a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        } catch (ServiceSpecificException e) {
2024a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
2034a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                throw new DecryptionFailedException(e.getMessage());
2044a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            }
2054a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
2064a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                throw new SessionExpiredException(e.getMessage());
2074a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            }
2084a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
2094a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        }
2104a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    }
2114a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry
2124a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */
2130bbaf189c259f7d3154737c4284023921dc821b0Dmitry Dementyev    private @NonNull Map<String, Key> getKeysFromGrants(@NonNull Map<String, String> grantAliases)
2144a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            throws InternalRecoveryServiceException {
2154a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size());
2164a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        for (String alias : grantAliases.keySet()) {
2174a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            String grantAlias = grantAliases.get(alias);
2184a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            Key key;
2194a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            try {
2204a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                key = mRecoveryController.getKeyFromGrant(grantAlias);
2214a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            } catch (UnrecoverableKeyException e) {
2224a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                throw new InternalRecoveryServiceException(
2234a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                        String.format(
2244a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                Locale.US,
2254a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                "Failed to get key '%s' from grant '%s'",
2264a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                alias,
2274a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry                                grantAlias), e);
2284a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            }
2294a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry            keysByAlias.put(alias, key);
2304a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        }
2314a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry        return keysByAlias;
2324a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    }
2334a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry
2344a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry    /**
23581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     * An internal session ID, used by the framework to match recovery claims to snapshot responses.
2360916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     *
2370916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     * @hide
23881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry     */
23981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    String getSessionId() {
24081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry        return mSessionId;
24181ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
24281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry
2430916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev    /**
24486f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev     * Deletes all data associated with {@code session}.
2450916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev     */
246f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
24781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    @Override
24881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    public void close() {
2490916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        try {
2500916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            mRecoveryController.getBinder().closeSession(mSessionId);
2510916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        } catch (RemoteException | ServiceSpecificException e) {
2520916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev            Log.e(TAG, "Unexpected error trying to close session", e);
2530916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev        }
25481ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry    }
25581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry}
256