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