diff options
author | Douglas Sigelbaum <sigelbaum@google.com> | 2017-08-31 16:42:37 -0700 |
---|---|---|
committer | Douglas Sigelbaum <sigelbaum@google.com> | 2017-09-13 15:24:11 -0700 |
commit | 96be336b4584215c04e49de7e27e5b15648fb496 (patch) | |
tree | 65cf928eb734eb2316963e3058c00d28cfa8bf33 /input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android | |
parent | 03c96abdc9195b6be58765156002812ecee91ab9 (diff) | |
download | android-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
Diffstat (limited to 'input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android')
2 files changed, 112 insertions, 3 deletions
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 |