diff options
author | 2022-10-04 13:18:12 -0400 | |
---|---|---|
committer | 2022-12-09 02:59:23 -0500 | |
commit | 55f62fc1252f45803263d9d88a9fe7179d334172 (patch) | |
tree | b1e8283e7f6b742236deb68c1e8e41156801b33e | |
parent | 9375c9ce61fc4e780f98922952497c8ad9a57ad4 (diff) |
identity: Add support for ECDSA auth and don't require session encryption.
This adds a new method which allows applications to use mdoc ECDSA
authentication instead of mdoc MAC authentication. Additionally, also
relax requirements on SessionTranscript so the APIs can be used even
when mdoc session encryption isn't being used.
Bug: 241912421
Test: atest VtsHalIdentityTargetTest
Test: atest android.security.identity.cts
Change-Id: I25336f1352102208887531d066ec432a9ae3cd36
10 files changed, 100 insertions, 19 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 7ef4f770fcd9..367b88bd2806 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -37682,6 +37682,7 @@ package android.security.identity { public abstract class CredentialDataResult { method @Nullable public abstract byte[] getDeviceMac(); method @NonNull public abstract byte[] getDeviceNameSpaces(); + method @Nullable public byte[] getDeviceSignature(); 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(); diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java index beb03af46303..dca039a06a0d 100644 --- a/identity/java/android/security/identity/CredentialDataResult.java +++ b/identity/java/android/security/identity/CredentialDataResult.java @@ -106,6 +106,30 @@ public abstract class CredentialDataResult { public abstract @Nullable byte[] getDeviceMac(); /** + * Returns a signature over the {@code DeviceAuthenticationBytes} CBOR + * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data + * is from a trusted credential. + * + * <p>The signature is made using the authentication private key. See section 9.1.3.4 of + * ISO/IEC 18013-5:2021 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 signature will be produced and this method + * will return {@code null}. + * + * <p>This is only implemented in feature version 202301 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. + * + * @return A COSE_Sign1 structure as described above or {@code null} if the conditions + * specified above are not met. + */ + public @Nullable byte[] getDeviceSignature() { + throw new UnsupportedOperationException(); + } + + /** * Returns the static authentication data associated with the dynamic authentication * key used to MAC the data returned by {@link #getDeviceNameSpaces()}. * diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java index 7afe3d448bf9..b4fd5d3fe5a7 100644 --- a/identity/java/android/security/identity/CredstoreCredentialDataResult.java +++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java @@ -47,6 +47,11 @@ class CredstoreCredentialDataResult extends CredentialDataResult { } @Override + public @Nullable byte[] getDeviceSignature() { + return mDeviceSignedResult.getSignature(); + } + + @Override public @NonNull byte[] getStaticAuthenticationData() { return mDeviceSignedResult.getStaticAuthenticationData(); } diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java index c591c879059c..06953ba83289 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java @@ -60,16 +60,19 @@ class CredstoreIdentityCredential extends IdentityCredential { private Context mContext; private ICredential mBinder; private CredstorePresentationSession mSession; + private int mFeatureVersion; CredstoreIdentityCredential(Context context, String credentialName, @IdentityCredentialStore.Ciphersuite int cipherSuite, ICredential binder, - @Nullable CredstorePresentationSession session) { + @Nullable CredstorePresentationSession session, + int featureVersion) { mContext = context; mCredentialName = credentialName; mCipherSuite = cipherSuite; mBinder = binder; mSession = session; + mFeatureVersion = featureVersion; } private KeyPair mEphemeralKeyPair = null; @@ -347,12 +350,18 @@ class CredstoreIdentityCredential extends IdentityCredential { } } + byte[] signature = resultParcel.signature; + if (signature != null && signature.length == 0) { + signature = null; + } + byte[] mac = resultParcel.mac; if (mac != null && mac.length == 0) { mac = null; } CredstoreResultData.Builder resultDataBuilder = new CredstoreResultData.Builder( - resultParcel.staticAuthenticationData, resultParcel.deviceNameSpaces, mac); + mFeatureVersion, resultParcel.staticAuthenticationData, + resultParcel.deviceNameSpaces, mac, signature); for (ResultNamespaceParcel resultNamespaceParcel : resultParcel.resultNamespaces) { for (ResultEntryParcel resultEntryParcel : resultNamespaceParcel.entries) { diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java index bbaf0862f923..d785c3c895b8 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java @@ -19,6 +19,8 @@ package android.security.identity; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.FeatureInfo; +import android.content.pm.PackageManager; import android.os.RemoteException; import android.os.ServiceManager; import android.security.GenerateRkpKey; @@ -30,10 +32,28 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { private Context mContext = null; private ICredentialStore mStore = null; + private int mFeatureVersion; + + static int getFeatureVersion(@NonNull Context context) { + PackageManager pm = context.getPackageManager(); + if (pm.hasSystemFeature(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) { + FeatureInfo[] infos = pm.getSystemAvailableFeatures(); + for (int n = 0; n < infos.length; n++) { + FeatureInfo info = infos[n]; + if (info.name.equals(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) { + return info.version; + } + } + } + // Use of the system feature is not required since Android 12. So for Android 11 + // return 202009 which is the feature version shipped with Android 11. + return 202009; + } private CredstoreIdentityCredentialStore(@NonNull Context context, ICredentialStore store) { mContext = context; mStore = store; + mFeatureVersion = getFeatureVersion(mContext); } static CredstoreIdentityCredentialStore getInstanceForType(@NonNull Context context, @@ -139,8 +159,7 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { ICredential credstoreCredential; credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite); return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite, - credstoreCredential, - null); + credstoreCredential, null, mFeatureVersion); } catch (android.os.RemoteException e) { throw new RuntimeException("Unexpected RemoteException ", e); } catch (android.os.ServiceSpecificException e) { @@ -182,7 +201,8 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { throws CipherSuiteNotSupportedException { try { ISession credstoreSession = mStore.createPresentationSession(cipherSuite); - return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession); + return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession, + mFeatureVersion); } catch (android.os.RemoteException e) { throw new RuntimeException("Unexpected RemoteException ", e); } catch (android.os.ServiceSpecificException e) { diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java index e3c6689a8914..96bda5215c6d 100644 --- a/identity/java/android/security/identity/CredstorePresentationSession.java +++ b/identity/java/android/security/identity/CredstorePresentationSession.java @@ -48,15 +48,18 @@ class CredstorePresentationSession extends PresentationSession { private byte[] mSessionTranscript = null; private boolean mOperationHandleSet = false; private long mOperationHandle = 0; + private int mFeatureVersion = 0; CredstorePresentationSession(Context context, @IdentityCredentialStore.Ciphersuite int cipherSuite, CredstoreIdentityCredentialStore store, - ISession binder) { + ISession binder, + int featureVersion) { mContext = context; mCipherSuite = cipherSuite; mStore = store; mBinder = binder; + mFeatureVersion = featureVersion; } private void ensureEphemeralKeyPair() { @@ -147,7 +150,7 @@ class CredstorePresentationSession extends PresentationSession { mBinder.getCredentialForPresentation(credentialName); credential = new CredstoreIdentityCredential(mContext, credentialName, mCipherSuite, credstoreCredential, - this); + this, mFeatureVersion); mCredentialCache.put(credentialName, credential); credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys()); diff --git a/identity/java/android/security/identity/CredstoreResultData.java b/identity/java/android/security/identity/CredstoreResultData.java index 2ef735eec81d..57c04369203c 100644 --- a/identity/java/android/security/identity/CredstoreResultData.java +++ b/identity/java/android/security/identity/CredstoreResultData.java @@ -30,10 +30,11 @@ import java.util.Map; * data requested from a {@link IdentityCredential}. */ class CredstoreResultData extends ResultData { - + int mFeatureVersion = 0; byte[] mStaticAuthenticationData = null; byte[] mAuthenticatedData = null; byte[] mMessageAuthenticationCode = null; + byte[] mSignature = null; private Map<String, Map<String, EntryData>> mData = new LinkedHashMap<>(); @@ -61,6 +62,14 @@ class CredstoreResultData extends ResultData { } @Override + @Nullable byte[] getSignature() { + if (mFeatureVersion < 202301) { + throw new UnsupportedOperationException(); + } + return mSignature; + } + + @Override public @NonNull byte[] getStaticAuthenticationData() { return mStaticAuthenticationData; } @@ -124,13 +133,17 @@ class CredstoreResultData extends ResultData { static class Builder { private CredstoreResultData mResultData; - Builder(byte[] staticAuthenticationData, + Builder(int featureVersion, + byte[] staticAuthenticationData, byte[] authenticatedData, - byte[] messageAuthenticationCode) { + byte[] messageAuthenticationCode, + byte[] signature) { this.mResultData = new CredstoreResultData(); + this.mResultData.mFeatureVersion = featureVersion; this.mResultData.mStaticAuthenticationData = staticAuthenticationData; this.mResultData.mAuthenticatedData = authenticatedData; this.mResultData.mMessageAuthenticationCode = messageAuthenticationCode; + this.mResultData.mSignature = signature; } private Map<String, EntryData> getOrCreateInnerMap(String namespaceName) { diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index f440b693a5b3..3988c0c82b6e 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -236,14 +236,15 @@ public abstract class IdentityCredential { * IntentToRetain = bool * </pre> * - * <p>If the {@code sessionTranscript} parameter is not {@code null}, the X and Y coordinates - * of the public part of the key-pair previously generated by {@link #createEphemeralKeyPair()} - * must appear somewhere in the bytes of the 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 in {@code sessionTranscript} and ditto for the 32 bytes for the Y - * coordinate. + * <p>If mdoc session encryption is used (e.g. if {@link #createEphemeralKeyPair()} has been + * called) and if the {@code sessionTranscript} parameter is not {@code null}, the X and Y + * coordinates of the public part of the key-pair previously generated by + * {@link #createEphemeralKeyPair()} must appear somewhere in the bytes of the 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 in {@code sessionTranscript} and + * ditto for the 32 bytes for the Y coordinate. * * <p>If {@code readerSignature} is not {@code null} it must be the bytes of a * {@code COSE_Sign1} structure as defined in RFC 8152. For the payload nil shall be used and diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java index 6cde611fcd63..f392e121beca 100644 --- a/identity/java/android/security/identity/PresentationSession.java +++ b/identity/java/android/security/identity/PresentationSession.java @@ -73,7 +73,8 @@ public abstract class PresentationSession { * <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 + * <p>If mdoc session encryption is used (e.g. if {@link #getEphemeralKeyPair()} has been + * called) then 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 diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java index d46f9854b278..8a0e56ed3184 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -134,6 +134,10 @@ public abstract class ResultData { */ public abstract @Nullable byte[] getMessageAuthenticationCode(); + @Nullable byte[] getSignature() { + throw new UnsupportedOperationException(); + } + /** * Returns the static authentication data associated with the dynamic authentication * key used to sign or MAC the data returned by {@link #getAuthenticatedData()}. |