• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.security;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.net.Uri;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 import org.xmlpull.v1.XmlSerializer;
29 
30 import java.io.IOException;
31 import java.security.Principal;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Map;
35 import java.util.Objects;
36 import java.util.Set;
37 
38 /**
39  * The app-URI authentication policy is set by the credential management app. This policy determines
40  * which alias for a private key and certificate pair should be used for authentication.
41  * <p>
42  * The authentication policy should be added as a parameter when calling
43  * {@link KeyChain#createManageCredentialsIntent}.
44  * <p>
45  * Example:
46  * <pre>{@code
47  *     AppUriAuthenticationPolicy authenticationPolicy = new AppUriAuthenticationPolicy.Builder()
48  *              .addAppAndUriMapping("com.test.pkg", testUri, "testAlias")
49  *              .addAppAndUriMapping("com.test2.pkg", testUri1, "testAlias2")
50  *              .addAppAndUriMapping("com.test2.pkg", testUri2, "testAlias2")
51  *              .build();
52  *     Intent requestIntent = KeyChain.createManageCredentialsIntent(authenticationPolicy);
53  * }</pre>
54  * <p>
55  */
56 public final class AppUriAuthenticationPolicy implements Parcelable {
57 
58     private static final String KEY_AUTHENTICATION_POLICY_APP_TO_URIS =
59             "authentication_policy_app_to_uris";
60     private static final String KEY_AUTHENTICATION_POLICY_APP = "policy_app";
61 
62     /**
63      * The mappings from an app and list of URIs to a list of aliases, which will be used for
64      * authentication.
65      * <p>
66      * appPackageName -> uri -> alias
67      */
68     @NonNull
69     private final Map<String, UrisToAliases> mAppToUris;
70 
AppUriAuthenticationPolicy(@onNull Map<String, UrisToAliases> appToUris)71     private AppUriAuthenticationPolicy(@NonNull Map<String, UrisToAliases> appToUris) {
72         Objects.requireNonNull(appToUris);
73         this.mAppToUris = appToUris;
74     }
75 
76     /**
77      * Builder class for {@link AppUriAuthenticationPolicy} objects.
78      */
79     public static final class Builder {
80         private Map<String, UrisToAliases> mPackageNameToUris;
81 
82         /**
83          * Initialize a new Builder to construct an {@link AppUriAuthenticationPolicy}.
84          */
Builder()85         public Builder() {
86             mPackageNameToUris = new HashMap<>();
87         }
88 
89         /**
90          * Adds mappings from an app and URI to an alias, which will be used for authentication.
91          * <p>
92          * If this method is called with a package name and URI that was previously added, the
93          * previous alias will be overwritten.
94          * <p>
95          * When the system tries to determine which alias to return to a requesting app calling
96          * {@code KeyChain.choosePrivateKeyAlias}, it will choose the alias whose associated URI
97          * exactly matches the URI provided in {@link KeyChain#choosePrivateKeyAlias(
98          * Activity, KeyChainAliasCallback, String[], Principal[], Uri, String)} or the URI
99          * built from the host and port provided in {@link KeyChain#choosePrivateKeyAlias(
100          * Activity, KeyChainAliasCallback, String[], Principal[], String, int, String)}.
101          *
102          * @param appPackageName The app's package name to authenticate the user to.
103          * @param uri            The URI to authenticate the user to.
104          * @param alias          The alias which will be used for authentication.
105          *
106          * @return the same Builder instance.
107          */
108         @NonNull
addAppAndUriMapping(@onNull String appPackageName, @NonNull Uri uri, @NonNull String alias)109         public Builder addAppAndUriMapping(@NonNull String appPackageName, @NonNull Uri uri,
110                 @NonNull String alias) {
111             Objects.requireNonNull(appPackageName);
112             Objects.requireNonNull(uri);
113             Objects.requireNonNull(alias);
114             UrisToAliases urisToAliases =
115                     mPackageNameToUris.getOrDefault(appPackageName, new UrisToAliases());
116             urisToAliases.addUriToAlias(uri, alias);
117             mPackageNameToUris.put(appPackageName, urisToAliases);
118             return this;
119         }
120 
121         /**
122          * Adds mappings from an app and list of URIs to a list of aliases, which will be used for
123          * authentication.
124          * <p>
125          * appPackageName -> uri -> alias
126          *
127          * @hide
128          */
129         @NonNull
addAppAndUriMapping(@onNull String appPackageName, @NonNull UrisToAliases urisToAliases)130         public Builder addAppAndUriMapping(@NonNull String appPackageName,
131                 @NonNull UrisToAliases urisToAliases) {
132             Objects.requireNonNull(appPackageName);
133             Objects.requireNonNull(urisToAliases);
134             mPackageNameToUris.put(appPackageName, urisToAliases);
135             return this;
136         }
137 
138         /**
139          * Combines all of the attributes that have been set on the {@link Builder}
140          *
141          * @return a new {@link AppUriAuthenticationPolicy} object.
142          */
143         @NonNull
build()144         public AppUriAuthenticationPolicy build() {
145             return new AppUriAuthenticationPolicy(mPackageNameToUris);
146         }
147     }
148 
149     @Override
describeContents()150     public int describeContents() {
151         return 0;
152     }
153 
154     @Override
writeToParcel(@onNull Parcel dest, int flags)155     public void writeToParcel(@NonNull Parcel dest, int flags) {
156         dest.writeMap(mAppToUris);
157     }
158 
159     @NonNull
160     public static final Parcelable.Creator<AppUriAuthenticationPolicy> CREATOR =
161             new Parcelable.Creator<AppUriAuthenticationPolicy>() {
162                 @Override
163                 public AppUriAuthenticationPolicy createFromParcel(Parcel in) {
164                     Map<String, UrisToAliases> appToUris = new HashMap<>();
165                     in.readMap(appToUris, UrisToAliases.class.getClassLoader());
166                     return new AppUriAuthenticationPolicy(appToUris);
167                 }
168 
169                 @Override
170                 public AppUriAuthenticationPolicy[] newArray(int size) {
171                     return new AppUriAuthenticationPolicy[size];
172                 }
173             };
174 
175     @Override
toString()176     public String toString() {
177         return "AppUriAuthenticationPolicy{"
178                 + "mPackageNameToUris=" + mAppToUris
179                 + '}';
180     }
181 
182     /**
183      * Return the authentication policy mapping, which determines which alias for a private key
184      * and certificate pair should be used for authentication.
185      * <p>
186      * appPackageName -> uri -> alias
187      */
188     @NonNull
getAppAndUriMappings()189     public Map<String, Map<Uri, String>> getAppAndUriMappings() {
190         Map<String, Map<Uri, String>> appAndUris = new HashMap<>();
191         for (Map.Entry<String, UrisToAliases> entry : mAppToUris.entrySet()) {
192             appAndUris.put(entry.getKey(), entry.getValue().getUrisToAliases());
193         }
194         return appAndUris;
195     }
196 
197     /**
198      * Restore a previously saved {@link AppUriAuthenticationPolicy} from XML.
199      *
200      * @hide
201      */
202     @Nullable
readFromXml(@onNull XmlPullParser parser)203     public static AppUriAuthenticationPolicy readFromXml(@NonNull XmlPullParser parser)
204             throws IOException, XmlPullParserException {
205         AppUriAuthenticationPolicy.Builder builder = new AppUriAuthenticationPolicy.Builder();
206         int outerDepth = parser.getDepth();
207         int type;
208         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
209                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
210             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
211                 continue;
212             }
213             if (!parser.getName().equals(KEY_AUTHENTICATION_POLICY_APP_TO_URIS)) {
214                 continue;
215             }
216             String app = parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_APP);
217             UrisToAliases urisToAliases = UrisToAliases.readFromXml(parser);
218             builder.addAppAndUriMapping(app, urisToAliases);
219         }
220         return builder.build();
221     }
222 
223     /**
224      * Save the {@link AppUriAuthenticationPolicy} to XML.
225      *
226      * @hide
227      */
writeToXml(@onNull XmlSerializer out)228     public void writeToXml(@NonNull XmlSerializer out) throws IOException {
229         for (Map.Entry<String, UrisToAliases> appsToUris : mAppToUris.entrySet()) {
230             out.startTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS);
231             out.attribute(null, KEY_AUTHENTICATION_POLICY_APP, appsToUris.getKey());
232             appsToUris.getValue().writeToXml(out);
233             out.endTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS);
234         }
235     }
236 
237     /**
238      * Get the set of aliases found in the policy.
239      *
240      * @hide
241      */
getAliases()242     public Set<String> getAliases() {
243         Set<String> aliases = new HashSet<>();
244         for (UrisToAliases appsToUris : mAppToUris.values()) {
245             aliases.addAll(appsToUris.getUrisToAliases().values());
246         }
247         return aliases;
248     }
249 
250     @Override
equals(Object obj)251     public boolean equals(Object obj) {
252         if (this == obj) {
253             return true;
254         }
255         if (!(obj instanceof AppUriAuthenticationPolicy)) {
256             return false;
257         }
258         AppUriAuthenticationPolicy other = (AppUriAuthenticationPolicy) obj;
259         return Objects.equals(mAppToUris, other.mAppToUris);
260     }
261 
262     @Override
hashCode()263     public int hashCode() {
264         return mAppToUris.hashCode();
265     }
266 
267 }
268