aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Sigelbaum <sigelbaum@google.com>2017-08-31 16:42:37 -0700
committerDouglas Sigelbaum <sigelbaum@google.com>2017-09-13 15:24:11 -0700
commit96be336b4584215c04e49de7e27e5b15648fb496 (patch)
tree65cf928eb734eb2316963e3058c00d28cfa8bf33
parent03c96abdc9195b6be58765156002812ecee91ab9 (diff)
downloadandroid-96be336b4584215c04e49de7e27e5b15648fb496.tar.gz
Autofill sample: Adding pkg signature check.
Also, minor refactor to AutofillDataSource to make getInstance() parameter-less. Bug: 38182790 Test: manual Change-Id: Ib8e6780dfde2e263d9648d78df6b7e70477cf146
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java5
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java1
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java23
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java (renamed from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillRepository.java)17
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java38
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java78
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java126
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java1
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java3
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java20
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml1
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt17
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt98
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml1
14 files changed, 369 insertions, 60 deletions
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
index c6615fff..015dc68f 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
@@ -15,7 +15,6 @@
*/
package com.example.android.autofillframework.multidatasetservice;
-import android.app.Activity;
import android.app.PendingIntent;
import android.app.assist.AssistStructure;
import android.content.Context;
@@ -131,8 +130,8 @@ public class AuthActivity extends AppCompatActivity {
int saveTypes = autofillFields.getSaveType();
mReplyIntent = new Intent();
HashMap<String, FilledAutofillFieldCollection> clientFormDataMap =
- SharedPrefsAutofillRepository.getInstance(this).getFilledAutofillFieldCollection
- (autofillFields.getFocusedHints(), autofillFields.getAllHints());
+ SharedPrefsAutofillRepository.getInstance().getFilledAutofillFieldCollection
+ (this, autofillFields.getFocusedHints(), autofillFields.getAllHints());
if (forResponse) {
setResponseIntent(AutofillHelper.newResponse
(this, false, autofillFields, clientFormDataMap));
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
index 75cc849f..1e76f6ee 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
@@ -29,7 +29,6 @@ import android.widget.RemoteViews;
import com.example.android.autofillframework.R;
import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
index 19cf2ae0..047ee672 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
@@ -29,9 +29,11 @@ import android.service.autofill.SaveRequest;
import android.util.Log;
import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
+import android.widget.Toast;
import com.example.android.autofillframework.R;
import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
+import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
import com.example.android.autofillframework.multidatasetservice.settings.MyPreferences;
@@ -49,6 +51,13 @@ public class MyAutofillService extends AutofillService {
FillCallback callback) {
AssistStructure structure = request.getFillContexts()
.get(request.getFillContexts().size() - 1).getStructure();
+ String packageName = structure.getActivityComponent().getPackageName();
+ if (!SharedPrefsPackageVerificationRepository.getInstance()
+ .putPackageSignatures(getApplicationContext(), packageName)) {
+ Toast.makeText(getApplicationContext(), R.string.invalid_package_signature,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
final Bundle data = request.getClientState();
Log.d(TAG, "onFillRequest(): data=" + bundleToString(data));
@@ -79,8 +88,8 @@ public class MyAutofillService extends AutofillService {
} else {
boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth();
HashMap<String, FilledAutofillFieldCollection> clientFormDataMap =
- SharedPrefsAutofillRepository.getInstance(this).getFilledAutofillFieldCollection
- (autofillFields.getFocusedHints(), autofillFields.getAllHints());
+ SharedPrefsAutofillRepository.getInstance().getFilledAutofillFieldCollection
+ (this, autofillFields.getFocusedHints(), autofillFields.getAllHints());
FillResponse response = AutofillHelper.newResponse
(this, datasetAuth, autofillFields, clientFormDataMap);
callback.onSuccess(response);
@@ -91,12 +100,20 @@ public class MyAutofillService extends AutofillService {
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
List<FillContext> context = request.getFillContexts();
final AssistStructure structure = context.get(context.size() - 1).getStructure();
+ String packageName = structure.getActivityComponent().getPackageName();
+ if (!SharedPrefsPackageVerificationRepository.getInstance()
+ .putPackageSignatures(getApplicationContext(), packageName)) {
+ Toast.makeText(getApplicationContext(), R.string.invalid_package_signature,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
final Bundle data = request.getClientState();
Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data));
StructureParser parser = new StructureParser(structure);
parser.parseForSave();
FilledAutofillFieldCollection filledAutofillFieldCollection = parser.getClientFormData();
- SharedPrefsAutofillRepository.getInstance(this).saveFilledAutofillFieldCollection(filledAutofillFieldCollection);
+ SharedPrefsAutofillRepository.getInstance()
+ .saveFilledAutofillFieldCollection(this, filledAutofillFieldCollection);
}
@Override
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
index 7ee95552..99d1c261 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillRepository.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
@@ -15,27 +15,30 @@
*/
package com.example.android.autofillframework.multidatasetservice.datasource;
+import android.content.Context;
+
import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
import java.util.HashMap;
import java.util.List;
-public interface AutofillRepository {
+public interface AutofillDataSource {
/**
- * Gets saved FilledAutofillFieldCollection that contains some objects that can autofill fields with these
- * {@code autofillHints}.
+ * Gets saved FilledAutofillFieldCollection that contains some objects that can autofill fields
+ * with these {@code autofillHints}.
*/
- HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(List<String> focusedAutofillHints,
- List<String> allAutofillHints);
+ HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(Context context,
+ List<String> focusedAutofillHints, List<String> allAutofillHints);
/**
* Saves LoginCredential under this datasetName.
*/
- void saveFilledAutofillFieldCollection(FilledAutofillFieldCollection filledAutofillFieldCollection);
+ void saveFilledAutofillFieldCollection(Context context,
+ FilledAutofillFieldCollection filledAutofillFieldCollection);
/**
* Clears all data.
*/
- void clear();
+ void clear(Context context);
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java
new file mode 100644
index 00000000..129001d0
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 com.example.android.autofillframework.multidatasetservice.datasource;
+
+import android.content.Context;
+
+public interface PackageVerificationDataSource {
+
+ /**
+ * Verifies that the signatures in the passed {@code Context} match what is currently in
+ * storage. If there are no current signatures in storage for this packageName, it will store
+ * the signatures from the passed {@code Context}.
+ *
+ * @return {@code true} if signatures for this packageName are not currently in storage
+ * or if the signatures in the passed {@code Context} match what is currently in storage;
+ * {@code false} if the signatures in the passed {@code Context} do not match what is
+ * currently in storage or if an {@code Exception} was thrown while generating the signatures.
+ */
+ boolean putPackageSignatures(Context context, String packageName);
+
+ /**
+ * Clears all signature data currently in storage.
+ */
+ void clear(Context context);
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
index 9d29fb3a..7b55ef26 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
@@ -16,7 +16,6 @@
package com.example.android.autofillframework.multidatasetservice.datasource;
import android.content.Context;
-import android.content.SharedPreferences;
import android.util.ArraySet;
import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
@@ -32,36 +31,33 @@ import java.util.Set;
* Disclaimer: you should not store sensitive fields like user data unencrypted. This is done
* here only for simplicity and learning purposes.
*/
-public class SharedPrefsAutofillRepository implements AutofillRepository {
- private static final String SHARED_PREF_KEY = "com.example.android.autofillframework.service";
+public class SharedPrefsAutofillRepository implements AutofillDataSource {
+ private static final String SHARED_PREF_KEY = "com.example.android.autofillframework"
+ + ".multidatasetservice.datasource.AutofillDataSource";
private static final String CLIENT_FORM_DATA_KEY = "loginCredentialDatasets";
private static final String DATASET_NUMBER_KEY = "datasetNumber";
-
private static SharedPrefsAutofillRepository sInstance;
- private final SharedPreferences mPrefs;
-
- private SharedPrefsAutofillRepository(Context context) {
- mPrefs = context.getApplicationContext()
- .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ private SharedPrefsAutofillRepository() {
}
- public static SharedPrefsAutofillRepository getInstance(Context context) {
+ public static SharedPrefsAutofillRepository getInstance() {
if (sInstance == null) {
- sInstance = new SharedPrefsAutofillRepository(context);
+ sInstance = new SharedPrefsAutofillRepository();
}
return sInstance;
}
@Override
- public HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(List<String> focusedAutofillHints,
- List<String> allAutofillHints) {
+ public HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(
+ Context context, List<String> focusedAutofillHints, List<String> allAutofillHints) {
boolean hasDataForFocusedAutofillHints = false;
HashMap<String, FilledAutofillFieldCollection> clientFormDataMap = new HashMap<>();
- Set<String> clientFormDataStringSet = getAllAutofillDataStringSet();
+ Set<String> clientFormDataStringSet = getAllAutofillDataStringSet(context);
for (String clientFormDataString : clientFormDataStringSet) {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
- FilledAutofillFieldCollection filledAutofillFieldCollection = gson.fromJson(clientFormDataString, FilledAutofillFieldCollection.class);
+ FilledAutofillFieldCollection filledAutofillFieldCollection =
+ gson.fromJson(clientFormDataString, FilledAutofillFieldCollection.class);
if (filledAutofillFieldCollection != null) {
if (filledAutofillFieldCollection.helpsWithHints(focusedAutofillHints)) {
// Saved data has data relevant to at least 1 of the hints associated with the
@@ -71,7 +67,8 @@ public class SharedPrefsAutofillRepository implements AutofillRepository {
if (filledAutofillFieldCollection.helpsWithHints(allAutofillHints)) {
// Saved data has data relevant to at least 1 of these hints associated with any
// of the Views in the hierarchy.
- clientFormDataMap.put(filledAutofillFieldCollection.getDatasetName(), filledAutofillFieldCollection);
+ clientFormDataMap.put(filledAutofillFieldCollection.getDatasetName(),
+ filledAutofillFieldCollection);
}
}
}
@@ -83,42 +80,61 @@ public class SharedPrefsAutofillRepository implements AutofillRepository {
}
@Override
- public void saveFilledAutofillFieldCollection(FilledAutofillFieldCollection filledAutofillFieldCollection) {
- String datasetName = "dataset-" + getDatasetNumber();
+ public void saveFilledAutofillFieldCollection(Context context,
+ FilledAutofillFieldCollection filledAutofillFieldCollection) {
+ String datasetName = "dataset-" + getDatasetNumber(context);
filledAutofillFieldCollection.setDatasetName(datasetName);
- Set<String> allAutofillData = getAllAutofillDataStringSet();
+ Set<String> allAutofillData = getAllAutofillDataStringSet(context);
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
allAutofillData.add(gson.toJson(filledAutofillFieldCollection));
- saveAllAutofillDataStringSet(allAutofillData);
- incrementDatasetNumber();
+ saveAllAutofillDataStringSet(context, allAutofillData);
+ incrementDatasetNumber(context);
}
@Override
- public void clear() {
- mPrefs.edit().remove(CLIENT_FORM_DATA_KEY).apply();
+ public void clear(Context context) {
+ context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .edit()
+ .remove(CLIENT_FORM_DATA_KEY)
+ .remove(DATASET_NUMBER_KEY)
+ .apply();
}
- private Set<String> getAllAutofillDataStringSet() {
- return mPrefs.getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet<String>());
+ private Set<String> getAllAutofillDataStringSet(Context context) {
+ return context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet<String>());
}
- private void saveAllAutofillDataStringSet(Set<String> allAutofillDataStringSet) {
- mPrefs.edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply();
+ private void saveAllAutofillDataStringSet(Context context,
+ Set<String> allAutofillDataStringSet) {
+ context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .edit()
+ .putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet)
+ .apply();
}
/**
* For simplicity, datasets will be named in the form "dataset-X" where X means
* this was the Xth dataset saved.
*/
- private int getDatasetNumber() {
- return mPrefs.getInt(DATASET_NUMBER_KEY, 0);
+ private int getDatasetNumber(Context context) {
+ return context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .getInt(DATASET_NUMBER_KEY, 0);
}
/**
* Every time a dataset is saved, this should be called to increment the dataset number.
* (only important for this service's dataset naming scheme).
*/
- private void incrementDatasetNumber() {
- mPrefs.edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber() + 1).apply();
+ private void incrementDatasetNumber(Context context) {
+ context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .edit()
+ .putInt(DATASET_NUMBER_KEY, getDatasetNumber(context) + 1)
+ .apply();
}
} \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java
new file mode 100644
index 00000000..b7bb5828
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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 com.example.android.autofillframework.multidatasetservice.datasource;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import static com.example.android.autofillframework.CommonUtil.TAG;
+
+public class SharedPrefsPackageVerificationRepository implements PackageVerificationDataSource {
+
+ private static final String SHARED_PREF_KEY = "com.example.android.autofillframework"
+ + ".multidatasetservice.datasource.PackageVerificationDataSource";
+ private static PackageVerificationDataSource sInstance;
+
+ private SharedPrefsPackageVerificationRepository() {
+ }
+
+ public static PackageVerificationDataSource getInstance() {
+ if (sInstance == null) {
+ sInstance = new SharedPrefsPackageVerificationRepository();
+ }
+ return sInstance;
+ }
+
+ @Override
+ public void clear(Context context) {
+ context.getApplicationContext().getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .edit()
+ .clear()
+ .apply();
+ }
+
+ @Override
+ public boolean putPackageSignatures(Context context, String packageName) {
+ String hash;
+ try {
+ hash = getCertificateHash(context, packageName);
+ Log.d(TAG, "Hash for " + packageName + ": " + hash);
+ } catch (Exception e) {
+ Log.w(TAG, "Error getting hash for " + packageName + ": " + e);
+ return false;
+ }
+
+ if (!containsSignatureForPackage(context, packageName)) {
+ // Storage does not yet contain signature for this package name.
+ context.getApplicationContext()
+ .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+ .edit()
+ .putString(packageName, hash)
+ .apply();
+ return true;
+ }
+ return containsMatchingSignatureForPackage(context, packageName, hash);
+ }
+
+ private boolean containsSignatureForPackage(Context context, String packageName) {
+ SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
+ SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ return prefs.contains(packageName);
+ }
+
+ private boolean containsMatchingSignatureForPackage(Context context, String packageName,
+ String hash) {
+ SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
+ SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ return hash.equals(prefs.getString(packageName, null));
+ }
+
+ private String getCertificateHash(Context context, String packageName)
+ throws Exception {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ Signature[] signatures = packageInfo.signatures;
+ byte[] cert = signatures[0].toByteArray();
+ try (InputStream input = new ByteArrayInputStream(cert)) {
+ CertificateFactory factory = CertificateFactory.getInstance("X509");
+ X509Certificate x509 = (X509Certificate) factory.generateCertificate(input);
+ MessageDigest md = MessageDigest.getInstance("SHA256");
+ byte[] publicKey = md.digest(x509.getEncoded());
+ return toHexFormat(publicKey);
+ }
+ }
+
+ private String toHexFormat(byte[] bytes) {
+ StringBuilder builder = new StringBuilder(bytes.length * 2);
+ for (int i = 0; i < bytes.length; i++) {
+ String hex = Integer.toHexString(bytes[i]);
+ int length = hex.length();
+ if (length == 1) {
+ hex = "0" + hex;
+ }
+ if (length > 2) {
+ hex = hex.substring(length - 2, length);
+ }
+ builder.append(hex.toUpperCase());
+ if (i < (bytes.length - 1)) {
+ builder.append(':');
+ }
+ }
+ return builder.toString();
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
index fccb3b6c..d50af538 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
@@ -78,6 +78,7 @@ public final class FilledAutofillFieldCollection {
* Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId}
* in a {@code AutofillFieldMetadataCollection}.
*
+ * <p>
* In other words, it constructs an autofill
* {@link Dataset.Builder} by applying saved values (from this {@code FilledAutofillFieldCollection})
* to Views specified in a {@code AutofillFieldMetadataCollection}, which represents the current
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
index bb6e8314..34d63ef3 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
@@ -22,12 +22,9 @@ import android.service.autofill.FillResponse;
import android.support.annotation.NonNull;
public class MyPreferences {
- private static final String TAG = "MyPreferences";
-
private static final String RESPONSE_AUTH_KEY = "response_auth";
private static final String DATASET_AUTH_KEY = "dataset_auth";
private static final String MASTER_PASSWORD_KEY = "master_password";
-
private static MyPreferences sInstance;
private final SharedPreferences mPrefs;
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
index f013f785..5224bea3 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
@@ -30,6 +30,7 @@ import android.widget.TextView;
import com.example.android.autofillframework.R;
import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
+import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
public class SettingsActivity extends AppCompatActivity {
@@ -90,10 +91,10 @@ public class SettingsActivity extends AppCompatActivity {
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- SharedPrefsAutofillRepository.getInstance
- (SettingsActivity.this).clear();
- MyPreferences.getInstance(SettingsActivity.this)
- .clearCredentials();
+ SharedPrefsAutofillRepository.getInstance().clear(SettingsActivity.this);
+ SharedPrefsPackageVerificationRepository.getInstance()
+ .clear(SettingsActivity.this);
+ MyPreferences.getInstance(SettingsActivity.this).clearCredentials();
dialog.dismiss();
}
})
@@ -149,7 +150,7 @@ public class SettingsActivity extends AppCompatActivity {
private void setupSettingsSwitch(int containerId, int labelId, int switchId, boolean checked,
CompoundButton.OnCheckedChangeListener checkedChangeListener) {
- ViewGroup container = (ViewGroup) findViewById(containerId);
+ ViewGroup container = findViewById(containerId);
String switchLabel = ((TextView) container.findViewById(labelId)).getText().toString();
final Switch switchView = container.findViewById(switchId);
switchView.setContentDescription(switchLabel);
@@ -165,10 +166,11 @@ public class SettingsActivity extends AppCompatActivity {
private void setupSettingsButton(int containerId, int labelId, int imageViewId,
final View.OnClickListener onClickListener) {
- ViewGroup container = (ViewGroup) findViewById(containerId);
- String buttonLabel = ((TextView) container.findViewById(labelId)).getText().toString();
- final ImageView imageView = container.findViewById(imageViewId);
- imageView.setContentDescription(buttonLabel);
+ ViewGroup container = findViewById(containerId);
+ TextView buttonLabel = container.findViewById(labelId);
+ String buttonLabelText = buttonLabel.getText().toString();
+ ImageView imageView = container.findViewById(imageViewId);
+ imageView.setContentDescription(buttonLabelText);
container.setOnClickListener(onClickListener);
}
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
index 89ec9d4b..16a87813 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
@@ -66,6 +66,7 @@
<string name="submit_label">Submit</string>
<string name="cc_exp_month_description">Credit Card Expiration Month</string>
<string name="cc_exp_year_description">Credit Card Expiration Year</string>
+ <string name="invalid_package_signature">Invalid package signature</string>
<string name="edittext_login_info">This is a sample login page that uses standard EditTexts
from the UI toolkit. EditTexts are already optimized for autofill so extra autofill-specific
code is almost never needed.
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt
index f74c5873..704d17a1 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt
@@ -23,19 +23,24 @@ import android.service.autofill.FillResponse
import android.service.autofill.SaveCallback
import android.service.autofill.SaveRequest
import android.util.Log
-import android.view.autofill.AutofillId
+import android.widget.Toast
import com.example.android.autofillframework.CommonUtil.TAG
import com.example.android.autofillframework.CommonUtil.bundleToString
import com.example.android.autofillframework.R
import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository
import com.example.android.autofillframework.multidatasetservice.settings.MyPreferences
-import java.util.Arrays
class MyAutofillService : AutofillService() {
override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal,
callback: FillCallback) {
- val structure = request.getFillContexts().get(request.getFillContexts().size - 1).structure
+ val structure = request.fillContexts[request.fillContexts.size - 1].structure
+ val packageName = structure.activityComponent.packageName
+ if (!PackageVerifier.isValidPackage(applicationContext, packageName)) {
+ Toast.makeText(applicationContext, R.string.invalid_package_signature,
+ Toast.LENGTH_SHORT).show()
+ return
+ }
val data = request.clientState
Log.d(TAG, "onFillRequest(): data=" + bundleToString(data))
cancellationSignal.setOnCancelListener { Log.w(TAG, "Cancel autofill not implemented in this sample.") }
@@ -68,6 +73,12 @@ class MyAutofillService : AutofillService() {
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
val context = request.fillContexts
val structure = context[context.size - 1].structure
+ val packageName = structure.activityComponent.packageName
+ if (!PackageVerifier.isValidPackage(applicationContext, packageName)) {
+ Toast.makeText(applicationContext, R.string.invalid_package_signature,
+ Toast.LENGTH_SHORT).show()
+ return
+ }
val data = request.clientState
Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data))
val parser = StructureParser(structure)
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt
new file mode 100644
index 00000000..d059a233
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt
@@ -0,0 +1,98 @@
+package com.example.android.autofillframework.multidatasetservice
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.util.Log
+import com.example.android.autofillframework.CommonUtil.TAG
+import java.io.ByteArrayInputStream
+import java.security.MessageDigest
+import java.security.cert.CertificateFactory
+import java.security.cert.X509Certificate
+
+object PackageVerifier {
+
+
+ /**
+ * Verifies if a package is valid by matching its certificate with the previously stored
+ * certificate.
+ */
+ fun isValidPackage(context: Context, packageName: String): Boolean {
+ val hash: String
+ try {
+ hash = getCertificateHash(context, packageName)
+ Log.d(TAG, "Hash for $packageName: $hash")
+ } catch (e: Exception) {
+ Log.w(TAG, "Error getting hash for $packageName: $e")
+ return false
+ }
+
+ return verifyHash(context, packageName, hash)
+ }
+
+ private fun getCertificateHash(context: Context, packageName: String): String {
+ val pm = context.packageManager
+ val packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
+ val signatures = packageInfo.signatures
+ val cert = signatures[0].toByteArray()
+ ByteArrayInputStream(cert).use { input ->
+ val factory = CertificateFactory.getInstance("X509")
+ val x509 = factory.generateCertificate(input) as X509Certificate
+ val md = MessageDigest.getInstance("SHA256")
+ val publicKey = md.digest(x509.encoded)
+ return toHexFormat(publicKey)
+ }
+ }
+
+ private fun toHexFormat(bytes: ByteArray): String {
+ val builder = StringBuilder(bytes.size * 2)
+ for (i in bytes.indices) {
+ var hex = Integer.toHexString(bytes[i].toInt())
+ val length = hex.length
+ if (length == 1) {
+ hex = "0" + hex
+ }
+ if (length > 2) {
+ hex = hex.substring(length - 2, length)
+ }
+ builder.append(hex.toUpperCase())
+ if (i < bytes.size - 1) {
+ builder.append(':')
+ }
+ }
+ return builder.toString()
+ }
+
+ private fun verifyHash(context: Context, packageName: String, hash: String): Boolean {
+ val prefs = context.applicationContext.getSharedPreferences(
+ "package-hashes", Context.MODE_PRIVATE)
+ if (!prefs.contains(packageName)) {
+ Log.d(TAG, "Creating intial hash for " + packageName)
+ prefs.edit().putString(packageName, hash).apply()
+ return true
+ }
+
+ val existingHash = prefs.getString(packageName, null)
+ if (hash != existingHash) {
+ Log.w(TAG, "hash mismatch for " + packageName + ": expected " + existingHash
+ + ", got " + hash)
+ return false
+ }
+ return true
+ }
+} \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
index 1eca8c1e..5a370190 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
@@ -60,6 +60,7 @@
<string name="submit_label">Submit</string>
<string name="cc_exp_month_description">Credit Card Expiration Month</string>
<string name="cc_exp_year_description">Credit Card Expiration Year</string>
+ <string name="invalid_package_signature">Invalid package signature</string>
<string name="edittext_login_info">This is a sample login page that uses standard EditTexts
from the UI toolkit. EditTexts are already optimized for autofill so extra autofill-specific
code is almost never needed.