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