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

Commit 6fc37331 authored by David Zeuthen's avatar David Zeuthen
Browse files

identity: Add multi-document presentation support.

This new PresentationSession interface enables an application to do a
multi-document presentation, something which isn't possible with the
existing API. As a practical example of this consider presenting both
your Mobile Driving License and your Vaccination Certificate in a single
transaction.

Also update the documentation for IdentityCredential to clarify that
the same AuthKey is used for multiple getEntries() calls on the same
credential.

Also deprecate existing IdentityCredential.getEntries() method and
related methods and classes.

Bug: 197965513
Test: New CTS tests and new screen in CtsVerifier
Change-Id: I74534969143882552407917a82f44d43da12711c
parent 0892c27c
Loading
Loading
Loading
Loading
+80 −25
Original line number Diff line number Diff line
@@ -17979,10 +17979,12 @@ package android.hardware.biometrics {
    ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
    ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
    ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
    ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
    method public javax.crypto.Cipher getCipher();
    method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
    method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
    method public javax.crypto.Mac getMac();
    method @Nullable public android.security.identity.PresentationSession getPresentationSession();
    method public java.security.Signature getSignature();
  }
@@ -37500,6 +37502,51 @@ package android.security.identity {
    ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
  }
  public class CredentialDataRequest {
    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getDeviceSignedEntriesToRequest();
    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getIssuerSignedEntriesToRequest();
    method @Nullable public byte[] getReaderSignature();
    method @Nullable public byte[] getRequestMessage();
    method public boolean isAllowUsingExhaustedKeys();
    method public boolean isAllowUsingExpiredKeys();
    method public boolean isIncrementUseCount();
  }
  public static final class CredentialDataRequest.Builder {
    ctor public CredentialDataRequest.Builder();
    method @NonNull public android.security.identity.CredentialDataRequest build();
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExhaustedKeys(boolean);
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExpiredKeys(boolean);
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setDeviceSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIncrementUseCount(boolean);
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIssuerSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setReaderSignature(@NonNull byte[]);
    method @NonNull public android.security.identity.CredentialDataRequest.Builder setRequestMessage(@NonNull byte[]);
  }
  public abstract class CredentialDataResult {
    method @Nullable public abstract byte[] getDeviceMac();
    method @NonNull public abstract byte[] getDeviceNameSpaces();
    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
    method @NonNull public abstract byte[] getStaticAuthenticationData();
  }
  public static interface CredentialDataResult.Entries {
    method @Nullable public byte[] getEntry(@NonNull String, @NonNull String);
    method @NonNull public java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
    method @NonNull public java.util.Collection<java.lang.String> getNamespaces();
    method @NonNull public java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
    method public int getStatus(@NonNull String, @NonNull String);
    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
    field public static final int STATUS_OK = 0; // 0x0
    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
  }
  public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
    ctor public DocTypeNotSupportedException(@NonNull String);
    ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -37511,19 +37558,19 @@ package android.security.identity {
  }
  public abstract class IdentityCredential {
    method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
    method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
    method @Deprecated @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
    method @Deprecated @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
    method @NonNull public byte[] delete(@NonNull byte[]);
    method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
    method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
    method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
    method @NonNull public abstract int[] getAuthenticationDataUsageCount();
    method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
    method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
    method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
    method @NonNull public byte[] proveOwnership(@NonNull byte[]);
    method public abstract void setAllowUsingExhaustedKeys(boolean);
    method public void setAllowUsingExpiredKeys(boolean);
    method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
    method @Deprecated public void setAllowUsingExpiredKeys(boolean);
    method public abstract void setAvailableAuthenticationKeys(int, int);
    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
    method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
    method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
    method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
    method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
@@ -37536,6 +37583,7 @@ package android.security.identity {
  public abstract class IdentityCredentialStore {
    method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
    method @NonNull public android.security.identity.PresentationSession createPresentationSession(int) throws android.security.identity.CipherSuiteNotSupportedException;
    method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
    method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
    method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
@@ -37574,22 +37622,29 @@ package android.security.identity {
    method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
  }
  public abstract class ResultData {
    method @NonNull public abstract byte[] getAuthenticatedData();
    method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
    method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
    method @Nullable public abstract byte[] getMessageAuthenticationCode();
    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
    method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
    method @NonNull public abstract byte[] getStaticAuthenticationData();
    method public abstract int getStatus(@NonNull String, @NonNull String);
    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
    field public static final int STATUS_OK = 0; // 0x0
    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
  public abstract class PresentationSession {
    method @Nullable public abstract android.security.identity.CredentialDataResult getCredentialData(@NonNull String, @NonNull android.security.identity.CredentialDataRequest) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException;
    method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
    method public abstract void setSessionTranscript(@NonNull byte[]);
  }
  @Deprecated public abstract class ResultData {
    method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
    method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
    method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
    method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
    method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
    method @Deprecated public abstract int getStatus(@NonNull String, @NonNull String);
    field @Deprecated public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
    field @Deprecated public static final int STATUS_NOT_REQUESTED = 2; // 0x2
    field @Deprecated public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
    field @Deprecated public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
    field @Deprecated public static final int STATUS_OK = 0; // 0x0
    field @Deprecated public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
    field @Deprecated public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
  }
  public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
+2 −0
Original line number Diff line number Diff line
@@ -2485,6 +2485,8 @@ public abstract class PackageManager {
     * API shipped in Android 11.
     * <li><code>202101</code>: corresponds to the features included in the Identity Credential
     * API shipped in Android 12.
     * <li><code>202201</code>: corresponds to the features included in the Identity Credential
     * API shipped in Android 13.
     * </ul>
     */
    @SdkConstant(SdkConstantType.FEATURE)
+24 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
import android.security.identity.PresentationSession;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -653,8 +654,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
    /**
     * A wrapper class for the cryptographic operations supported by BiometricPrompt.
     *
     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
     * {@link IdentityCredential}.
     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
     * {@link IdentityCredential}, and {@link PresentationSession}.
     *
     * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
     * time-based. This is specified during key creation via the timeout parameter of the
@@ -684,10 +685,21 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
            super(mac);
        }

        /**
         * Create from a {@link IdentityCredential} object.
         *
         * @param credential a {@link IdentityCredential} object.
         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
         */
        @Deprecated
        public CryptoObject(@NonNull IdentityCredential credential) {
            super(credential);
        }

        public CryptoObject(@NonNull PresentationSession session) {
            super(session);
        }

        /**
         * Get {@link Signature} object.
         * @return {@link Signature} object or null if this doesn't contain one.
@@ -715,10 +727,20 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
        /**
         * Get {@link IdentityCredential} object.
         * @return {@link IdentityCredential} object or null if this doesn't contain one.
         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
         */
        @Deprecated
        public @Nullable IdentityCredential getIdentityCredential() {
            return super.getIdentityCredential();
        }

        /**
         * Get {@link PresentationSession} object.
         * @return {@link PresentationSession} object or null if this doesn't contain one.
         */
        public @Nullable PresentationSession getPresentationSession() {
            return super.getPresentationSession();
        }
    }

    /**
+26 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.hardware.biometrics;

import android.annotation.NonNull;
import android.security.identity.IdentityCredential;
import android.security.identity.PresentationSession;
import android.security.keystore2.AndroidKeyStoreProvider;

import java.security.Signature;
@@ -27,8 +28,8 @@ import javax.crypto.Mac;

/**
 * A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
 * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
 * {@link IdentityCredential} objects.
 * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
 * {@link IdentityCredential}, and {@link PresentationSession} objects.
 * @hide
 */
public class CryptoObject {
@@ -46,10 +47,21 @@ public class CryptoObject {
        mCrypto = mac;
    }

    /**
     * Create from a {@link IdentityCredential} object.
     *
     * @param credential a {@link IdentityCredential} object.
     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
     */
    @Deprecated
    public CryptoObject(@NonNull IdentityCredential credential) {
        mCrypto = credential;
    }

    public CryptoObject(@NonNull PresentationSession session) {
        mCrypto = session;
    }

    /**
     * Get {@link Signature} object.
     * @return {@link Signature} object or null if this doesn't contain one.
@@ -77,11 +89,21 @@ public class CryptoObject {
    /**
     * Get {@link IdentityCredential} object.
     * @return {@link IdentityCredential} object or null if this doesn't contain one.
     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
     */
    @Deprecated
    public IdentityCredential getIdentityCredential() {
        return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
    }

    /**
     * Get {@link PresentationSession} object.
     * @return {@link PresentationSession} object or null if this doesn't contain one.
     */
    public PresentationSession getPresentationSession() {
        return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
    }

    /**
     * @hide
     * @return the opId associated with this object or 0 if none
@@ -91,6 +113,8 @@ public class CryptoObject {
            return 0;
        } else if (mCrypto instanceof IdentityCredential) {
            return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
        } else if (mCrypto instanceof PresentationSession) {
            return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
        }
        return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
    }
+12 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.identity.IdentityCredential;
import android.security.identity.PresentationSession;
import android.util.Slog;
import android.view.Surface;

@@ -264,10 +265,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
         * Get {@link IdentityCredential} object.
         * @return {@link IdentityCredential} object or null if this doesn't contain one.
         * @hide
         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
         */
        @Deprecated
        public IdentityCredential getIdentityCredential() {
            return super.getIdentityCredential();
        }

        /**
         * Get {@link PresentationSession} object.
         * @return {@link PresentationSession} object or null if this doesn't contain one.
         * @hide
         */
        public PresentationSession getPresentationSession() {
            return super.getPresentationSession();
        }
    }

    /**
Loading