RecoverySession.java revision 86f5bb1a8cfe2d169767fb723d315955dda3a0e6
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 /** 81e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead. 820916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev */ 837c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @Deprecated 84f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 850916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull public byte[] start( 860916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull byte[] verifierPublicKey, 870916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull byte[] vaultParams, 880916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull byte[] vaultChallenge, 890916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull List<KeyChainProtectionParams> secrets) 900916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throws CertificateException, InternalRecoveryServiceException { 910916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev try { 920916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev byte[] recoveryClaim = 930916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev mRecoveryController.getBinder().startRecoverySession( 940916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev mSessionId, 950916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev verifierPublicKey, 960916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev vaultParams, 970916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev vaultChallenge, 980916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev secrets); 990916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev return recoveryClaim; 1000916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } catch (RemoteException e) { 1010916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw e.rethrowFromSystemServer(); 1020916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } catch (ServiceSpecificException e) { 1037f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT 1047f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { 1050916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw new CertificateException(e.getMessage()); 1060916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 1070916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 1080916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 1090916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 1100916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev 1110916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev /** 112e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead. 1137c1972ff71080568b7288197e96e163d5a469e5fBo Zhu */ 114e7997a3ea7c5dea839220ae832ea5ff7a7dc7742Bo Zhu @Deprecated 1157c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 1167c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @NonNull public byte[] start( 1177c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @NonNull CertPath verifierCertPath, 1187c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @NonNull byte[] vaultParams, 1197c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @NonNull byte[] vaultChallenge, 1207c1972ff71080568b7288197e96e163d5a469e5fBo Zhu @NonNull List<KeyChainProtectionParams> secrets) 1217c1972ff71080568b7288197e96e163d5a469e5fBo Zhu throws CertificateException, InternalRecoveryServiceException { 1227c1972ff71080568b7288197e96e163d5a469e5fBo Zhu // Wrap the CertPath in a Parcelable so it can be passed via Binder calls. 1237c1972ff71080568b7288197e96e163d5a469e5fBo Zhu RecoveryCertPath recoveryCertPath = 1247c1972ff71080568b7288197e96e163d5a469e5fBo Zhu RecoveryCertPath.createRecoveryCertPath(verifierCertPath); 1257c1972ff71080568b7288197e96e163d5a469e5fBo Zhu try { 1267c1972ff71080568b7288197e96e163d5a469e5fBo Zhu byte[] recoveryClaim = 1277c1972ff71080568b7288197e96e163d5a469e5fBo Zhu mRecoveryController.getBinder().startRecoverySessionWithCertPath( 1287c1972ff71080568b7288197e96e163d5a469e5fBo Zhu mSessionId, 129b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu /*rootCertificateAlias=*/ "", // Use the default root cert 130b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu recoveryCertPath, 131b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu vaultParams, 132b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu vaultChallenge, 133b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu secrets); 134b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu return recoveryClaim; 135b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu } catch (RemoteException e) { 136b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu throw e.rethrowFromSystemServer(); 137b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu } catch (ServiceSpecificException e) { 138b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT 139b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { 140b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu throw new CertificateException(e.getMessage()); 141b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu } 142b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 143b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu } 144b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu } 145b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu 146b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu /** 147b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * Starts a recovery session and returns a blob with proof of recovery secret possession. 148b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * The method generates a symmetric key for a session, which trusted remote device can use to 149b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * return recovery key. 150b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * 151b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @param rootCertificateAlias The alias of the root certificate that is already in the Android 152b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * OS. The root certificate will be used for validating {@code verifierCertPath}. 153b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @param verifierCertPath The certificate path used to create the recovery blob on the source 154b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * device. Keystore will verify the certificate path by using the root of trust. 155b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @param vaultParams Must match the parameters in the corresponding field in the recovery blob. 156b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * Used to limit number of guesses. 157b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @param vaultChallenge Data passed from server for this recovery session and used to prevent 158b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * replay attacks. 159b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @param secrets Secrets provided by user, the method only uses type and secret fields. 16086f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey 16186f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev * and contains a proof of user secrets possession, session symmetric 162b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * key and parameters necessary to identify the counter with the number of failed recovery 163b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * attempts. 164b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @throws CertificateException if the {@code verifierCertPath} is invalid. 165b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery 166b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu * service. 167b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu */ 168b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 169b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @NonNull public byte[] start( 170b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @NonNull String rootCertificateAlias, 171b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @NonNull CertPath verifierCertPath, 172b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @NonNull byte[] vaultParams, 173b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @NonNull byte[] vaultChallenge, 174b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu @NonNull List<KeyChainProtectionParams> secrets) 175b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu throws CertificateException, InternalRecoveryServiceException { 176b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu // Wrap the CertPath in a Parcelable so it can be passed via Binder calls. 177b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu RecoveryCertPath recoveryCertPath = 178b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu RecoveryCertPath.createRecoveryCertPath(verifierCertPath); 179b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu try { 180b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu byte[] recoveryClaim = 181b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu mRecoveryController.getBinder().startRecoverySessionWithCertPath( 182b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu mSessionId, 183b31ab6740d66b21a74ffa77b753ea3364288254eBo Zhu rootCertificateAlias, 1847c1972ff71080568b7288197e96e163d5a469e5fBo Zhu recoveryCertPath, 1857c1972ff71080568b7288197e96e163d5a469e5fBo Zhu vaultParams, 1867c1972ff71080568b7288197e96e163d5a469e5fBo Zhu vaultChallenge, 1877c1972ff71080568b7288197e96e163d5a469e5fBo Zhu secrets); 1887c1972ff71080568b7288197e96e163d5a469e5fBo Zhu return recoveryClaim; 1897c1972ff71080568b7288197e96e163d5a469e5fBo Zhu } catch (RemoteException e) { 1907c1972ff71080568b7288197e96e163d5a469e5fBo Zhu throw e.rethrowFromSystemServer(); 1917c1972ff71080568b7288197e96e163d5a469e5fBo Zhu } catch (ServiceSpecificException e) { 1927f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT 1937f414d94fc4f6bd34325f3865b51e8d11acb52adBo Zhu || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { 1947c1972ff71080568b7288197e96e163d5a469e5fBo Zhu throw new CertificateException(e.getMessage()); 1957c1972ff71080568b7288197e96e163d5a469e5fBo Zhu } 1967c1972ff71080568b7288197e96e163d5a469e5fBo Zhu throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 1977c1972ff71080568b7288197e96e163d5a469e5fBo Zhu } 1987c1972ff71080568b7288197e96e163d5a469e5fBo Zhu } 1997c1972ff71080568b7288197e96e163d5a469e5fBo Zhu 2007c1972ff71080568b7288197e96e163d5a469e5fBo Zhu /** 201750b71c6512dad08e9c8eb59c2ad3c0d4fcfe79fRobert Berry * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead. 2020916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev */ 203750b71c6512dad08e9c8eb59c2ad3c0d4fcfe79fRobert Berry @Deprecated 204f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 2050916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev public Map<String, byte[]> recoverKeys( 2060916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull byte[] recoveryKeyBlob, 2070916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev @NonNull List<WrappedApplicationKey> applicationKeys) 2080916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throws SessionExpiredException, DecryptionFailedException, 2090916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev InternalRecoveryServiceException { 2100916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev try { 2110916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev return (Map<String, byte[]>) mRecoveryController.getBinder().recoverKeys( 2120916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev mSessionId, recoveryKeyBlob, applicationKeys); 2130916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } catch (RemoteException e) { 2140916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw e.rethrowFromSystemServer(); 2150916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } catch (ServiceSpecificException e) { 2160916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { 2170916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw new DecryptionFailedException(e.getMessage()); 2180916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 2190916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { 2200916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw new SessionExpiredException(e.getMessage()); 2210916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 2220916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 2230916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 2240916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 2250916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev 2260916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev /** 2274a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * Imports key chain snapshot recovered from a remote vault. 2284a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * 2294a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. 2304a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob 23186f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev * and session key generated by {@link #start}. 23286f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev * @return {@code Map} from recovered keys aliases to their references. 2334a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * @throws SessionExpiredException if {@code session} has since been closed. 2344a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * @throws DecryptionFailedException if unable to decrypt the snapshot. 2354a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. 2364a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry */ 2374a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE) 238fd4ae0b2ddd58f6acbb19632f20e40024e3d85b1Dmitry Dementyev @NonNull public Map<String, Key> recoverKeyChainSnapshot( 2394a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry @NonNull byte[] recoveryKeyBlob, 2404a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry @NonNull List<WrappedApplicationKey> applicationKeys 2414a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { 2424a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry try { 2434a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry Map<String, String> grantAliases = mRecoveryController 2444a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry .getBinder() 2454a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys); 2464a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry return getKeysFromGrants(grantAliases); 2474a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } catch (RemoteException e) { 2484a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry throw e.rethrowFromSystemServer(); 2494a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } catch (ServiceSpecificException e) { 2504a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { 2514a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry throw new DecryptionFailedException(e.getMessage()); 2524a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2534a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { 2544a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry throw new SessionExpiredException(e.getMessage()); 2554a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2564a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 2574a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2584a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2594a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry 2604a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */ 2610bbaf189c259f7d3154737c4284023921dc821b0Dmitry Dementyev private @NonNull Map<String, Key> getKeysFromGrants(@NonNull Map<String, String> grantAliases) 2624a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry throws InternalRecoveryServiceException { 2634a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size()); 2644a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry for (String alias : grantAliases.keySet()) { 2654a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry String grantAlias = grantAliases.get(alias); 2664a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry Key key; 2674a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry try { 2684a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry key = mRecoveryController.getKeyFromGrant(grantAlias); 2694a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } catch (UnrecoverableKeyException e) { 2704a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry throw new InternalRecoveryServiceException( 2714a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry String.format( 2724a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry Locale.US, 2734a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry "Failed to get key '%s' from grant '%s'", 2744a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry alias, 2754a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry grantAlias), e); 2764a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2774a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry keysByAlias.put(alias, key); 2784a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2794a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry return keysByAlias; 2804a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry } 2814a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry 2824a5c87def075c805d4fcae7ff01dd2e78ec27b1aRobert Berry /** 28381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry * An internal session ID, used by the framework to match recovery claims to snapshot responses. 2840916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev * 2850916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev * @hide 28681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry */ 28781ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry String getSessionId() { 28881ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry return mSessionId; 28981ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry } 29081ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry 2910916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev /** 29286f5bb1a8cfe2d169767fb723d315955dda3a0e6Dmitry Dementyev * Deletes all data associated with {@code session}. 2930916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev */ 294f8ae5deba2911b7bc8441df31c0504eaaa687addDmitry Dementyev @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 29581ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry @Override 29681ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry public void close() { 2970916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev try { 2980916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev mRecoveryController.getBinder().closeSession(mSessionId); 2990916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } catch (RemoteException | ServiceSpecificException e) { 3000916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev Log.e(TAG, "Unexpected error trying to close session", e); 3010916e7ca44aba5e6c89d75007da805697fdace9eDmitry Dementyev } 30281ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry } 30381ee34bf957dffe020442e3f0c6c06817397ebf0Robert Berry} 304