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
This commit is contained in:
Helen Qin
2022-11-05 00:35:30 +00:00
parent 8e0b6abf0e
commit 433013a6d6
6 changed files with 84 additions and 63 deletions

View File

@@ -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<android.credentials.CreateCredentialRequest> 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<android.credentials.GetCredentialOption> CREATOR;
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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()
)
}
}
}

View File

@@ -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;