Merge "identity: Add multi-document presentation support."
This commit is contained in:
@@ -17980,10 +17980,12 @@ package android.hardware.biometrics {
|
|||||||
ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
|
ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
|
||||||
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
|
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
|
||||||
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
|
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 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 public javax.crypto.Mac getMac();
|
||||||
|
method @Nullable public android.security.identity.PresentationSession getPresentationSession();
|
||||||
method public java.security.Signature getSignature();
|
method public java.security.Signature getSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37550,6 +37552,51 @@ package android.security.identity {
|
|||||||
ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
|
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 {
|
public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
|
||||||
ctor public DocTypeNotSupportedException(@NonNull String);
|
ctor public DocTypeNotSupportedException(@NonNull String);
|
||||||
ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
|
ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
|
||||||
@@ -37561,19 +37608,19 @@ package android.security.identity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract class IdentityCredential {
|
public abstract class IdentityCredential {
|
||||||
method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
|
method @Deprecated @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 byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
|
||||||
method @NonNull public byte[] delete(@NonNull byte[]);
|
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 java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
|
||||||
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
|
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
|
||||||
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
|
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 @NonNull public byte[] proveOwnership(@NonNull byte[]);
|
||||||
method public abstract void setAllowUsingExhaustedKeys(boolean);
|
method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
|
||||||
method public void setAllowUsingExpiredKeys(boolean);
|
method @Deprecated public void setAllowUsingExpiredKeys(boolean);
|
||||||
method public abstract void setAvailableAuthenticationKeys(int, int);
|
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 @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 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);
|
method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
|
||||||
@@ -37586,6 +37633,7 @@ package android.security.identity {
|
|||||||
|
|
||||||
public abstract class IdentityCredentialStore {
|
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 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 @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 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);
|
method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
|
||||||
@@ -37624,22 +37672,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[]);
|
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 {
|
public abstract class PresentationSession {
|
||||||
method @NonNull public abstract byte[] getAuthenticatedData();
|
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 @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
|
method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
|
||||||
method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
|
method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
|
||||||
method @Nullable public abstract byte[] getMessageAuthenticationCode();
|
method public abstract void setSessionTranscript(@NonNull byte[]);
|
||||||
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();
|
@Deprecated public abstract class ResultData {
|
||||||
method public abstract int getStatus(@NonNull String, @NonNull String);
|
method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
|
||||||
field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
|
method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
|
||||||
field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
|
method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
|
||||||
field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
|
method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
|
||||||
field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
|
method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
|
||||||
field public static final int STATUS_OK = 0; // 0x0
|
method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
|
||||||
field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
|
method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
|
||||||
field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
|
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 {
|
public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
|
||||||
|
|||||||
@@ -2485,6 +2485,8 @@ public abstract class PackageManager {
|
|||||||
* API shipped in Android 11.
|
* API shipped in Android 11.
|
||||||
* <li><code>202101</code>: corresponds to the features included in the Identity Credential
|
* <li><code>202101</code>: corresponds to the features included in the Identity Credential
|
||||||
* API shipped in Android 12.
|
* API shipped in Android 12.
|
||||||
|
* <li><code>202201</code>: corresponds to the features included in the Identity Credential
|
||||||
|
* API shipped in Android 13.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@SdkConstant(SdkConstantType.FEATURE)
|
@SdkConstant(SdkConstantType.FEATURE)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import android.os.Parcel;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.security.identity.IdentityCredential;
|
import android.security.identity.IdentityCredential;
|
||||||
|
import android.security.identity.PresentationSession;
|
||||||
import android.security.keystore.KeyProperties;
|
import android.security.keystore.KeyProperties;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -653,8 +654,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
|
|||||||
/**
|
/**
|
||||||
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
|
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
|
||||||
*
|
*
|
||||||
* <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
|
* <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
|
||||||
* {@link IdentityCredential}.
|
* {@link IdentityCredential}, and {@link PresentationSession}.
|
||||||
*
|
*
|
||||||
* <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
|
* <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
|
* 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);
|
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) {
|
public CryptoObject(@NonNull IdentityCredential credential) {
|
||||||
super(credential);
|
super(credential);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CryptoObject(@NonNull PresentationSession session) {
|
||||||
|
super(session);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get {@link Signature} object.
|
* Get {@link Signature} object.
|
||||||
* @return {@link Signature} object or null if this doesn't contain one.
|
* @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.
|
* Get {@link IdentityCredential} object.
|
||||||
* @return {@link IdentityCredential} object or null if this doesn't contain one.
|
* @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() {
|
public @Nullable IdentityCredential getIdentityCredential() {
|
||||||
return super.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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
|
|||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.security.identity.IdentityCredential;
|
import android.security.identity.IdentityCredential;
|
||||||
|
import android.security.identity.PresentationSession;
|
||||||
import android.security.keystore2.AndroidKeyStoreProvider;
|
import android.security.keystore2.AndroidKeyStoreProvider;
|
||||||
|
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
@@ -27,8 +28,8 @@ import javax.crypto.Mac;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
|
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
|
||||||
* Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
|
* Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
|
||||||
* {@link IdentityCredential} objects.
|
* {@link IdentityCredential}, and {@link PresentationSession} objects.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class CryptoObject {
|
public class CryptoObject {
|
||||||
@@ -46,10 +47,21 @@ public class CryptoObject {
|
|||||||
mCrypto = mac;
|
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) {
|
public CryptoObject(@NonNull IdentityCredential credential) {
|
||||||
mCrypto = credential;
|
mCrypto = credential;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CryptoObject(@NonNull PresentationSession session) {
|
||||||
|
mCrypto = session;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get {@link Signature} object.
|
* Get {@link Signature} object.
|
||||||
* @return {@link Signature} object or null if this doesn't contain one.
|
* @return {@link Signature} object or null if this doesn't contain one.
|
||||||
@@ -77,11 +89,21 @@ public class CryptoObject {
|
|||||||
/**
|
/**
|
||||||
* Get {@link IdentityCredential} object.
|
* Get {@link IdentityCredential} object.
|
||||||
* @return {@link IdentityCredential} object or null if this doesn't contain one.
|
* @return {@link IdentityCredential} object or null if this doesn't contain one.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public IdentityCredential getIdentityCredential() {
|
public IdentityCredential getIdentityCredential() {
|
||||||
return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
|
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
|
* @hide
|
||||||
* @return the opId associated with this object or 0 if none
|
* @return the opId associated with this object or 0 if none
|
||||||
@@ -91,6 +113,8 @@ public class CryptoObject {
|
|||||||
return 0;
|
return 0;
|
||||||
} else if (mCrypto instanceof IdentityCredential) {
|
} else if (mCrypto instanceof IdentityCredential) {
|
||||||
return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
|
return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
|
||||||
|
} else if (mCrypto instanceof PresentationSession) {
|
||||||
|
return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
|
||||||
}
|
}
|
||||||
return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
|
return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import android.os.PowerManager;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.security.identity.IdentityCredential;
|
import android.security.identity.IdentityCredential;
|
||||||
|
import android.security.identity.PresentationSession;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
@@ -264,10 +265,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
|
|||||||
* Get {@link IdentityCredential} object.
|
* Get {@link IdentityCredential} object.
|
||||||
* @return {@link IdentityCredential} object or null if this doesn't contain one.
|
* @return {@link IdentityCredential} object or null if this doesn't contain one.
|
||||||
* @hide
|
* @hide
|
||||||
|
* @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public IdentityCredential getIdentityCredential() {
|
public IdentityCredential getIdentityCredential() {
|
||||||
return super.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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.identity;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing a request for credential data.
|
||||||
|
*/
|
||||||
|
public class CredentialDataRequest {
|
||||||
|
CredentialDataRequest() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the device-signed entries to request.
|
||||||
|
*
|
||||||
|
* @return the device-signed entries to request.
|
||||||
|
*/
|
||||||
|
public @NonNull Map<String, Collection<String>> getDeviceSignedEntriesToRequest() {
|
||||||
|
return mDeviceSignedEntriesToRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the issuer-signed entries to request.
|
||||||
|
*
|
||||||
|
* @return the issuer-signed entries to request.
|
||||||
|
*/
|
||||||
|
public @NonNull Map<String, Collection<String>> getIssuerSignedEntriesToRequest() {
|
||||||
|
return mIssuerSignedEntriesToRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether to allow using an authentication key which use count has been exceeded.
|
||||||
|
*
|
||||||
|
* <p>By default this is set to true.
|
||||||
|
*
|
||||||
|
* @return whether to allow using an authentication key which use
|
||||||
|
* count has been exceeded if no other key is available.
|
||||||
|
*/
|
||||||
|
public boolean isAllowUsingExhaustedKeys() {
|
||||||
|
return mAllowUsingExhaustedKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether to allow using an authentication key which is expired.
|
||||||
|
*
|
||||||
|
* <p>By default this is set to false.
|
||||||
|
*
|
||||||
|
* @return whether to allow using an authentication key which is
|
||||||
|
* expired if no other key is available.
|
||||||
|
*/
|
||||||
|
public boolean isAllowUsingExpiredKeys() {
|
||||||
|
return mAllowUsingExpiredKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether to increment the use-count for the authentication key used.
|
||||||
|
*
|
||||||
|
* <p>By default this is set to true.
|
||||||
|
*
|
||||||
|
* @return whether to increment the use count of the authentication key used.
|
||||||
|
*/
|
||||||
|
public boolean isIncrementUseCount() {
|
||||||
|
return mIncrementUseCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the request message CBOR.
|
||||||
|
*
|
||||||
|
* <p>This data structure is described in the documentation for the
|
||||||
|
* {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
|
||||||
|
*
|
||||||
|
* @return the request message CBOR as described above.
|
||||||
|
*/
|
||||||
|
public @Nullable byte[] getRequestMessage() {
|
||||||
|
return mRequestMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the reader signature.
|
||||||
|
*
|
||||||
|
* <p>This data structure is described in the documentation for the
|
||||||
|
* {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
|
||||||
|
*
|
||||||
|
* @return a {@code COSE_Sign1} structure as described above.
|
||||||
|
*/
|
||||||
|
public @Nullable byte[] getReaderSignature() {
|
||||||
|
return mReaderSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Collection<String>> mDeviceSignedEntriesToRequest = new LinkedHashMap<>();
|
||||||
|
Map<String, Collection<String>> mIssuerSignedEntriesToRequest = new LinkedHashMap<>();
|
||||||
|
boolean mAllowUsingExhaustedKeys = true;
|
||||||
|
boolean mAllowUsingExpiredKeys = false;
|
||||||
|
boolean mIncrementUseCount = true;
|
||||||
|
byte[] mRequestMessage = null;
|
||||||
|
byte[] mReaderSignature = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link CredentialDataRequest}.
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
private CredentialDataRequest mData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new builder.
|
||||||
|
*/
|
||||||
|
public Builder() {
|
||||||
|
mData = new CredentialDataRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the device-signed entries to request.
|
||||||
|
*
|
||||||
|
* @param entriesToRequest the device-signed entries to request.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setDeviceSignedEntriesToRequest(
|
||||||
|
@NonNull Map<String, Collection<String>> entriesToRequest) {
|
||||||
|
mData.mDeviceSignedEntriesToRequest = entriesToRequest;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the issuer-signed entries to request.
|
||||||
|
*
|
||||||
|
* @param entriesToRequest the issuer-signed entries to request.
|
||||||
|
* @return the builder.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setIssuerSignedEntriesToRequest(
|
||||||
|
@NonNull Map<String, Collection<String>> entriesToRequest) {
|
||||||
|
mData.mIssuerSignedEntriesToRequest = entriesToRequest;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to allow using an authentication key which use count has been exceeded.
|
||||||
|
*
|
||||||
|
* By default this is set to true.
|
||||||
|
*
|
||||||
|
* @param allowUsingExhaustedKeys whether to allow using an authentication key which use
|
||||||
|
* count has been exceeded if no other key is available.
|
||||||
|
* @return the builder.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
|
||||||
|
mData.mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to allow using an authentication key which is expired.
|
||||||
|
*
|
||||||
|
* By default this is set to false.
|
||||||
|
*
|
||||||
|
* @param allowUsingExpiredKeys whether to allow using an authentication key which is
|
||||||
|
* expired if no other key is available.
|
||||||
|
* @return the builder.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
|
||||||
|
mData.mAllowUsingExpiredKeys = allowUsingExpiredKeys;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to increment the use-count for the authentication key used.
|
||||||
|
*
|
||||||
|
* By default this is set to true.
|
||||||
|
*
|
||||||
|
* @param incrementUseCount whether to increment the use count of the authentication
|
||||||
|
* key used.
|
||||||
|
* @return the builder.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setIncrementUseCount(boolean incrementUseCount) {
|
||||||
|
mData.mIncrementUseCount = incrementUseCount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the request message CBOR.
|
||||||
|
*
|
||||||
|
* <p>This data structure is described in the documentation for the
|
||||||
|
* {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
|
||||||
|
*
|
||||||
|
* @param requestMessage the request message CBOR as described above.
|
||||||
|
* @return the builder.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setRequestMessage(@NonNull byte[] requestMessage) {
|
||||||
|
mData.mRequestMessage = requestMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the reader signature.
|
||||||
|
*
|
||||||
|
* <p>This data structure is described in the documentation for the
|
||||||
|
* {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
|
||||||
|
*
|
||||||
|
* @param readerSignature a {@code COSE_Sign1} structure as described above.
|
||||||
|
* @return the builder.
|
||||||
|
*/
|
||||||
|
public @NonNull Builder setReaderSignature(@NonNull byte[] readerSignature) {
|
||||||
|
mData.mReaderSignature = readerSignature;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes building a {@link CredentialDataRequest}.
|
||||||
|
*
|
||||||
|
* @return the {@link CredentialDataRequest} object.
|
||||||
|
*/
|
||||||
|
public @NonNull CredentialDataRequest build() {
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.identity;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that contains the result of retrieving data from a credential. This is used to return
|
||||||
|
* data requested in a {@link PresentationSession}.
|
||||||
|
*/
|
||||||
|
public abstract class CredentialDataResult {
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
protected CredentialDataResult() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CBOR structure containing the retrieved device-signed data.
|
||||||
|
*
|
||||||
|
* <p>This structure - along with the session transcript - may be cryptographically
|
||||||
|
* authenticated to prove to the reader that the data is from a trusted credential and
|
||||||
|
* {@link #getDeviceMac()} can be used to get a MAC.
|
||||||
|
*
|
||||||
|
* <p>The CBOR structure which is cryptographically authenticated is the
|
||||||
|
* {@code DeviceAuthenticationBytes} structure according to the following
|
||||||
|
* <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* DeviceAuthentication = [
|
||||||
|
* "DeviceAuthentication",
|
||||||
|
* SessionTranscript,
|
||||||
|
* DocType,
|
||||||
|
* DeviceNameSpacesBytes
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* DocType = tstr
|
||||||
|
* SessionTranscript = any
|
||||||
|
* DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
|
||||||
|
* DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>where
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* DeviceNameSpaces = {
|
||||||
|
* * NameSpace => DeviceSignedItems
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* DeviceSignedItems = {
|
||||||
|
* + DataItemName => DataItemValue
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* NameSpace = tstr
|
||||||
|
* DataItemName = tstr
|
||||||
|
* DataItemValue = any
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
|
||||||
|
* as defined above.
|
||||||
|
*
|
||||||
|
* @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
|
||||||
|
*/
|
||||||
|
public abstract @NonNull byte[] getDeviceNameSpaces();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
|
||||||
|
* specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
|
||||||
|
* is from a trusted credential.
|
||||||
|
*
|
||||||
|
* <p>The MAC proves to the reader that the data is from a trusted credential. This code is
|
||||||
|
* produced by using the key agreement and key derivation function from the ciphersuite
|
||||||
|
* with the authentication private key and the reader ephemeral public key to compute a
|
||||||
|
* shared message authentication code (MAC) key, then using the MAC function from the
|
||||||
|
* ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
|
||||||
|
* ISO/IEC 18013-5 for details of this operation.
|
||||||
|
*
|
||||||
|
* <p>If the session transcript or reader ephemeral key wasn't set on the {@link
|
||||||
|
* PresentationSession} used to obtain this data no message authencation code will be produced
|
||||||
|
* and this method will return {@code null}.
|
||||||
|
*
|
||||||
|
* @return A COSE_Mac0 structure with the message authentication code as described above
|
||||||
|
* or {@code null} if the conditions specified above are not met.
|
||||||
|
*/
|
||||||
|
public abstract @Nullable byte[] getDeviceMac();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the static authentication data associated with the dynamic authentication
|
||||||
|
* key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
|
||||||
|
*
|
||||||
|
* @return The static authentication data associated with dynamic authentication key used to
|
||||||
|
* MAC the data.
|
||||||
|
*/
|
||||||
|
public abstract @NonNull byte[] getStaticAuthenticationData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the device-signed entries that was returned.
|
||||||
|
*
|
||||||
|
* @return an object to examine the entries returned.
|
||||||
|
*/
|
||||||
|
public abstract @NonNull Entries getDeviceSignedEntries();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the issuer-signed entries that was returned.
|
||||||
|
*
|
||||||
|
* @return an object to examine the entries returned.
|
||||||
|
*/
|
||||||
|
public abstract @NonNull Entries getIssuerSignedEntries();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for representing data elements returned.
|
||||||
|
*/
|
||||||
|
public interface Entries {
|
||||||
|
/** Value was successfully retrieved. */
|
||||||
|
int STATUS_OK = 0;
|
||||||
|
|
||||||
|
/** The entry does not exist. */
|
||||||
|
int STATUS_NO_SUCH_ENTRY = 1;
|
||||||
|
|
||||||
|
/** The entry was not requested. */
|
||||||
|
int STATUS_NOT_REQUESTED = 2;
|
||||||
|
|
||||||
|
/** The entry wasn't in the request message. */
|
||||||
|
int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
|
||||||
|
|
||||||
|
/** The entry was not retrieved because user authentication failed. */
|
||||||
|
int STATUS_USER_AUTHENTICATION_FAILED = 4;
|
||||||
|
|
||||||
|
/** The entry was not retrieved because reader authentication failed. */
|
||||||
|
int STATUS_READER_AUTHENTICATION_FAILED = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry was not retrieved because it was configured without any access
|
||||||
|
* control profile.
|
||||||
|
*/
|
||||||
|
int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the names of namespaces with retrieved entries.
|
||||||
|
*
|
||||||
|
* @return collection of name of namespaces containing retrieved entries. May be empty if no
|
||||||
|
* data was retrieved.
|
||||||
|
*/
|
||||||
|
@NonNull Collection<String> getNamespaces();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the names of all requested entries in a name space.
|
||||||
|
*
|
||||||
|
* <p>This includes the name of entries that wasn't successfully retrieved.
|
||||||
|
*
|
||||||
|
* @param namespaceName the namespace name to get entries for.
|
||||||
|
* @return A collection of names for the given namespace or the empty collection if no
|
||||||
|
* entries was returned for the given name space.
|
||||||
|
*/
|
||||||
|
@NonNull Collection<String> getEntryNames(@NonNull String namespaceName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the names of all entries that was successfully retrieved from a name space.
|
||||||
|
*
|
||||||
|
* <p>This only return entries for which {@link #getStatus(String, String)} will return
|
||||||
|
* {@link #STATUS_OK}.
|
||||||
|
*
|
||||||
|
* @param namespaceName the namespace name to get entries for.
|
||||||
|
* @return The entries in the given namespace that were successfully rerieved or the
|
||||||
|
* empty collection if no entries was returned for the given name space.
|
||||||
|
*/
|
||||||
|
@NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the status of an entry.
|
||||||
|
*
|
||||||
|
* <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link
|
||||||
|
* #STATUS_NO_SUCH_ENTRY} if the given entry wasn't retrieved, {@link
|
||||||
|
* #STATUS_NOT_REQUESTED} if it wasn't requested, {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if
|
||||||
|
* the request message was set but the entry wasn't present in the request message, {@link
|
||||||
|
* #STATUS_USER_AUTHENTICATION_FAILED} if the value wasn't retrieved because the necessary
|
||||||
|
* user authentication wasn't performed, {@link #STATUS_READER_AUTHENTICATION_FAILED} if
|
||||||
|
* the supplied reader certificate chain didn't match the set of certificates the entry was
|
||||||
|
* provisioned with, or {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was
|
||||||
|
* configured without any access control profiles.
|
||||||
|
*
|
||||||
|
* @param namespaceName the namespace name of the entry.
|
||||||
|
* @param name the name of the entry to get the value for.
|
||||||
|
* @return the status indicating whether the value was retrieved and if not, why.
|
||||||
|
*/
|
||||||
|
@Status int getStatus(@NonNull String namespaceName, @NonNull String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the raw CBOR data for the value of an entry.
|
||||||
|
*
|
||||||
|
* <p>This should only be called on an entry for which the {@link #getStatus(String,
|
||||||
|
* String)} method returns {@link #STATUS_OK}.
|
||||||
|
*
|
||||||
|
* @param namespaceName the namespace name of the entry.
|
||||||
|
* @param name the name of the entry to get the value for.
|
||||||
|
* @return the raw CBOR data or {@code null} if no entry with the given name exists.
|
||||||
|
*/
|
||||||
|
@Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the entry status.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@Retention(SOURCE)
|
||||||
|
@IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED,
|
||||||
|
STATUS_NOT_IN_REQUEST_MESSAGE, STATUS_USER_AUTHENTICATION_FAILED,
|
||||||
|
STATUS_READER_AUTHENTICATION_FAILED, STATUS_NO_ACCESS_CONTROL_PROFILES})
|
||||||
|
@interface Status {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.identity;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
class CredstoreCredentialDataResult extends CredentialDataResult {
|
||||||
|
|
||||||
|
ResultData mDeviceSignedResult;
|
||||||
|
ResultData mIssuerSignedResult;
|
||||||
|
CredstoreEntries mDeviceSignedEntries;
|
||||||
|
CredstoreEntries mIssuerSignedEntries;
|
||||||
|
|
||||||
|
CredstoreCredentialDataResult(ResultData deviceSignedResult, ResultData issuerSignedResult) {
|
||||||
|
mDeviceSignedResult = deviceSignedResult;
|
||||||
|
mIssuerSignedResult = issuerSignedResult;
|
||||||
|
mDeviceSignedEntries = new CredstoreEntries(deviceSignedResult);
|
||||||
|
mIssuerSignedEntries = new CredstoreEntries(issuerSignedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull byte[] getDeviceNameSpaces() {
|
||||||
|
return mDeviceSignedResult.getAuthenticatedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable byte[] getDeviceMac() {
|
||||||
|
return mDeviceSignedResult.getMessageAuthenticationCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull byte[] getStaticAuthenticationData() {
|
||||||
|
return mDeviceSignedResult.getStaticAuthenticationData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull CredentialDataResult.Entries getDeviceSignedEntries() {
|
||||||
|
return mDeviceSignedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull CredentialDataResult.Entries getIssuerSignedEntries() {
|
||||||
|
return mIssuerSignedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CredstoreEntries implements CredentialDataResult.Entries {
|
||||||
|
ResultData mResultData;
|
||||||
|
|
||||||
|
CredstoreEntries(ResultData resultData) {
|
||||||
|
mResultData = resultData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Collection<String> getNamespaces() {
|
||||||
|
return mResultData.getNamespaces();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Collection<String> getEntryNames(@NonNull String namespaceName) {
|
||||||
|
Collection<String> ret = mResultData.getEntryNames(namespaceName);
|
||||||
|
if (ret == null) {
|
||||||
|
ret = new LinkedList<String>();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
|
||||||
|
Collection<String> ret = mResultData.getRetrievedEntryNames(namespaceName);
|
||||||
|
if (ret == null) {
|
||||||
|
ret = new LinkedList<String>();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Status
|
||||||
|
public int getStatus(@NonNull String namespaceName, @NonNull String name) {
|
||||||
|
return mResultData.getStatus(namespaceName, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
|
||||||
|
return mResultData.getEntry(namespaceName, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -58,14 +58,17 @@ class CredstoreIdentityCredential extends IdentityCredential {
|
|||||||
private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
|
private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ICredential mBinder;
|
private ICredential mBinder;
|
||||||
|
private CredstorePresentationSession mSession;
|
||||||
|
|
||||||
CredstoreIdentityCredential(Context context, String credentialName,
|
CredstoreIdentityCredential(Context context, String credentialName,
|
||||||
@IdentityCredentialStore.Ciphersuite int cipherSuite,
|
@IdentityCredentialStore.Ciphersuite int cipherSuite,
|
||||||
ICredential binder) {
|
ICredential binder,
|
||||||
|
@Nullable CredstorePresentationSession session) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mCredentialName = credentialName;
|
mCredentialName = credentialName;
|
||||||
mCipherSuite = cipherSuite;
|
mCipherSuite = cipherSuite;
|
||||||
mBinder = binder;
|
mBinder = binder;
|
||||||
|
mSession = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeyPair mEphemeralKeyPair = null;
|
private KeyPair mEphemeralKeyPair = null;
|
||||||
@@ -239,6 +242,7 @@ class CredstoreIdentityCredential extends IdentityCredential {
|
|||||||
|
|
||||||
private boolean mAllowUsingExhaustedKeys = true;
|
private boolean mAllowUsingExhaustedKeys = true;
|
||||||
private boolean mAllowUsingExpiredKeys = false;
|
private boolean mAllowUsingExpiredKeys = false;
|
||||||
|
private boolean mIncrementKeyUsageCount = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
|
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
|
||||||
@@ -250,6 +254,11 @@ class CredstoreIdentityCredential extends IdentityCredential {
|
|||||||
mAllowUsingExpiredKeys = allowUsingExpiredKeys;
|
mAllowUsingExpiredKeys = allowUsingExpiredKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
|
||||||
|
mIncrementKeyUsageCount = incrementKeyUsageCount;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean mOperationHandleSet = false;
|
private boolean mOperationHandleSet = false;
|
||||||
private long mOperationHandle = 0;
|
private long mOperationHandle = 0;
|
||||||
|
|
||||||
@@ -264,7 +273,8 @@ class CredstoreIdentityCredential extends IdentityCredential {
|
|||||||
if (!mOperationHandleSet) {
|
if (!mOperationHandleSet) {
|
||||||
try {
|
try {
|
||||||
mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
|
mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
|
||||||
mAllowUsingExpiredKeys);
|
mAllowUsingExpiredKeys,
|
||||||
|
mIncrementKeyUsageCount);
|
||||||
mOperationHandleSet = true;
|
mOperationHandleSet = true;
|
||||||
} catch (android.os.RemoteException e) {
|
} catch (android.os.RemoteException e) {
|
||||||
throw new RuntimeException("Unexpected RemoteException ", e);
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
@@ -315,7 +325,8 @@ class CredstoreIdentityCredential extends IdentityCredential {
|
|||||||
sessionTranscript != null ? sessionTranscript : new byte[0],
|
sessionTranscript != null ? sessionTranscript : new byte[0],
|
||||||
readerSignature != null ? readerSignature : new byte[0],
|
readerSignature != null ? readerSignature : new byte[0],
|
||||||
mAllowUsingExhaustedKeys,
|
mAllowUsingExhaustedKeys,
|
||||||
mAllowUsingExpiredKeys);
|
mAllowUsingExpiredKeys,
|
||||||
|
mIncrementKeyUsageCount);
|
||||||
} catch (android.os.RemoteException e) {
|
} catch (android.os.RemoteException e) {
|
||||||
throw new RuntimeException("Unexpected RemoteException ", e);
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
} catch (android.os.ServiceSpecificException e) {
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore {
|
|||||||
ICredential credstoreCredential;
|
ICredential credstoreCredential;
|
||||||
credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
|
credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
|
||||||
return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
|
return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
|
||||||
credstoreCredential);
|
credstoreCredential,
|
||||||
|
null);
|
||||||
} catch (android.os.RemoteException e) {
|
} catch (android.os.RemoteException e) {
|
||||||
throw new RuntimeException("Unexpected RemoteException ", e);
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
} catch (android.os.ServiceSpecificException e) {
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
@@ -162,4 +163,23 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore {
|
|||||||
+ e.errorCode, e);
|
+ e.errorCode, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
|
||||||
|
throws CipherSuiteNotSupportedException {
|
||||||
|
try {
|
||||||
|
ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
|
||||||
|
return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
|
||||||
|
} catch (android.os.RemoteException e) {
|
||||||
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
|
||||||
|
throw new CipherSuiteNotSupportedException(e.getMessage(), e);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unexpected ServiceSpecificException with code "
|
||||||
|
+ e.errorCode, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.identity;
|
||||||
|
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class CredstorePresentationSession extends PresentationSession {
|
||||||
|
private static final String TAG = "CredstorePresentationSession";
|
||||||
|
|
||||||
|
private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
|
||||||
|
private Context mContext;
|
||||||
|
private CredstoreIdentityCredentialStore mStore;
|
||||||
|
private ISession mBinder;
|
||||||
|
private Map<String, CredstoreIdentityCredential> mCredentialCache = new LinkedHashMap<>();
|
||||||
|
private KeyPair mEphemeralKeyPair = null;
|
||||||
|
private byte[] mSessionTranscript = null;
|
||||||
|
private boolean mOperationHandleSet = false;
|
||||||
|
private long mOperationHandle = 0;
|
||||||
|
|
||||||
|
CredstorePresentationSession(Context context,
|
||||||
|
@IdentityCredentialStore.Ciphersuite int cipherSuite,
|
||||||
|
CredstoreIdentityCredentialStore store,
|
||||||
|
ISession binder) {
|
||||||
|
mContext = context;
|
||||||
|
mCipherSuite = cipherSuite;
|
||||||
|
mStore = store;
|
||||||
|
mBinder = binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureEphemeralKeyPair() {
|
||||||
|
if (mEphemeralKeyPair != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// This PKCS#12 blob is generated in credstore, using BoringSSL.
|
||||||
|
//
|
||||||
|
// The main reason for this convoluted approach and not just sending the decomposed
|
||||||
|
// key-pair is that this would require directly using (device-side) BouncyCastle which
|
||||||
|
// is tricky due to various API hiding efforts. So instead we have credstore generate
|
||||||
|
// this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
|
||||||
|
// doesn't support not using encryption when building a PKCS#12 blob).
|
||||||
|
//
|
||||||
|
byte[] pkcs12 = mBinder.getEphemeralKeyPair();
|
||||||
|
String alias = "ephemeralKey";
|
||||||
|
char[] password = {};
|
||||||
|
|
||||||
|
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
|
||||||
|
ks.load(bais, password);
|
||||||
|
PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
|
||||||
|
|
||||||
|
Certificate cert = ks.getCertificate(alias);
|
||||||
|
PublicKey pubKey = cert.getPublicKey();
|
||||||
|
|
||||||
|
mEphemeralKeyPair = new KeyPair(pubKey, privKey);
|
||||||
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
throw new RuntimeException("Unexpected ServiceSpecificException with code "
|
||||||
|
+ e.errorCode, e);
|
||||||
|
} catch (android.os.RemoteException
|
||||||
|
| KeyStoreException
|
||||||
|
| CertificateException
|
||||||
|
| UnrecoverableKeyException
|
||||||
|
| NoSuchAlgorithmException
|
||||||
|
| IOException e) {
|
||||||
|
throw new RuntimeException("Unexpected exception ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull KeyPair getEphemeralKeyPair() {
|
||||||
|
ensureEphemeralKeyPair();
|
||||||
|
return mEphemeralKeyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
|
||||||
|
throws InvalidKeyException {
|
||||||
|
try {
|
||||||
|
byte[] uncompressedForm =
|
||||||
|
Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
|
||||||
|
mBinder.setReaderEphemeralPublicKey(uncompressedForm);
|
||||||
|
} catch (android.os.RemoteException e) {
|
||||||
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
throw new RuntimeException("Unexpected ServiceSpecificException with code "
|
||||||
|
+ e.errorCode, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSessionTranscript(@NonNull byte[] sessionTranscript) {
|
||||||
|
try {
|
||||||
|
mBinder.setSessionTranscript(sessionTranscript);
|
||||||
|
mSessionTranscript = sessionTranscript;
|
||||||
|
} catch (android.os.RemoteException e) {
|
||||||
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
throw new RuntimeException("Unexpected ServiceSpecificException with code "
|
||||||
|
+ e.errorCode, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable CredentialDataResult getCredentialData(@NonNull String credentialName,
|
||||||
|
@NonNull CredentialDataRequest request)
|
||||||
|
throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
|
||||||
|
InvalidRequestMessageException, EphemeralPublicKeyNotFoundException {
|
||||||
|
try {
|
||||||
|
// Cache the IdentityCredential to satisfy the property that AuthKey usage counts are
|
||||||
|
// incremented on only the _first_ getCredentialData() call.
|
||||||
|
//
|
||||||
|
CredstoreIdentityCredential credential = mCredentialCache.get(credentialName);
|
||||||
|
if (credential == null) {
|
||||||
|
ICredential credstoreCredential =
|
||||||
|
mBinder.getCredentialForPresentation(credentialName);
|
||||||
|
credential = new CredstoreIdentityCredential(mContext, credentialName,
|
||||||
|
mCipherSuite, credstoreCredential,
|
||||||
|
this);
|
||||||
|
mCredentialCache.put(credentialName, credential);
|
||||||
|
|
||||||
|
credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
|
||||||
|
credential.setAllowUsingExpiredKeys(request.isAllowUsingExpiredKeys());
|
||||||
|
credential.setIncrementKeyUsageCount(request.isIncrementUseCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultData deviceSignedResult = credential.getEntries(
|
||||||
|
request.getRequestMessage(),
|
||||||
|
request.getDeviceSignedEntriesToRequest(),
|
||||||
|
mSessionTranscript,
|
||||||
|
request.getReaderSignature());
|
||||||
|
|
||||||
|
// By design this second getEntries() call consumes the same auth-key.
|
||||||
|
|
||||||
|
ResultData issuerSignedResult = credential.getEntries(
|
||||||
|
request.getRequestMessage(),
|
||||||
|
request.getIssuerSignedEntriesToRequest(),
|
||||||
|
mSessionTranscript,
|
||||||
|
request.getReaderSignature());
|
||||||
|
|
||||||
|
return new CredstoreCredentialDataResult(deviceSignedResult, issuerSignedResult);
|
||||||
|
|
||||||
|
} catch (SessionTranscriptMismatchException e) {
|
||||||
|
throw new RuntimeException("Unexpected ", e);
|
||||||
|
} catch (android.os.RemoteException e) {
|
||||||
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unexpected ServiceSpecificException with code "
|
||||||
|
+ e.errorCode, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
|
||||||
|
* operation handle.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getCredstoreOperationHandle() {
|
||||||
|
if (!mOperationHandleSet) {
|
||||||
|
try {
|
||||||
|
mOperationHandle = mBinder.getAuthChallenge();
|
||||||
|
mOperationHandleSet = true;
|
||||||
|
} catch (android.os.RemoteException e) {
|
||||||
|
throw new RuntimeException("Unexpected RemoteException ", e);
|
||||||
|
} catch (android.os.ServiceSpecificException e) {
|
||||||
|
if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
|
||||||
|
// The NoAuthenticationKeyAvailableException will be thrown when
|
||||||
|
// the caller proceeds to call getEntries().
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Unexpected ServiceSpecificException with code "
|
||||||
|
+ e.errorCode, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mOperationHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -48,7 +48,9 @@ public abstract class IdentityCredential {
|
|||||||
* encryption".
|
* encryption".
|
||||||
*
|
*
|
||||||
* @return ephemeral key pair to use to establish a secure channel with a reader.
|
* @return ephemeral key pair to use to establish a secure channel with a reader.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public @NonNull abstract KeyPair createEphemeralKeyPair();
|
public @NonNull abstract KeyPair createEphemeralKeyPair();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +60,9 @@ public abstract class IdentityCredential {
|
|||||||
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
|
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
|
||||||
* establish a secure session.
|
* establish a secure session.
|
||||||
* @throws InvalidKeyException if the given key is invalid.
|
* @throws InvalidKeyException if the given key is invalid.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
|
public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
|
||||||
throws InvalidKeyException;
|
throws InvalidKeyException;
|
||||||
|
|
||||||
@@ -72,7 +76,10 @@ public abstract class IdentityCredential {
|
|||||||
*
|
*
|
||||||
* @param messagePlaintext unencrypted message to encrypt.
|
* @param messagePlaintext unencrypted message to encrypt.
|
||||||
* @return encrypted message.
|
* @return encrypted message.
|
||||||
|
* @deprecated Applications should use {@link PresentationSession} and
|
||||||
|
* implement encryption/decryption themselves.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
|
public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,7 +93,10 @@ public abstract class IdentityCredential {
|
|||||||
* @param messageCiphertext encrypted message to decrypt.
|
* @param messageCiphertext encrypted message to decrypt.
|
||||||
* @return decrypted message.
|
* @return decrypted message.
|
||||||
* @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
|
* @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
|
||||||
|
* @deprecated Applications should use {@link PresentationSession} and
|
||||||
|
* implement encryption/decryption themselves.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
|
public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
|
||||||
throws MessageDecryptionException;
|
throws MessageDecryptionException;
|
||||||
|
|
||||||
@@ -111,7 +121,9 @@ public abstract class IdentityCredential {
|
|||||||
*
|
*
|
||||||
* @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
|
* @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
|
||||||
* has been exceeded if no other key is available.
|
* has been exceeded if no other key is available.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
|
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,11 +140,35 @@ public abstract class IdentityCredential {
|
|||||||
*
|
*
|
||||||
* @param allowUsingExpiredKeys whether to allow using an authentication key which use count
|
* @param allowUsingExpiredKeys whether to allow using an authentication key which use count
|
||||||
* has been exceeded if no other key is available.
|
* has been exceeded if no other key is available.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
|
public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*
|
||||||
|
* Sets whether the usage count of an authentication key should be increased. This must be
|
||||||
|
* called prior to calling
|
||||||
|
* {@link #getEntries(byte[], Map, byte[], byte[])} or using a
|
||||||
|
* {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this object.
|
||||||
|
*
|
||||||
|
* <p>By default this is set to true.
|
||||||
|
*
|
||||||
|
* <p>This is only implemented in feature version 202201 or later. If not implemented, the call
|
||||||
|
* fails with {@link UnsupportedOperationException}. See
|
||||||
|
* {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
|
||||||
|
* feature versions.
|
||||||
|
*
|
||||||
|
* @param incrementKeyUsageCount whether the usage count of the key should be increased.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
|
*/
|
||||||
|
public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
|
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
|
||||||
* operation handle.
|
* operation handle.
|
||||||
@@ -149,15 +185,19 @@ public abstract class IdentityCredential {
|
|||||||
* by using the {@link ResultData#getStatus(String, String)} method on each of the requested
|
* by using the {@link ResultData#getStatus(String, String)} method on each of the requested
|
||||||
* entries.
|
* entries.
|
||||||
*
|
*
|
||||||
* <p>It is the responsibility of the calling application to know if authentication is needed
|
|
||||||
* and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
|
|
||||||
* authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
|
|
||||||
* references this object. If needed, this must be done before calling
|
|
||||||
* {@link #getEntries(byte[], Map, byte[], byte[])}.
|
|
||||||
*
|
|
||||||
* <p>It is permissible to call this method multiple times using the same instance but if this
|
* <p>It is permissible to call this method multiple times using the same instance but if this
|
||||||
* is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
|
* is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
|
||||||
* not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
|
* not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
|
||||||
|
* Additionally, if this is done the same auth-key will be used.
|
||||||
|
*
|
||||||
|
* <p>The application should not make any assumptions on whether user authentication is needed.
|
||||||
|
* Instead, the application should request the data elements values first and then examine
|
||||||
|
* the returned {@link ResultData}. If {@link ResultData#STATUS_USER_AUTHENTICATION_FAILED}
|
||||||
|
* is returned the application should get a
|
||||||
|
* {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
|
||||||
|
* object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
|
||||||
|
* authentication the application may call {@link #getEntries(byte[], Map, byte[], byte[])}
|
||||||
|
* again.
|
||||||
*
|
*
|
||||||
* <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
|
* <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
|
||||||
* from the verifier. The content can be defined in the way appropriate for the credential, but
|
* from the verifier. The content can be defined in the way appropriate for the credential, but
|
||||||
@@ -269,7 +309,9 @@ public abstract class IdentityCredential {
|
|||||||
* @throws InvalidRequestMessageException if the requestMessage is malformed.
|
* @throws InvalidRequestMessageException if the requestMessage is malformed.
|
||||||
* @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
|
* @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
|
||||||
* the session transcript.
|
* the session transcript.
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract @NonNull ResultData getEntries(
|
public abstract @NonNull ResultData getEntries(
|
||||||
@Nullable byte[] requestMessage,
|
@Nullable byte[] requestMessage,
|
||||||
@NonNull Map<String, Collection<String>> entriesToRequest,
|
@NonNull Map<String, Collection<String>> entriesToRequest,
|
||||||
|
|||||||
@@ -209,6 +209,25 @@ public abstract class IdentityCredentialStore {
|
|||||||
@Deprecated
|
@Deprecated
|
||||||
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
|
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new presentation session.
|
||||||
|
*
|
||||||
|
* <p>This method gets an object to be used for interaction with a remote verifier for
|
||||||
|
* presentation of one or more credentials.
|
||||||
|
*
|
||||||
|
* <p>This is only implemented in feature version 202201 or later. If not implemented, the call
|
||||||
|
* fails with {@link UnsupportedOperationException}. See
|
||||||
|
* {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
|
||||||
|
* feature versions.
|
||||||
|
*
|
||||||
|
* @param cipherSuite the cipher suite to use for communicating with the verifier.
|
||||||
|
* @return The presentation session.
|
||||||
|
*/
|
||||||
|
public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
|
||||||
|
throws CipherSuiteNotSupportedException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
|
@IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
|||||||
173
identity/java/android/security/identity/PresentationSession.java
Normal file
173
identity/java/android/security/identity/PresentationSession.java
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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.identity;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for presenting multiple documents to a remote verifier.
|
||||||
|
*
|
||||||
|
* Use {@link IdentityCredentialStore#createPresentationSession(int)} to create a {@link
|
||||||
|
* PresentationSession} instance.
|
||||||
|
*/
|
||||||
|
public abstract class PresentationSession {
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
protected PresentationSession() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ephemeral key pair to use to establish a secure channel with the verifier.
|
||||||
|
*
|
||||||
|
* <p>Applications should use this key-pair for the communications channel with the verifier
|
||||||
|
* using a protocol / cipher-suite appropriate for the application. One example of such a
|
||||||
|
* protocol is the one used for Mobile Driving Licenses, see ISO 18013-5.
|
||||||
|
*
|
||||||
|
* <p>The ephemeral key pair is tied to the {@link PresentationSession} instance so subsequent
|
||||||
|
* calls to this method will return the same key-pair.
|
||||||
|
*
|
||||||
|
* @return ephemeral key pair to use to establish a secure channel with a reader.
|
||||||
|
*/
|
||||||
|
public @NonNull abstract KeyPair getEphemeralKeyPair();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ephemeral public key provided by the verifier.
|
||||||
|
*
|
||||||
|
* <p>If called, this must be called before any calls to
|
||||||
|
* {@link #getCredentialData(String, CredentialDataRequest)}.
|
||||||
|
*
|
||||||
|
* <p>This method can only be called once per {@link PresentationSession} instance.
|
||||||
|
*
|
||||||
|
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
|
||||||
|
* establish a secure session.
|
||||||
|
* @throws InvalidKeyException if the given key is invalid.
|
||||||
|
*/
|
||||||
|
public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
|
||||||
|
throws InvalidKeyException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the session transcript.
|
||||||
|
*
|
||||||
|
* <p>If called, this must be called before any calls to
|
||||||
|
* {@link #getCredentialData(String, CredentialDataRequest)}.
|
||||||
|
*
|
||||||
|
* <p>The X and Y coordinates of the public part of the key-pair returned by {@link
|
||||||
|
* #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR. Each of
|
||||||
|
* these coordinates must appear encoded with the most significant bits first and use the exact
|
||||||
|
* amount of bits indicated by the key size of the ephemeral keys. For example, if the
|
||||||
|
* ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
|
||||||
|
* the most significant bits first must appear somewhere and ditto for the 32 bytes for the Y
|
||||||
|
* coordinate.
|
||||||
|
*
|
||||||
|
* <p>This method can only be called once per {@link PresentationSession} instance.
|
||||||
|
*
|
||||||
|
* @param sessionTranscript the session transcript.
|
||||||
|
*/
|
||||||
|
public abstract void setSessionTranscript(@NonNull byte[] sessionTranscript);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves data from a named credential in the current presentation session.
|
||||||
|
*
|
||||||
|
* <p>If an access control check fails for one of the requested entries or if the entry
|
||||||
|
* doesn't exist, the entry is simply not returned. The application can detect this
|
||||||
|
* by using the {@link CredentialDataResult.Entries#getStatus(String, String)} method on
|
||||||
|
* each of the requested entries.
|
||||||
|
*
|
||||||
|
* <p>The application should not make any assumptions on whether user authentication is needed.
|
||||||
|
* Instead, the application should request the data elements values first and then examine
|
||||||
|
* the returned {@link CredentialDataResult.Entries}. If
|
||||||
|
* {@link CredentialDataResult.Entries#STATUS_USER_AUTHENTICATION_FAILED} is returned the
|
||||||
|
* application should get a
|
||||||
|
* {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
|
||||||
|
* object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
|
||||||
|
* authentication the application may call
|
||||||
|
* {@link #getCredentialData(String, CredentialDataRequest)} again.
|
||||||
|
*
|
||||||
|
* <p>It is permissible to call this method multiple times using the same credential name.
|
||||||
|
* If this is done the same auth-key will be used.
|
||||||
|
*
|
||||||
|
* <p>If the reader signature is set in the request parameter (via the
|
||||||
|
* {@link CredentialDataRequest.Builder#setReaderSignature(byte[])} method) it must contain
|
||||||
|
* the bytes of a {@code COSE_Sign1} structure as defined in RFC 8152. For the payload
|
||||||
|
* {@code nil} shall be used and the detached payload is the {@code ReaderAuthenticationBytes}
|
||||||
|
* CBOR described below.
|
||||||
|
* <pre>
|
||||||
|
* ReaderAuthentication = [
|
||||||
|
* "ReaderAuthentication",
|
||||||
|
* SessionTranscript,
|
||||||
|
* ItemsRequestBytes
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
|
||||||
|
*
|
||||||
|
* ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>where {@code ItemsRequestBytes} are the bytes of the request message set in
|
||||||
|
* the request parameter (via the
|
||||||
|
* {@link CredentialDataRequest.Builder#setRequestMessage(byte[])} method).
|
||||||
|
*
|
||||||
|
* <p>The public key corresponding to the key used to make the signature, can be found in the
|
||||||
|
* {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
|
||||||
|
* described in
|
||||||
|
* <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-08">draft-ietf-cose-x509-08</a>).
|
||||||
|
* There will be at least one certificate in said element and there may be more (and if so,
|
||||||
|
* each certificate must be signed by its successor).
|
||||||
|
*
|
||||||
|
* <p>Data elements protected by reader authentication are returned if, and only if,
|
||||||
|
* {@code requestMessage} is signed by the top-most certificate in the reader's certificate
|
||||||
|
* chain, and the data element is configured with an {@link AccessControlProfile} configured
|
||||||
|
* with an X.509 certificate for a key which appear in the certificate chain.
|
||||||
|
*
|
||||||
|
* <p>Note that the request message CBOR is used only for enforcing reader authentication, it's
|
||||||
|
* not used for determining which entries this API will return. The application is expected to
|
||||||
|
* have parsed the request message and filtered it according to user preference and/or consent.
|
||||||
|
*
|
||||||
|
* @param credentialName the name of the credential to retrieve.
|
||||||
|
* @param request the data to retrieve from the credential
|
||||||
|
* @return If the credential wasn't found, returns null. Otherwise a
|
||||||
|
* {@link CredentialDataResult} object containing entry data organized by namespace and
|
||||||
|
* a cryptographically authenticated representation of the same data, bound to the
|
||||||
|
* current session.
|
||||||
|
* @throws NoAuthenticationKeyAvailableException if authentication keys were never
|
||||||
|
* provisioned for the credential or if they
|
||||||
|
* are expired or exhausted their use-count.
|
||||||
|
* @throws InvalidRequestMessageException if the requestMessage is malformed.
|
||||||
|
* @throws InvalidReaderSignatureException if the reader signature is invalid, or it
|
||||||
|
* doesn't contain a certificate chain, or if
|
||||||
|
* the signature failed to validate.
|
||||||
|
* @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
|
||||||
|
* the session transcript.
|
||||||
|
*/
|
||||||
|
public abstract @Nullable CredentialDataResult getCredentialData(
|
||||||
|
@NonNull String credentialName, @NonNull CredentialDataRequest request)
|
||||||
|
throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
|
||||||
|
InvalidRequestMessageException, EphemeralPublicKeyNotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
|
||||||
|
* operation handle.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public abstract long getCredstoreOperationHandle();
|
||||||
|
}
|
||||||
@@ -28,7 +28,10 @@ import java.util.Collection;
|
|||||||
/**
|
/**
|
||||||
* An object that contains the result of retrieving data from a credential. This is used to return
|
* An object that contains the result of retrieving data from a credential. This is used to return
|
||||||
* data requested from a {@link IdentityCredential}.
|
* data requested from a {@link IdentityCredential}.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link PresentationSession} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class ResultData {
|
public abstract class ResultData {
|
||||||
|
|
||||||
/** Value was successfully retrieved. */
|
/** Value was successfully retrieved. */
|
||||||
|
|||||||
Reference in New Issue
Block a user