Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f37f0cc3 authored by Bo Zhu's avatar Bo Zhu Committed by Android (Google) Code Review
Browse files

Merge "Add an optional metadata blob for recoverable application keys"

parents 5daa3568 c704834c
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -5421,7 +5421,8 @@ package android.security.keystore.recovery {
  public class RecoveryController {
    method public android.security.keystore.recovery.RecoverySession createRecoverySession();
    method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
    method public deprecated java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
    method public java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
    method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
    method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
    method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
@@ -5429,7 +5430,8 @@ package android.security.keystore.recovery {
    method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
    method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
    method public java.util.Map<java.lang.String, java.security.cert.X509Certificate> getRootCertificates();
    method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
    method public deprecated java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
    method public java.security.Key importKey(java.lang.String, byte[], byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
    method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
    method public static boolean isRecoverableKeyStoreEnabled(android.content.Context);
    method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -5456,6 +5458,7 @@ package android.security.keystore.recovery {
    method public int describeContents();
    method public java.lang.String getAlias();
    method public byte[] getEncryptedKeyMaterial();
    method public byte[] getMetadata();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.WrappedApplicationKey> CREATOR;
  }
@@ -5465,6 +5468,7 @@ package android.security.keystore.recovery {
    method public android.security.keystore.recovery.WrappedApplicationKey build();
    method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String);
    method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]);
    method public android.security.keystore.recovery.WrappedApplicationKey.Builder setMetadata(byte[]);
  }
}
+89 −0
Original line number Diff line number Diff line
@@ -533,7 +533,10 @@ public class RecoveryController {
     *     service.
     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
     *     screen is required to generate recoverable keys.
     *
     * @deprecated Use the method {@link #generateKey(String, byte[])} instead.
     */
    @Deprecated
    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
    public @NonNull Key generateKey(@NonNull String alias) throws InternalRecoveryServiceException,
            LockScreenRequiredException {
@@ -555,6 +558,47 @@ public class RecoveryController {
        }
    }

    /**
     * Generates a recoverable key with the given {@code alias} and {@code metadata}.
     *
     * <p>The metadata should contain any data that needs to be cryptographically bound to the
     * generated key, but does not need to be encrypted by the key. For example, the metadata can
     * be a byte string describing the algorithms and non-secret parameters to be used with the
     * key. The supplied metadata can later be obtained via
     * {@link WrappedApplicationKey#getMetadata()}.
     *
     * <p>During the key recovery process, the same metadata has to be supplied via
     * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process
     * will fail due to the checking of the cryptographic binding. This can help prevent
     * potential attacks that try to swap key materials on the backup server and trick the
     * application to use keys with different algorithms or parameters.
     *
     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
     *     service.
     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
     *     screen is required to generate recoverable keys.
     */
    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
    public @NonNull Key generateKey(@NonNull String alias, @Nullable byte[] metadata)
            throws InternalRecoveryServiceException, LockScreenRequiredException {
        try {
            String grantAlias = mBinder.generateKeyWithMetadata(alias, metadata);
            if (grantAlias == null) {
                throw new InternalRecoveryServiceException("null grant alias");
            }
            return getKeyFromGrant(grantAlias);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (UnrecoverableKeyException e) {
            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
        } catch (ServiceSpecificException e) {
            if (e.errorCode == ERROR_INSECURE_USER) {
                throw new LockScreenRequiredException(e.getMessage());
            }
            throw wrapUnexpectedServiceSpecificException(e);
        }
    }

    /**
     * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code
     * keyBytes}.
@@ -564,7 +608,9 @@ public class RecoveryController {
     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
     *     screen is required to generate recoverable keys.
     *
     * @deprecated Use the method {@link #importKey(String, byte[], byte[])} instead.
     */
    @Deprecated
    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
    public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes)
            throws InternalRecoveryServiceException, LockScreenRequiredException {
@@ -586,6 +632,49 @@ public class RecoveryController {
        }
    }

    /**
     * Imports a recoverable 256-bit AES key with the given {@code alias}, the raw bytes {@code
     * keyBytes}, and the {@code metadata}.
     *
     * <p>The metadata should contain any data that needs to be cryptographically bound to the
     * imported key, but does not need to be encrypted by the key. For example, the metadata can
     * be a byte string describing the algorithms and non-secret parameters to be used with the
     * key. The supplied metadata can later be obtained via
     * {@link WrappedApplicationKey#getMetadata()}.
     *
     * <p>During the key recovery process, the same metadata has to be supplied via
     * {@link WrappedApplicationKey.Builder#setMetadata(byte[])}; otherwise, the recovery process
     * will fail due to the checking of the cryptographic binding. This can help prevent
     * potential attacks that try to swap key materials on the backup server and trick the
     * application to use keys with different algorithms or parameters.
     *
     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
     *     service.
     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
     *     screen is required to generate recoverable keys.
     */
    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
    public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes,
            @Nullable byte[] metadata)
            throws InternalRecoveryServiceException, LockScreenRequiredException {
        try {
            String grantAlias = mBinder.importKeyWithMetadata(alias, keyBytes, metadata);
            if (grantAlias == null) {
                throw new InternalRecoveryServiceException("Null grant alias");
            }
            return getKeyFromGrant(grantAlias);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (UnrecoverableKeyException e) {
            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
        } catch (ServiceSpecificException e) {
            if (e.errorCode == ERROR_INSECURE_USER) {
                throw new LockScreenRequiredException(e.getMessage());
            }
            throw wrapUnexpectedServiceSpecificException(e);
        }
    }

    /**
     * Gets a key called {@code alias} from the recoverable key store.
     *
+26 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.security.keystore.recovery;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +42,8 @@ public final class WrappedApplicationKey implements Parcelable {
    private String mAlias;
    // The only supported format is AES-256 symmetric key.
    private byte[] mEncryptedKeyMaterial;
    // The optional metadata that's authenticated (but unencrypted) with the key material.
    private byte[] mMetadata;

    // IMPORTANT! PLEASE READ!
    // -----------------------
@@ -80,12 +83,22 @@ public final class WrappedApplicationKey implements Parcelable {
         * @param encryptedKeyMaterial The key material
         * @return This builder
         */

        public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) {
            mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial;
            return this;
        }

        /**
         * Sets the metadata that is authenticated (but unecrypted) with the key material.
         *
         * @param metadata The metadata
         * @return This builder
         */
        public Builder setMetadata(@Nullable byte[] metadata) {
            mInstance.mMetadata = metadata;
            return this;
        }

        /**
         * Creates a new {@link WrappedApplicationKey} instance.
         *
@@ -102,9 +115,10 @@ public final class WrappedApplicationKey implements Parcelable {
    private WrappedApplicationKey() { }

    /**
     * Deprecated - consider using Builder.
     * @deprecated Use the builder instead.
     * @hide
     */
    @Deprecated
    public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
        mAlias = Preconditions.checkNotNull(alias);
        mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
@@ -124,6 +138,11 @@ public final class WrappedApplicationKey implements Parcelable {
        return mEncryptedKeyMaterial;
    }

    /** The metadata with the key. */
    public @Nullable byte[] getMetadata() {
        return mMetadata;
    }

    public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
            new Parcelable.Creator<WrappedApplicationKey>() {
                public WrappedApplicationKey createFromParcel(Parcel in) {
@@ -139,6 +158,7 @@ public final class WrappedApplicationKey implements Parcelable {
    public void writeToParcel(Parcel out, int flags) {
        out.writeString(mAlias);
        out.writeByteArray(mEncryptedKeyMaterial);
        out.writeByteArray(mMetadata);
    }

    /**
@@ -147,6 +167,10 @@ public final class WrappedApplicationKey implements Parcelable {
    protected WrappedApplicationKey(Parcel in) {
        mAlias = in.readString();
        mEncryptedKeyMaterial = in.createByteArray();
        // Check if there is still data to be read.
        if (in.dataAvail() > 0) {
            mMetadata = in.createByteArray();
        }
    }

    @Override
+2 −0
Original line number Diff line number Diff line
@@ -62,7 +62,9 @@ interface ILockSettings {
            in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile);
    KeyChainSnapshot getKeyChainSnapshot();
    String generateKey(String alias);
    String generateKeyWithMetadata(String alias, in byte[] metadata);
    String importKey(String alias, in byte[] keyBytes);
    String importKeyWithMetadata(String alias, in byte[] keyBytes, in byte[] metadata);
    String getKey(String alias);
    void removeKey(String alias);
    void setSnapshotCreatedPendingIntent(in PendingIntent intent);
+4 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ public class KeyChainSnapshotTest {
    private static final int USER_SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
    private static final String KEY_ALIAS = "steph";
    private static final byte[] KEY_MATERIAL = new byte[] { 3, 5, 7, 9, 1 };
    private static final byte[] KEY_METADATA = new byte[] { 5, 3, 11, 13 };
    private static final CertPath CERT_PATH = TestData.getThmCertPath();

    @Test
@@ -100,6 +101,7 @@ public class KeyChainSnapshotTest {
        WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
        assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
        assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
        assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata());
    }

    @Test
@@ -166,6 +168,7 @@ public class KeyChainSnapshotTest {
        WrappedApplicationKey wrappedApplicationKey = snapshot.getWrappedApplicationKeys().get(0);
        assertEquals(KEY_ALIAS, wrappedApplicationKey.getAlias());
        assertArrayEquals(KEY_MATERIAL, wrappedApplicationKey.getEncryptedKeyMaterial());
        assertArrayEquals(KEY_METADATA, wrappedApplicationKey.getMetadata());
    }

    private static KeyChainSnapshot createKeyChainSnapshot() throws Exception {
@@ -197,6 +200,7 @@ public class KeyChainSnapshotTest {
        return new WrappedApplicationKey.Builder()
                .setAlias(KEY_ALIAS)
                .setEncryptedKeyMaterial(KEY_MATERIAL)
                .setMetadata(KEY_METADATA)
                .build();
    }

Loading