From 433013a6d65b8c5d07ad06f3552494aae68b269e Mon Sep 17 00:00:00 2001 From: Helen Qin Date: Sat, 5 Nov 2022 00:35:30 +0000 Subject: [PATCH] Update CreateCredentialRequest and GetCredentialOption with recent changes. Specifically: 1. for the create request, add the api for prefetch data that's the partial request data used during the initial ping to each provider. This is needed for security reason, so that we don't send sensitive request data to providers until the user explicitly consents. 2. for both requests, add(unhide) the requireSystemProvider api, which when true indicates this request can only be fulfilled by system providers. Bug: 246564035 Bug: 253153445 CTS-Coverage-Bug: 246637346 Test: Local Build & Deployment Change-Id: I9f4a96dc28e87f92924abda8412b15010ee431b7 --- core/api/current.txt | 9 +- .../credentials/CreateCredentialRequest.java | 85 ++++++++++++------- .../credentials/GetCredentialOption.java | 16 ---- .../CredentialManagerRepo.kt | 19 +++-- .../developer/CreateCredentialRequest.kt | 12 ++- .../credentials/ProviderCreateSession.java | 6 +- 6 files changed, 84 insertions(+), 63 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index a03a753d7cefb..14f8d3776dd6a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13025,10 +13025,12 @@ package android.credentials { } public final class CreateCredentialRequest implements android.os.Parcelable { - ctor public CreateCredentialRequest(@NonNull String, @NonNull android.os.Bundle); + ctor public CreateCredentialRequest(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean); method public int describeContents(); - method @NonNull public android.os.Bundle getData(); + method @NonNull public android.os.Bundle getCandidateQueryData(); + method @NonNull public android.os.Bundle getCredentialData(); method @NonNull public String getType(); + method public boolean requireSystemProvider(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } @@ -13066,10 +13068,11 @@ package android.credentials { } public final class GetCredentialOption implements android.os.Parcelable { - ctor public GetCredentialOption(@NonNull String, @NonNull android.os.Bundle); + ctor public GetCredentialOption(@NonNull String, @NonNull android.os.Bundle, boolean); method public int describeContents(); method @NonNull public android.os.Bundle getData(); method @NonNull public String getType(); + method public boolean requireSystemProvider(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } diff --git a/core/java/android/credentials/CreateCredentialRequest.java b/core/java/android/credentials/CreateCredentialRequest.java index 22ef23019dcdd..45890392bed72 100644 --- a/core/java/android/credentials/CreateCredentialRequest.java +++ b/core/java/android/credentials/CreateCredentialRequest.java @@ -39,10 +39,17 @@ public final class CreateCredentialRequest implements Parcelable { private final String mType; /** - * The request data. + * The full credential creation request data. */ @NonNull - private final Bundle mData; + private final Bundle mCredentialData; + + /** + * The partial request data that will be sent to the provider during the initial creation + * candidate query stage. + */ + @NonNull + private final Bundle mCandidateQueryData; /** * Determines whether or not the request must only be fulfilled by a system provider. @@ -58,18 +65,39 @@ public final class CreateCredentialRequest implements Parcelable { } /** - * Returns the request data. + * Returns the full credential creation request data. + * + * For security reason, a provider will receive the request data in two stages. First it gets + * a partial request, {@link #getCandidateQueryData()} that do not contain sensitive user + * information; it uses this information to provide credential creation candidates that the + * [@code CredentialManager] will show to the user. Next, this full request data will be sent to + * a provider only if the user further grants the consent by choosing a candidate from the + * provider. */ @NonNull - public Bundle getData() { - return mData; + public Bundle getCredentialData() { + return mCredentialData; + } + + /** + * Returns the partial request data that will be sent to the provider during the initial + * creation candidate query stage. + * + * For security reason, a provider will receive the request data in two stages. First it gets + * this partial request that do not contain sensitive user information; it uses this information + * to provide credential creation candidates that the [@code CredentialManager] will show to + * the user. Next, the full request data, {@link #getCredentialData()}, will be sent to a + * provider only if the user further grants the consent by choosing a candidate from the + * provider. + */ + @NonNull + public Bundle getCandidateQueryData() { + return mCandidateQueryData; } /** * Returns true if the request must only be fulfilled by a system provider, and false * otherwise. - * - * @hide */ public boolean requireSystemProvider() { return mRequireSystemProvider; @@ -78,7 +106,8 @@ public final class CreateCredentialRequest implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString8(mType); - dest.writeBundle(mData); + dest.writeBundle(mCredentialData); + dest.writeBundle(mCandidateQueryData); dest.writeBoolean(mRequireSystemProvider); } @@ -91,7 +120,8 @@ public final class CreateCredentialRequest implements Parcelable { public String toString() { return "CreateCredentialRequest {" + "type=" + mType - + ", data=" + mData + + ", credentialData=" + mCredentialData + + ", candidateQueryData=" + mCandidateQueryData + ", requireSystemProvider=" + mRequireSystemProvider + "}"; } @@ -100,44 +130,37 @@ public final class CreateCredentialRequest implements Parcelable { * Constructs a {@link CreateCredentialRequest}. * * @param type the requested credential type - * @param data the request data - * - * @throws IllegalArgumentException If type is empty - */ - public CreateCredentialRequest(@NonNull String type, @NonNull Bundle data) { - this(type, data, /*requireSystemProvider=*/ false); - } - - /** - * Constructs a {@link CreateCredentialRequest}. - * - * @param type the requested credential type - * @param data the request data - * @param requireSystemProvider whether or not the request must only be fulfilled by a system - * provider + * @param credentialData the full credential creation request data + * @param candidateQueryData the partial request data that will be sent to the provider + * during the initial creation candidate query stage + * @param requireSystemProvider whether the request must only be fulfilled by a system provider * * @throws IllegalArgumentException If type is empty. - * - * @hide */ public CreateCredentialRequest( @NonNull String type, - @NonNull Bundle data, + @NonNull Bundle credentialData, + @NonNull Bundle candidateQueryData, boolean requireSystemProvider) { mType = Preconditions.checkStringNotEmpty(type, "type must not be empty"); - mData = requireNonNull(data, "data must not be null"); + mCredentialData = requireNonNull(credentialData, "credentialData must not be null"); + mCandidateQueryData = requireNonNull(candidateQueryData, + "candidateQueryData must not be null"); mRequireSystemProvider = requireSystemProvider; } private CreateCredentialRequest(@NonNull Parcel in) { String type = in.readString8(); - Bundle data = in.readBundle(); + Bundle credentialData = in.readBundle(); + Bundle candidateQueryData = in.readBundle(); boolean requireSystemProvider = in.readBoolean(); mType = type; AnnotationValidations.validate(NonNull.class, null, mType); - mData = data; - AnnotationValidations.validate(NonNull.class, null, mData); + mCredentialData = credentialData; + AnnotationValidations.validate(NonNull.class, null, mCredentialData); + mCandidateQueryData = candidateQueryData; + AnnotationValidations.validate(NonNull.class, null, mCandidateQueryData); mRequireSystemProvider = requireSystemProvider; } diff --git a/core/java/android/credentials/GetCredentialOption.java b/core/java/android/credentials/GetCredentialOption.java index a0d3c0b2e6ede..ed93daef20d31 100644 --- a/core/java/android/credentials/GetCredentialOption.java +++ b/core/java/android/credentials/GetCredentialOption.java @@ -67,8 +67,6 @@ public final class GetCredentialOption implements Parcelable { /** * Returns true if the request must only be fulfilled by a system provider, and false * otherwise. - * - * @hide */ public boolean requireSystemProvider() { return mRequireSystemProvider; @@ -95,18 +93,6 @@ public final class GetCredentialOption implements Parcelable { + "}"; } - /** - * Constructs a {@link GetCredentialOption}. - * - * @param type the requested credential type - * @param data the request data - * - * @throws IllegalArgumentException If type is empty - */ - public GetCredentialOption(@NonNull String type, @NonNull Bundle data) { - this(type, data, /*requireSystemProvider=*/ false); - } - /** * Constructs a {@link GetCredentialOption}. * @@ -116,8 +102,6 @@ public final class GetCredentialOption implements Parcelable { * provider * * @throws IllegalArgumentException If type is empty. - * - * @hide */ public GetCredentialOption( @NonNull String type, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 0cc11946ca85b..23953a7974bd3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -458,12 +458,15 @@ class CredentialManagerRepo( " \"residentKey\": \"required\",\n" + " \"requireResidentKey\": true\n" + " }}") - val data = request.data + val credentialData = request.data return RequestInfo.newCreateRequestInfo( Binder(), CreateCredentialRequest( TYPE_PUBLIC_KEY_CREDENTIAL, - data + credentialData, + // TODO: populate with actual data + /*candidateQueryData=*/ Bundle(), + /*requireSystemProvider=*/ false ), /*isFirstUsage=*/false, "tribank" @@ -476,7 +479,10 @@ class CredentialManagerRepo( Binder(), CreateCredentialRequest( TYPE_PASSWORD_CREDENTIAL, - data + data, + // TODO: populate with actual data + /*candidateQueryData=*/ Bundle(), + /*requireSystemProvider=*/ false ), /*isFirstUsage=*/false, "tribank" @@ -489,7 +495,9 @@ class CredentialManagerRepo( Binder(), CreateCredentialRequest( "other-sign-ins", - data + data, + /*candidateQueryData=*/ Bundle(), + /*requireSystemProvider=*/ false ), /*isFirstUsage=*/false, "tribank" @@ -501,7 +509,8 @@ class CredentialManagerRepo( Binder(), GetCredentialRequest.Builder() .addGetCredentialOption( - GetCredentialOption(TYPE_PUBLIC_KEY_CREDENTIAL, Bundle()) + GetCredentialOption( + TYPE_PUBLIC_KEY_CREDENTIAL, Bundle(), /*requireSystemProvider=*/ false) ) .build(), /*isFirstUsage=*/false, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt index 7e7dbde8655af..008e1b6317ded 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/CreateCredentialRequest.kt @@ -38,14 +38,18 @@ open class CreateCredentialRequest( return try { when (from.type) { Credential.TYPE_PASSWORD_CREDENTIAL -> - CreatePasswordRequest.createFrom(from.data) + CreatePasswordRequest.createFrom(from.credentialData) PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> - CreatePublicKeyCredentialBaseRequest.createFrom(from.data) + CreatePublicKeyCredentialBaseRequest.createFrom(from.credentialData) else -> - CreateCredentialRequest(from.type, from.data, from.requireSystemProvider()) + CreateCredentialRequest( + from.type, from.credentialData, from.requireSystemProvider() + ) } } catch (e: FrameworkClassParsingException) { - CreateCredentialRequest(from.type, from.data, from.requireSystemProvider()) + CreateCredentialRequest( + from.type, from.credentialData, from.requireSystemProvider() + ) } } } diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java index 332a75ea566b2..8854453a61cd5 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java @@ -25,7 +25,6 @@ import android.content.Intent; import android.credentials.ui.CreateCredentialProviderData; import android.credentials.ui.Entry; import android.credentials.ui.ProviderPendingIntentResponse; -import android.os.Bundle; import android.service.credentials.BeginCreateCredentialRequest; import android.service.credentials.BeginCreateCredentialResponse; import android.service.credentials.CreateCredentialRequest; @@ -68,12 +67,11 @@ public final class ProviderCreateSession extends ProviderSession< createRequestSession.mClientRequest, createRequestSession.mClientCallingPackage); if (providerCreateRequest != null) { - // TODO : Replace with proper splitting of request BeginCreateCredentialRequest providerBeginCreateRequest = new BeginCreateCredentialRequest( providerCreateRequest.getCallingPackage(), providerCreateRequest.getType(), - new Bundle()); + createRequestSession.mClientRequest.getCandidateQueryData()); return new ProviderCreateSession(context, providerInfo, createRequestSession, userId, remoteCredentialService, providerBeginCreateRequest, providerCreateRequest); } @@ -88,7 +86,7 @@ public final class ProviderCreateSession extends ProviderSession< String capability = clientRequest.getType(); if (providerCapabilities.contains(capability)) { return new CreateCredentialRequest(clientCallingPackage, capability, - clientRequest.getData()); + clientRequest.getCredentialData()); } Log.i(TAG, "Unable to create provider request - capabilities do not match"); return null;