| /* |
| * Copyright (C) 2020 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; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.Activity; |
| import android.net.Uri; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| import org.xmlpull.v1.XmlSerializer; |
| |
| import java.io.IOException; |
| import java.security.Principal; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| /** |
| * The app-URI authentication policy is set by the credential management app. This policy determines |
| * which alias for a private key and certificate pair should be used for authentication. |
| * <p> |
| * The authentication policy should be added as a parameter when calling |
| * {@link KeyChain#createManageCredentialsIntent}. |
| * <p> |
| * Example: |
| * <pre>{@code |
| * AppUriAuthenticationPolicy authenticationPolicy = new AppUriAuthenticationPolicy.Builder() |
| * .addAppAndUriMapping("com.test.pkg", testUri, "testAlias") |
| * .addAppAndUriMapping("com.test2.pkg", testUri1, "testAlias2") |
| * .addAppAndUriMapping("com.test2.pkg", testUri2, "testAlias2") |
| * .build(); |
| * Intent requestIntent = KeyChain.createManageCredentialsIntent(authenticationPolicy); |
| * }</pre> |
| * <p> |
| */ |
| public final class AppUriAuthenticationPolicy implements Parcelable { |
| |
| private static final String KEY_AUTHENTICATION_POLICY_APP_TO_URIS = |
| "authentication_policy_app_to_uris"; |
| private static final String KEY_AUTHENTICATION_POLICY_APP = "policy_app"; |
| |
| /** |
| * The mappings from an app and list of URIs to a list of aliases, which will be used for |
| * authentication. |
| * <p> |
| * appPackageName -> uri -> alias |
| */ |
| @NonNull |
| private final Map<String, UrisToAliases> mAppToUris; |
| |
| private AppUriAuthenticationPolicy(@NonNull Map<String, UrisToAliases> appToUris) { |
| Objects.requireNonNull(appToUris); |
| this.mAppToUris = appToUris; |
| } |
| |
| /** |
| * Builder class for {@link AppUriAuthenticationPolicy} objects. |
| */ |
| public static final class Builder { |
| private Map<String, UrisToAliases> mPackageNameToUris; |
| |
| /** |
| * Initialize a new Builder to construct an {@link AppUriAuthenticationPolicy}. |
| */ |
| public Builder() { |
| mPackageNameToUris = new HashMap<>(); |
| } |
| |
| /** |
| * Adds mappings from an app and URI to an alias, which will be used for authentication. |
| * <p> |
| * If this method is called with a package name and URI that was previously added, the |
| * previous alias will be overwritten. |
| * <p> |
| * When the system tries to determine which alias to return to a requesting app calling |
| * {@code KeyChain.choosePrivateKeyAlias}, it will choose the alias whose associated URI |
| * exactly matches the URI provided in {@link KeyChain#choosePrivateKeyAlias( |
| * Activity, KeyChainAliasCallback, String[], Principal[], Uri, String)} or the URI |
| * built from the host and port provided in {@link KeyChain#choosePrivateKeyAlias( |
| * Activity, KeyChainAliasCallback, String[], Principal[], String, int, String)}. |
| * |
| * @param appPackageName The app's package name to authenticate the user to. |
| * @param uri The URI to authenticate the user to. |
| * @param alias The alias which will be used for authentication. |
| * |
| * @return the same Builder instance. |
| */ |
| @NonNull |
| public Builder addAppAndUriMapping(@NonNull String appPackageName, @NonNull Uri uri, |
| @NonNull String alias) { |
| Objects.requireNonNull(appPackageName); |
| Objects.requireNonNull(uri); |
| Objects.requireNonNull(alias); |
| UrisToAliases urisToAliases = |
| mPackageNameToUris.getOrDefault(appPackageName, new UrisToAliases()); |
| urisToAliases.addUriToAlias(uri, alias); |
| mPackageNameToUris.put(appPackageName, urisToAliases); |
| return this; |
| } |
| |
| /** |
| * Adds mappings from an app and list of URIs to a list of aliases, which will be used for |
| * authentication. |
| * <p> |
| * appPackageName -> uri -> alias |
| * |
| * @hide |
| */ |
| @NonNull |
| public Builder addAppAndUriMapping(@NonNull String appPackageName, |
| @NonNull UrisToAliases urisToAliases) { |
| Objects.requireNonNull(appPackageName); |
| Objects.requireNonNull(urisToAliases); |
| mPackageNameToUris.put(appPackageName, urisToAliases); |
| return this; |
| } |
| |
| /** |
| * Combines all of the attributes that have been set on the {@link Builder} |
| * |
| * @return a new {@link AppUriAuthenticationPolicy} object. |
| */ |
| @NonNull |
| public AppUriAuthenticationPolicy build() { |
| return new AppUriAuthenticationPolicy(mPackageNameToUris); |
| } |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| dest.writeMap(mAppToUris); |
| } |
| |
| @NonNull |
| public static final Parcelable.Creator<AppUriAuthenticationPolicy> CREATOR = |
| new Parcelable.Creator<AppUriAuthenticationPolicy>() { |
| @Override |
| public AppUriAuthenticationPolicy createFromParcel(Parcel in) { |
| Map<String, UrisToAliases> appToUris = new HashMap<>(); |
| in.readMap(appToUris, UrisToAliases.class.getClassLoader()); |
| return new AppUriAuthenticationPolicy(appToUris); |
| } |
| |
| @Override |
| public AppUriAuthenticationPolicy[] newArray(int size) { |
| return new AppUriAuthenticationPolicy[size]; |
| } |
| }; |
| |
| @Override |
| public String toString() { |
| return "AppUriAuthenticationPolicy{" |
| + "mPackageNameToUris=" + mAppToUris |
| + '}'; |
| } |
| |
| /** |
| * Return the authentication policy mapping, which determines which alias for a private key |
| * and certificate pair should be used for authentication. |
| * <p> |
| * appPackageName -> uri -> alias |
| */ |
| @NonNull |
| public Map<String, Map<Uri, String>> getAppAndUriMappings() { |
| Map<String, Map<Uri, String>> appAndUris = new HashMap<>(); |
| for (Map.Entry<String, UrisToAliases> entry : mAppToUris.entrySet()) { |
| appAndUris.put(entry.getKey(), entry.getValue().getUrisToAliases()); |
| } |
| return appAndUris; |
| } |
| |
| /** |
| * Restore a previously saved {@link AppUriAuthenticationPolicy} from XML. |
| * |
| * @hide |
| */ |
| @Nullable |
| public static AppUriAuthenticationPolicy readFromXml(@NonNull XmlPullParser parser) |
| throws IOException, XmlPullParserException { |
| AppUriAuthenticationPolicy.Builder builder = new AppUriAuthenticationPolicy.Builder(); |
| int outerDepth = parser.getDepth(); |
| int type; |
| while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
| && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { |
| if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { |
| continue; |
| } |
| if (!parser.getName().equals(KEY_AUTHENTICATION_POLICY_APP_TO_URIS)) { |
| continue; |
| } |
| String app = parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_APP); |
| UrisToAliases urisToAliases = UrisToAliases.readFromXml(parser); |
| builder.addAppAndUriMapping(app, urisToAliases); |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Save the {@link AppUriAuthenticationPolicy} to XML. |
| * |
| * @hide |
| */ |
| public void writeToXml(@NonNull XmlSerializer out) throws IOException { |
| for (Map.Entry<String, UrisToAliases> appsToUris : mAppToUris.entrySet()) { |
| out.startTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS); |
| out.attribute(null, KEY_AUTHENTICATION_POLICY_APP, appsToUris.getKey()); |
| appsToUris.getValue().writeToXml(out); |
| out.endTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS); |
| } |
| } |
| |
| /** |
| * Get the set of aliases found in the policy. |
| * |
| * @hide |
| */ |
| public Set<String> getAliases() { |
| Set<String> aliases = new HashSet<>(); |
| for (UrisToAliases appsToUris : mAppToUris.values()) { |
| aliases.addAll(appsToUris.getUrisToAliases().values()); |
| } |
| return aliases; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof AppUriAuthenticationPolicy)) { |
| return false; |
| } |
| AppUriAuthenticationPolicy other = (AppUriAuthenticationPolicy) obj; |
| return Objects.equals(mAppToUris, other.mAppToUris); |
| } |
| |
| @Override |
| public int hashCode() { |
| return mAppToUris.hashCode(); |
| } |
| |
| } |