RecoverySession.java revision 41d2dd2f266eb8dc50afcda253f04f1c7e9ccc0e
15778822d86b0337407514b9372562b86edfa91cdAndreas Huber/* 25778822d86b0337407514b9372562b86edfa91cdAndreas Huber * Copyright (C) 2018 The Android Open Source Project 35778822d86b0337407514b9372562b86edfa91cdAndreas Huber * 45778822d86b0337407514b9372562b86edfa91cdAndreas Huber * Licensed under the Apache License, Version 2.0 (the "License"); 55778822d86b0337407514b9372562b86edfa91cdAndreas Huber * you may not use this file except in compliance with the License. 65778822d86b0337407514b9372562b86edfa91cdAndreas Huber * You may obtain a copy of the License at 75778822d86b0337407514b9372562b86edfa91cdAndreas Huber * 85778822d86b0337407514b9372562b86edfa91cdAndreas Huber * http://www.apache.org/licenses/LICENSE-2.0 95778822d86b0337407514b9372562b86edfa91cdAndreas Huber * 105778822d86b0337407514b9372562b86edfa91cdAndreas Huber * Unless required by applicable law or agreed to in writing, software 115778822d86b0337407514b9372562b86edfa91cdAndreas Huber * distributed under the License is distributed on an "AS IS" BASIS, 125778822d86b0337407514b9372562b86edfa91cdAndreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135778822d86b0337407514b9372562b86edfa91cdAndreas Huber * See the License for the specific language governing permissions and 145778822d86b0337407514b9372562b86edfa91cdAndreas Huber * limitations under the License. 155778822d86b0337407514b9372562b86edfa91cdAndreas Huber */ 165778822d86b0337407514b9372562b86edfa91cdAndreas Huber 175778822d86b0337407514b9372562b86edfa91cdAndreas Huberpackage android.security.keystore.recovery; 185778822d86b0337407514b9372562b86edfa91cdAndreas Huber 19fc7fca77caa12993dd938d5ff43797d781291027Lajos Molnarimport android.Manifest; 205778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport android.annotation.NonNull; 212606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhangimport android.annotation.RequiresPermission; 225778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport android.annotation.SystemApi; 235778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport android.os.RemoteException; 24c481b5012a5f6cf72e5e93b36f1ed4c9169916f2Jeff Tinkerimport android.os.ServiceSpecificException; 2567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wuimport android.util.ArrayMap; 262606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhangimport android.util.Log; 27c481b5012a5f6cf72e5e93b36f1ed4c9169916f2Jeff Tinker 2879608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhangimport java.security.Key; 291a2952aee048ca7b1765e2bc09ebe9aeddaeafa3Mathias Agopianimport java.security.SecureRandom; 30ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huberimport java.security.UnrecoverableKeyException; 31d291c222357303b9611cab89d0c3b047584ef377Chong Zhangimport java.security.cert.CertPath; 3267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wuimport java.security.cert.CertificateException; 335778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport java.util.List; 345778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport java.util.Locale; 355778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport java.util.Map; 365b8987e7de9d04b09153f329c680d2316cdb44ecAndreas Huber 37ed3e3e046840d5bf1ca84a8c0cc097425e89d6d6Andreas Huber/** 385778822d86b0337407514b9372562b86edfa91cdAndreas Huber * Session to recover a {@link KeyChainSnapshot} from the remote trusted hardware, initiated by a 397cd58537932ef6f481f68be0b9c597a89cebdfecAndy McFadden * recovery agent. 402606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhang * 416f9439efd2a6004b588605f6a9d4af20c98e8e80Marco Nelissen * @hide 42e96ee699aca0f711d41e6c0833e5de2341c4a36dAndreas Huber */ 435778822d86b0337407514b9372562b86edfa91cdAndreas Huber@SystemApi 44744f5739019d1fd917f981e740b353c3d73fd1a8David Smithpublic class RecoverySession implements AutoCloseable { 455778822d86b0337407514b9372562b86edfa91cdAndreas Huber private static final String TAG = "RecoverySession"; 46d291c222357303b9611cab89d0c3b047584ef377Chong Zhang 47d291c222357303b9611cab89d0c3b047584ef377Chong Zhang private static final int SESSION_ID_LENGTH_BYTES = 16; 488b23759763dbf11b0c628a7e62dc5b3dea7dc188Lajos Molnar 4999e69716215cd0665379bc90d708f2ea8689831dRuben Brunk private final String mSessionId; 502606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhang private final RecoveryController mRecoveryController; 512606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhang 522606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhang private RecoverySession(@NonNull RecoveryController recoveryController, 53e96ee699aca0f711d41e6c0833e5de2341c4a36dAndreas Huber @NonNull String sessionId) { 545778822d86b0337407514b9372562b86edfa91cdAndreas Huber mRecoveryController = recoveryController; 555778822d86b0337407514b9372562b86edfa91cdAndreas Huber mSessionId = sessionId; 5667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 5767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu 5867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu /** 5967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * A new session, started by the {@link RecoveryController}. 6067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu */ 6147a2e875bdd2bd25cb8500208940ff1488b01e08Ronghua Wu @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 6267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu static @NonNull RecoverySession newInstance(RecoveryController recoveryController) { 6367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu return new RecoverySession(recoveryController, newSessionId()); 6467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 654b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu 6667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu /** 6767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * Returns a new random session ID. 6867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu */ 6967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu private static @NonNull String newSessionId() { 7067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu SecureRandom secureRandom = new SecureRandom(); 7167e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES]; 7267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu secureRandom.nextBytes(sessionId); 7367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu StringBuilder sb = new StringBuilder(); 7467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu for (byte b : sessionId) { 7567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu sb.append(Byte.toHexString(b, /*upperCase=*/ false)); 7647a2e875bdd2bd25cb8500208940ff1488b01e08Ronghua Wu } 774b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu return sb.toString(); 784b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu } 794b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu 804b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu /** 814b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead. 824b710f086070fabe022b3a1f474bfcbec842b8fcRonghua Wu */ 8367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @Deprecated 8467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 8567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull public byte[] start( 8667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull byte[] verifierPublicKey, 8767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull byte[] vaultParams, 8867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull byte[] vaultChallenge, 898f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu @NonNull List<KeyChainProtectionParams> secrets) 908f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu throws CertificateException, InternalRecoveryServiceException { 918f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu try { 928f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu byte[] recoveryClaim = 938f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu mRecoveryController.getBinder().startRecoverySession( 948f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu mSessionId, 958f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu verifierPublicKey, 968f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu vaultParams, 978f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu vaultChallenge, 988f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu secrets); 998f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu return recoveryClaim; 1008f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu } catch (RemoteException e) { 1018f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu throw e.rethrowFromSystemServer(); 1028f9dd872366f54b6260506c75c3d0cc3f9f73f81Ronghua Wu } catch (ServiceSpecificException e) { 10367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT 10467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { 10567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu throw new CertificateException("Invalid certificate for recovery session", e); 10667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 10767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 10867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 10967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 11067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu 11167e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu /** 11267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead. 11368845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu */ 11468845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu @Deprecated 11568845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 11668845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu @NonNull public byte[] start( 11768845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu @NonNull CertPath verifierCertPath, 11867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull byte[] vaultParams, 11967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull byte[] vaultChallenge, 12067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull List<KeyChainProtectionParams> secrets) 12167e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu throws CertificateException, InternalRecoveryServiceException { 12267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu // Wrap the CertPath in a Parcelable so it can be passed via Binder calls. 12367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu RecoveryCertPath recoveryCertPath = 12467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu RecoveryCertPath.createRecoveryCertPath(verifierCertPath); 12567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu try { 12667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu byte[] recoveryClaim = 12767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu mRecoveryController.getBinder().startRecoverySessionWithCertPath( 12867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu mSessionId, 12967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu /*rootCertificateAlias=*/ "", // Use the default root cert 13067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu recoveryCertPath, 13167e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu vaultParams, 13267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu vaultChallenge, 13367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu secrets); 134e4237177a4a3eea059cd74247b2d770d301a8230Ronghua Wu return recoveryClaim; 13567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } catch (RemoteException e) { 13667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu throw e.rethrowFromSystemServer(); 13767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } catch (ServiceSpecificException e) { 13867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT 13967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { 14067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu throw new CertificateException("Invalid certificate for recovery session", e); 14167e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 14267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 14367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 14467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu } 14567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu 14667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu /** 14767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * Starts a recovery session and returns a blob with proof of recovery secret possession. 14867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * The method generates a symmetric key for a session, which trusted remote device can use to 14967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * return recovery key. 15067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * 15137c8924c508a7c9b8bd3c8ce80fc005070531902Ronghua Wu * @param rootCertificateAlias The alias of the root certificate that is already in the Android 15267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * OS. The root certificate will be used for validating {@code verifierCertPath}. 15367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @param verifierCertPath The certificate path used to create the recovery blob on the source 15467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * device. Keystore will verify the certificate path by using the root of trust. 15567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @param vaultParams Must match the parameters in the corresponding field in the recovery blob. 15667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * Used to limit number of guesses. 15767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @param vaultChallenge Data passed from server for this recovery session and used to prevent 15867e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * replay attacks. 15937c8924c508a7c9b8bd3c8ce80fc005070531902Ronghua Wu * @param secrets Secrets provided by user, the method only uses type and secret fields. 16067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey 16167e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * and contains a proof of user secrets possession, session symmetric 16267e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * key and parameters necessary to identify the counter with the number of failed recovery 16337c8924c508a7c9b8bd3c8ce80fc005070531902Ronghua Wu * attempts. 16467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @throws CertificateException if the {@code verifierCertPath} is invalid. 16567e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery 16667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu * service. 16767e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu */ 16837c8924c508a7c9b8bd3c8ce80fc005070531902Ronghua Wu @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 16967e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull public byte[] start( 17067e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu @NonNull String rootCertificateAlias, 1715778822d86b0337407514b9372562b86edfa91cdAndreas Huber @NonNull CertPath verifierCertPath, 1725778822d86b0337407514b9372562b86edfa91cdAndreas Huber @NonNull byte[] vaultParams, 1736fc17d1a7c5d2fb117491b2e9f66c6236b526508Lajos Molnar @NonNull byte[] vaultChallenge, 17468845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu @NonNull List<KeyChainProtectionParams> secrets) 1755778822d86b0337407514b9372562b86edfa91cdAndreas Huber throws CertificateException, InternalRecoveryServiceException { 176251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung // Wrap the CertPath in a Parcelable so it can be passed via Binder calls. 177251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung RecoveryCertPath recoveryCertPath = 178251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung RecoveryCertPath.createRecoveryCertPath(verifierCertPath); 179251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung try { 180251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung byte[] recoveryClaim = 1815778822d86b0337407514b9372562b86edfa91cdAndreas Huber mRecoveryController.getBinder().startRecoverySessionWithCertPath( 1825778822d86b0337407514b9372562b86edfa91cdAndreas Huber mSessionId, 1835778822d86b0337407514b9372562b86edfa91cdAndreas Huber rootCertificateAlias, 1845778822d86b0337407514b9372562b86edfa91cdAndreas Huber recoveryCertPath, 1856fc17d1a7c5d2fb117491b2e9f66c6236b526508Lajos Molnar vaultParams, 18668845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu vaultChallenge, 1875778822d86b0337407514b9372562b86edfa91cdAndreas Huber secrets); 188251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung return recoveryClaim; 189251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung } catch (RemoteException e) { 190251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung throw e.rethrowFromSystemServer(); 191251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung } catch (ServiceSpecificException e) { 192251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT 1935778822d86b0337407514b9372562b86edfa91cdAndreas Huber || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) { 1945778822d86b0337407514b9372562b86edfa91cdAndreas Huber throw new CertificateException("Invalid certificate for recovery session", e); 195d291c222357303b9611cab89d0c3b047584ef377Chong Zhang } 1965b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 1975b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar } 1985b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar } 1995b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar 2005b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar /** 2015b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead. 2025b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar */ 2035b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar @Deprecated 2045b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 2055b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar public Map<String, byte[]> recoverKeys( 2065b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar @NonNull byte[] recoveryKeyBlob, 2075b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar @NonNull List<WrappedApplicationKey> applicationKeys) 2085b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar throws SessionExpiredException, DecryptionFailedException, 2095b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar InternalRecoveryServiceException { 2105b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar try { 2115b05e49e6550cb2abf1a88272d6cd460b8957176Lajos Molnar return (Map<String, byte[]>) mRecoveryController.getBinder().recoverKeys( 212d291c222357303b9611cab89d0c3b047584ef377Chong Zhang mSessionId, recoveryKeyBlob, applicationKeys); 213d291c222357303b9611cab89d0c3b047584ef377Chong Zhang } catch (RemoteException e) { 214d291c222357303b9611cab89d0c3b047584ef377Chong Zhang throw e.rethrowFromSystemServer(); 215d291c222357303b9611cab89d0c3b047584ef377Chong Zhang } catch (ServiceSpecificException e) { 216d291c222357303b9611cab89d0c3b047584ef377Chong Zhang if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { 21779608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang throw new DecryptionFailedException(e.getMessage()); 21879608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } 21979608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { 22079608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang throw new SessionExpiredException(e.getMessage()); 22179608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } 22279608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 22379608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } 22479608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } 22579608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang 22679608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang /** 22779608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * Imports key chain snapshot recovered from a remote vault. 22879608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * 22979608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. 23079608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob 23179608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * and session key generated by {@link #start}. 23279608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * @return {@code Map} from recovered keys aliases to their references. 23379608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * @throws SessionExpiredException if {@code session} has since been closed. 23479608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * @throws DecryptionFailedException if unable to decrypt the snapshot. 23579608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. 23679608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang */ 23779608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE) 238d291c222357303b9611cab89d0c3b047584ef377Chong Zhang @NonNull public Map<String, Key> recoverKeyChainSnapshot( 239d291c222357303b9611cab89d0c3b047584ef377Chong Zhang @NonNull byte[] recoveryKeyBlob, 240d291c222357303b9611cab89d0c3b047584ef377Chong Zhang @NonNull List<WrappedApplicationKey> applicationKeys 241d291c222357303b9611cab89d0c3b047584ef377Chong Zhang ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { 242d291c222357303b9611cab89d0c3b047584ef377Chong Zhang try { 243d291c222357303b9611cab89d0c3b047584ef377Chong Zhang Map<String, String> grantAliases = mRecoveryController 244d291c222357303b9611cab89d0c3b047584ef377Chong Zhang .getBinder() 245d291c222357303b9611cab89d0c3b047584ef377Chong Zhang .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys); 246d291c222357303b9611cab89d0c3b047584ef377Chong Zhang return getKeysFromGrants(grantAliases); 247d291c222357303b9611cab89d0c3b047584ef377Chong Zhang } catch (RemoteException e) { 248d291c222357303b9611cab89d0c3b047584ef377Chong Zhang throw e.rethrowFromSystemServer(); 24979608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } catch (ServiceSpecificException e) { 25079608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { 25179608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang throw new DecryptionFailedException(e.getMessage()); 25279608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } 25379608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { 25479608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang throw new SessionExpiredException(e.getMessage()); 25579608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang } 25679608158c2254fe1357959157f2d0c1560a8a6c6Chong Zhang throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); 257d291c222357303b9611cab89d0c3b047584ef377Chong Zhang } 258d291c222357303b9611cab89d0c3b047584ef377Chong Zhang } 259d291c222357303b9611cab89d0c3b047584ef377Chong Zhang 26068845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */ 2615778822d86b0337407514b9372562b86edfa91cdAndreas Huber private @NonNull Map<String, Key> getKeysFromGrants(@NonNull Map<String, String> grantAliases) 26247a2e875bdd2bd25cb8500208940ff1488b01e08Ronghua Wu throws InternalRecoveryServiceException { 2635778822d86b0337407514b9372562b86edfa91cdAndreas Huber ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size()); 26492cd05b8f2e994aabcdda5d7454c96a707dc9579Lajos Molnar for (String alias : grantAliases.keySet()) { 2657cd58537932ef6f481f68be0b9c597a89cebdfecAndy McFadden String grantAlias = grantAliases.get(alias); 2665778822d86b0337407514b9372562b86edfa91cdAndreas Huber Key key; 267251d4be8aa5ab80bc915a82a2420233bdc62018eAndy Hung try { 2685778822d86b0337407514b9372562b86edfa91cdAndreas Huber key = mRecoveryController.getKeyFromGrant(grantAlias); 269ee4e1b1a63758941460ae79a064249d3a5189443Lajos Molnar } catch (UnrecoverableKeyException e) { 27068845c14ebf2c7282800b1abffde38d8e9a57aabRonghua Wu throw new InternalRecoveryServiceException( 2712606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhang String.format( 2722606b10d51c2dceb851a2ea63e803aba4134bf00Chong Zhang Locale.US, 27367e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu "Failed to get key '%s' from grant '%s'", 27467e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu alias, 275505aab41c0e8e79a49d4506344fcd9d220d5965bChong Zhang grantAlias), e); 2765778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 2775778822d86b0337407514b9372562b86edfa91cdAndreas Huber keysByAlias.put(alias, key); 2785778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 2796507d14c6d10f93d390de62b9eed267f9b544985Andy McFadden return keysByAlias; 2803d66eb4128aebef31bb0fa44c4d53d6122294a26Chong Zhang } 2813d66eb4128aebef31bb0fa44c4d53d6122294a26Chong Zhang 2825778822d86b0337407514b9372562b86edfa91cdAndreas Huber /** 2835778822d86b0337407514b9372562b86edfa91cdAndreas Huber * An internal session ID, used by the framework to match recovery claims to snapshot responses. 2845778822d86b0337407514b9372562b86edfa91cdAndreas Huber * 2855778822d86b0337407514b9372562b86edfa91cdAndreas Huber * @hide 28667e7f543c7f1c4fe4ee1989ceb0aebe44a63b49eRonghua Wu */ 2875778822d86b0337407514b9372562b86edfa91cdAndreas Huber String getSessionId() { 2885778822d86b0337407514b9372562b86edfa91cdAndreas Huber return mSessionId; 2895778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 2905778822d86b0337407514b9372562b86edfa91cdAndreas Huber 2915778822d86b0337407514b9372562b86edfa91cdAndreas Huber /** 2925778822d86b0337407514b9372562b86edfa91cdAndreas Huber * Deletes all data associated with {@code session}. 2935778822d86b0337407514b9372562b86edfa91cdAndreas Huber */ 2945778822d86b0337407514b9372562b86edfa91cdAndreas Huber @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) 2955778822d86b0337407514b9372562b86edfa91cdAndreas Huber @Override 2965778822d86b0337407514b9372562b86edfa91cdAndreas Huber public void close() { 2975778822d86b0337407514b9372562b86edfa91cdAndreas Huber try { 2985778822d86b0337407514b9372562b86edfa91cdAndreas Huber mRecoveryController.getBinder().closeSession(mSessionId); 2995778822d86b0337407514b9372562b86edfa91cdAndreas Huber } catch (RemoteException | ServiceSpecificException e) { 3005778822d86b0337407514b9372562b86edfa91cdAndreas Huber Log.e(TAG, "Unexpected error trying to close session", e); 3015778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 3025778822d86b0337407514b9372562b86edfa91cdAndreas Huber } 3035778822d86b0337407514b9372562b86edfa91cdAndreas Huber} 3045778822d86b0337407514b9372562b86edfa91cdAndreas Huber