Loading core/java/android/security/keystore/RecoveryClaim.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.security.keystore; /** * An attempt to recover a keychain protected by remote secure hardware. * * @hide */ public class RecoveryClaim { private final RecoverySession mRecoverySession; private final byte[] mClaimBytes; RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) { mRecoverySession = recoverySession; mClaimBytes = claimBytes; } /** * Returns the session associated with the recovery attempt. This is used to match the symmetric * key, which remains internal to the framework, for decrypting the claim response. * * @return The session data. */ public RecoverySession getRecoverySession() { return mRecoverySession; } /** * Returns the encrypted claim's bytes. * * <p>This should be sent by the recovery agent to the remote secure hardware, which will use * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key * to the device. */ public byte[] getClaimBytes() { return mClaimBytes; } } core/java/android/security/keystore/RecoveryManager.java +28 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.util.Log; import com.android.internal.widget.ILockSettings; Loading @@ -36,6 +37,7 @@ import java.util.Map; * @hide */ public class RecoveryManager { private static final String TAG = "RecoveryController"; /** Key has been successfully synced. */ public static final int RECOVERY_STATUS_SYNCED = 0; Loading Loading @@ -371,7 +373,6 @@ public class RecoveryManager { * The method generates symmetric key for a session, which trusted remote device can use to * return recovery key. * * @param sessionId ID for recovery session. * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key * used to create the recovery blob on the source device. * Keystore will verify the certificate using root of trust. Loading @@ -380,30 +381,31 @@ public class RecoveryManager { * @param vaultChallenge Data passed from server for this recovery session and used to prevent * replay attacks * @param secrets Secrets provided by user, the method only uses type and secret fields. * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains * a proof of user secrets, session symmetric key and parameters necessary to identify the * counter with the number of failed recovery attempts. * @return The recovery claim. Claim provides a binary blob with recovery claim. It is * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric * key and parameters necessary to identify the counter with the number of failed recovery * attempts. * @throws BadCertificateFormatException if the {@code verifierPublicKey} is in an incorrect * format. * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery * service. */ public @NonNull byte[] startRecoverySession( @NonNull String sessionId, @NonNull public RecoveryClaim startRecoverySession( @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParams> secrets) throws BadCertificateFormatException, InternalRecoveryServiceException { try { RecoverySession recoverySession = RecoverySession.newInstance(this); byte[] recoveryClaim = mBinder.startRecoverySession( sessionId, recoverySession.getSessionId(), verifierPublicKey, vaultParams, vaultChallenge, secrets); return recoveryClaim; return new RecoveryClaim(recoverySession, recoveryClaim); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { Loading @@ -417,8 +419,8 @@ public class RecoveryManager { /** * Imports keys. * * @param sessionId Id for recovery session, same as in * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. * @param session Related recovery session, as originally created by invoking * {@link #startRecoverySession(byte[], byte[], byte[], List)}. * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob * and session. KeyStore only uses package names from the application info in {@link Loading @@ -429,14 +431,14 @@ public class RecoveryManager { * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. */ public Map<String, byte[]> recoverKeys( @NonNull String sessionId, @NonNull RecoverySession session, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { try { return (Map<String, byte[]>) mBinder.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys); session.getSessionId(), recoveryKeyBlob, applicationKeys); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { Loading @@ -450,6 +452,20 @@ public class RecoveryManager { } } /** * Deletes all data associated with {@code session}. Should not be invoked directly but via * {@link RecoverySession#close()}. * * @hide */ void closeSession(RecoverySession session) { try { mBinder.closeSession(session.getSessionId()); } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Unexpected error trying to close session", e); } } /** * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the * raw material of the key. Loading core/java/android/security/keystore/RecoverySession.java 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.security.keystore; import java.security.SecureRandom; /** * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a * recovery agent. * * @hide */ public class RecoverySession implements AutoCloseable { private static final int SESSION_ID_LENGTH_BYTES = 16; private final String mSessionId; private final RecoveryManager mRecoveryManager; private RecoverySession(RecoveryManager recoveryManager, String sessionId) { mRecoveryManager = recoveryManager; mSessionId = sessionId; } /** * A new session, started by {@code recoveryManager}. */ static RecoverySession newInstance(RecoveryManager recoveryManager) { return new RecoverySession(recoveryManager, newSessionId()); } /** * Returns a new random session ID. */ private static String newSessionId() { SecureRandom secureRandom = new SecureRandom(); byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES]; secureRandom.nextBytes(sessionId); StringBuilder sb = new StringBuilder(); for (byte b : sessionId) { sb.append(Byte.toHexString(b, /*upperCase=*/ false)); } return sb.toString(); } /** * An internal session ID, used by the framework to match recovery claims to snapshot responses. */ String getSessionId() { return mSessionId; } @Override public void close() { mRecoveryManager.closeSession(this); } } core/java/com/android/internal/widget/ILockSettings.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -81,4 +81,5 @@ interface ILockSettings { in List<KeychainProtectionParams> secrets); Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); void closeSession(in String sessionId); } services/core/java/com/android/server/locksettings/LockSettingsService.java +5 −0 Original line number Diff line number Diff line Loading @@ -2028,6 +2028,11 @@ public class LockSettingsService extends ILockSettings.Stub { vaultParams, vaultChallenge, secrets); } @Override public void closeSession(@NonNull String sessionId) throws RemoteException { mRecoverableKeyStoreManager.closeSession(sessionId); } @Override public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) Loading Loading
core/java/android/security/keystore/RecoveryClaim.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.security.keystore; /** * An attempt to recover a keychain protected by remote secure hardware. * * @hide */ public class RecoveryClaim { private final RecoverySession mRecoverySession; private final byte[] mClaimBytes; RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) { mRecoverySession = recoverySession; mClaimBytes = claimBytes; } /** * Returns the session associated with the recovery attempt. This is used to match the symmetric * key, which remains internal to the framework, for decrypting the claim response. * * @return The session data. */ public RecoverySession getRecoverySession() { return mRecoverySession; } /** * Returns the encrypted claim's bytes. * * <p>This should be sent by the recovery agent to the remote secure hardware, which will use * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key * to the device. */ public byte[] getClaimBytes() { return mClaimBytes; } }
core/java/android/security/keystore/RecoveryManager.java +28 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.util.Log; import com.android.internal.widget.ILockSettings; Loading @@ -36,6 +37,7 @@ import java.util.Map; * @hide */ public class RecoveryManager { private static final String TAG = "RecoveryController"; /** Key has been successfully synced. */ public static final int RECOVERY_STATUS_SYNCED = 0; Loading Loading @@ -371,7 +373,6 @@ public class RecoveryManager { * The method generates symmetric key for a session, which trusted remote device can use to * return recovery key. * * @param sessionId ID for recovery session. * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key * used to create the recovery blob on the source device. * Keystore will verify the certificate using root of trust. Loading @@ -380,30 +381,31 @@ public class RecoveryManager { * @param vaultChallenge Data passed from server for this recovery session and used to prevent * replay attacks * @param secrets Secrets provided by user, the method only uses type and secret fields. * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains * a proof of user secrets, session symmetric key and parameters necessary to identify the * counter with the number of failed recovery attempts. * @return The recovery claim. Claim provides a binary blob with recovery claim. It is * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric * key and parameters necessary to identify the counter with the number of failed recovery * attempts. * @throws BadCertificateFormatException if the {@code verifierPublicKey} is in an incorrect * format. * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery * service. */ public @NonNull byte[] startRecoverySession( @NonNull String sessionId, @NonNull public RecoveryClaim startRecoverySession( @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParams> secrets) throws BadCertificateFormatException, InternalRecoveryServiceException { try { RecoverySession recoverySession = RecoverySession.newInstance(this); byte[] recoveryClaim = mBinder.startRecoverySession( sessionId, recoverySession.getSessionId(), verifierPublicKey, vaultParams, vaultChallenge, secrets); return recoveryClaim; return new RecoveryClaim(recoverySession, recoveryClaim); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { Loading @@ -417,8 +419,8 @@ public class RecoveryManager { /** * Imports keys. * * @param sessionId Id for recovery session, same as in * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. * @param session Related recovery session, as originally created by invoking * {@link #startRecoverySession(byte[], byte[], byte[], List)}. * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob * and session. KeyStore only uses package names from the application info in {@link Loading @@ -429,14 +431,14 @@ public class RecoveryManager { * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. */ public Map<String, byte[]> recoverKeys( @NonNull String sessionId, @NonNull RecoverySession session, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { try { return (Map<String, byte[]>) mBinder.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys); session.getSessionId(), recoveryKeyBlob, applicationKeys); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { Loading @@ -450,6 +452,20 @@ public class RecoveryManager { } } /** * Deletes all data associated with {@code session}. Should not be invoked directly but via * {@link RecoverySession#close()}. * * @hide */ void closeSession(RecoverySession session) { try { mBinder.closeSession(session.getSessionId()); } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "Unexpected error trying to close session", e); } } /** * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the * raw material of the key. Loading
core/java/android/security/keystore/RecoverySession.java 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.security.keystore; import java.security.SecureRandom; /** * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a * recovery agent. * * @hide */ public class RecoverySession implements AutoCloseable { private static final int SESSION_ID_LENGTH_BYTES = 16; private final String mSessionId; private final RecoveryManager mRecoveryManager; private RecoverySession(RecoveryManager recoveryManager, String sessionId) { mRecoveryManager = recoveryManager; mSessionId = sessionId; } /** * A new session, started by {@code recoveryManager}. */ static RecoverySession newInstance(RecoveryManager recoveryManager) { return new RecoverySession(recoveryManager, newSessionId()); } /** * Returns a new random session ID. */ private static String newSessionId() { SecureRandom secureRandom = new SecureRandom(); byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES]; secureRandom.nextBytes(sessionId); StringBuilder sb = new StringBuilder(); for (byte b : sessionId) { sb.append(Byte.toHexString(b, /*upperCase=*/ false)); } return sb.toString(); } /** * An internal session ID, used by the framework to match recovery claims to snapshot responses. */ String getSessionId() { return mSessionId; } @Override public void close() { mRecoveryManager.closeSession(this); } }
core/java/com/android/internal/widget/ILockSettings.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -81,4 +81,5 @@ interface ILockSettings { in List<KeychainProtectionParams> secrets); Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); void closeSession(in String sessionId); }
services/core/java/com/android/server/locksettings/LockSettingsService.java +5 −0 Original line number Diff line number Diff line Loading @@ -2028,6 +2028,11 @@ public class LockSettingsService extends ILockSettings.Stub { vaultParams, vaultChallenge, secrets); } @Override public void closeSession(@NonNull String sessionId) throws RemoteException { mRecoverableKeyStoreManager.closeSession(sessionId); } @Override public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) Loading