aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2013-12-30 19:16:21 +0100
committerDominik Schürmann <dominik@dominikschuermann.de>2013-12-30 19:16:21 +0100
commit7c3a53d1496a376bdaaa3bf7c79c77fe6d90a3d2 (patch)
tree203735e739e46024d6084d7497e1bd6ad561e49d /OpenPGP-Keychain
parent3235201cf3f62b345787f21d67dfd259d924b880 (diff)
downloadopen-keychain-7c3a53d1496a376bdaaa3bf7c79c77fe6d90a3d2.tar.gz
open-keychain-7c3a53d1496a376bdaaa3bf7c79c77fe6d90a3d2.tar.bz2
open-keychain-7c3a53d1496a376bdaaa3bf7c79c77fe6d90a3d2.zip
remote service: package signature verification, use string for service instead of getClass.getName
Diffstat (limited to 'OpenPGP-Keychain')
-rw-r--r--OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml219
-rw-r--r--OpenPGP-Keychain/res/values/strings.xml4
-rw-r--r--OpenPGP-Keychain/src/org/openintents/openpgp/IOpenPgpService.aidl1
-rw-r--r--OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpConstants.java10
-rw-r--r--OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpHelper.java2
-rw-r--r--OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpListPreference.java31
-rw-r--r--OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpServiceConnection.java32
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java1
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java13
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java25
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java10
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettings.java12
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java23
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java142
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java4
15 files changed, 349 insertions, 180 deletions
diff --git a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
index 81b48be72..a40444e0f 100644
--- a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
@@ -1,122 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
- <RelativeLayout
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:gravity="center_horizontal"
- android:orientation="horizontal"
- android:paddingBottom="3dip" >
-
- <ImageView
- android:id="@+id/api_app_settings_app_icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentTop="true"
- android:layout_marginRight="6dp"
- android:src="@drawable/icon" />
-
- <TextView
- android:id="@+id/api_app_settings_app_name"
- android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:paddingBottom="3dip" >
+
+ <ImageView
+ android:id="@+id/api_app_settings_app_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="6dp"
+ android:src="@drawable/icon" />
+
+ <TextView
+ android:id="@+id/api_app_settings_app_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/api_app_settings_app_icon"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:text="Name (set in-code)"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@+id/api_app_settings_app_icon"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:text="Name (set in-code)"
- android:textAppearance="?android:attr/textAppearanceMedium" />
- </RelativeLayout>
+ android:orientation="horizontal" >
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ <Button
+ android:id="@+id/api_app_settings_select_key_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/api_settings_select_key" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp" >
+
+ <TextView
+ android:id="@+id/api_app_settings_user_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="@string/api_settings_no_key"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/api_app_settings_user_id_rest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ </LinearLayout>
<Button
- android:id="@+id/api_app_settings_select_key_button"
- android:layout_width="wrap_content"
+ android:id="@+id/api_app_settings_advanced_button"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/api_settings_select_key" />
+ android:text="@string/api_settings_show_advanced" />
<LinearLayout
- android:layout_width="fill_parent"
+ android:id="@+id/api_app_settings_advanced"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:paddingLeft="16dp" >
+ android:visibility="gone" >
<TextView
- android:id="@+id/api_app_settings_user_id"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/api_settings_no_key"
+ android:text="@string/label_encryption_algorithm"
android:textAppearance="?android:attr/textAppearanceMedium" />
+ <Spinner
+ android:id="@+id/api_app_settings_encryption_algorithm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
<TextView
- android:id="@+id/api_app_settings_user_id_rest"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:ellipsize="end"
- android:singleLine="true"
- android:text=""
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
- </LinearLayout>
-
- <Button
- android:id="@+id/api_app_settings_advanced_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/api_settings_show_advanced" />
+ android:text="@string/label_hash_algorithm"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <LinearLayout
- android:id="@+id/api_app_settings_advanced"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible" >
+ <Spinner
+ android:id="@+id/api_app_settings_hash_algorithm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/label_encryption_algorithm"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/label_message_compression"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <Spinner
- android:id="@+id/api_app_settings_encryption_algorithm"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <Spinner
+ android:id="@+id/api_app_settings_compression"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/label_hash_algorithm"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/api_settings_package_name"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <Spinner
- android:id="@+id/api_app_settings_hash_algorithm"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/api_app_settings_package_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="com.example"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/label_message_compression"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/api_settings_package_signature"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
- <Spinner
- android:id="@+id/api_app_settings_compression"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/api_app_settings_package_signature"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Base64 encoded signature"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 63f267277..da82e7553 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -313,7 +313,7 @@
<string name="import_qr_code_missing">Missing QR Codes: %1$s</string>
<string name="import_qr_code_wrong">QR Code malformed! Please try again!</string>
<string name="import_qr_code_finished">QR Code scanning finished!</string>
-
+
<!-- Intent labels -->
<string name="intent_decrypt_file">OpenPGP: Decrypt File</string>
<string name="intent_import_key">OpenPGP: Import Key</string>
@@ -329,6 +329,8 @@
<string name="api_settings_save">Save</string>
<string name="api_settings_cancel">Cancel</string>
<string name="api_settings_revoke">Revoke access</string>
+ <string name="api_settings_package_name">Package Name</string>
+ <string name="api_settings_package_signature">SHA-256 of Package Signature</string>
<string name="api_register_text">The following application requests access to OpenPGP Keychain\'s API.\n\nAllow permanent access?</string>
<string name="api_register_allow">Allow access</string>
<string name="api_register_disallow">Disallow access</string>
diff --git a/OpenPGP-Keychain/src/org/openintents/openpgp/IOpenPgpService.aidl b/OpenPGP-Keychain/src/org/openintents/openpgp/IOpenPgpService.aidl
index 7cbf96b56..8f9e8a0fd 100644
--- a/OpenPGP-Keychain/src/org/openintents/openpgp/IOpenPgpService.aidl
+++ b/OpenPGP-Keychain/src/org/openintents/openpgp/IOpenPgpService.aidl
@@ -50,7 +50,6 @@ interface IOpenPgpService {
*/
oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
-
/**
* Encrypt
*
diff --git a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpConstants.java b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpConstants.java
new file mode 100644
index 000000000..b1ca1bfe6
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpConstants.java
@@ -0,0 +1,10 @@
+package org.openintents.openpgp;
+
+public class OpenPgpConstants {
+
+ public static final String TAG = "OpenPgp API";
+
+ public static final int REQUIRED_API_VERSION = 1;
+ public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
+
+}
diff --git a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpHelper.java b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpHelper.java
index 56c4a4dca..7305c47ce 100644
--- a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpHelper.java
+++ b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpHelper.java
@@ -40,7 +40,7 @@ public class OpenPgpHelper {
}
public boolean isAvailable() {
- Intent intent = new Intent(IOpenPgpService.class.getName());
+ Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
if (!resInfo.isEmpty()) {
return true;
diff --git a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpListPreference.java b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpListPreference.java
index 551401b18..4fef3f32c 100644
--- a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpListPreference.java
+++ b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpListPreference.java
@@ -39,22 +39,19 @@ public class OpenPgpListPreference extends DialogPreference {
ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
private String mSelectedPackage;
- public static final int REQUIRED_API_VERSION = 1;
-
public OpenPgpListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- List<ResolveInfo> resInfo =
- context.getPackageManager().queryIntentServices(
- new Intent(IOpenPgpService.class.getName()), PackageManager.GET_META_DATA);
+ List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(
+ new Intent(OpenPgpConstants.SERVICE_INTENT), PackageManager.GET_META_DATA);
if (!resInfo.isEmpty()) {
for (ResolveInfo resolveInfo : resInfo) {
if (resolveInfo.serviceInfo == null)
continue;
String packageName = resolveInfo.serviceInfo.packageName;
- String simpleName = String.valueOf(resolveInfo.serviceInfo
- .loadLabel(context.getPackageManager()));
+ String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(context
+ .getPackageManager()));
Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager());
// get api version
@@ -95,8 +92,8 @@ public class OpenPgpListPreference extends DialogPreference {
TextView tv = (TextView) v.findViewById(android.R.id.text1);
// Put the image on the TextView
- tv.setCompoundDrawablesWithIntrinsicBounds(mProviderList.get(position).icon,
- null, null, null);
+ tv.setCompoundDrawablesWithIntrinsicBounds(mProviderList.get(position).icon, null,
+ null, null);
// Add margin between image and text (support various screen
// densities)
@@ -104,13 +101,12 @@ public class OpenPgpListPreference extends DialogPreference {
tv.setCompoundDrawablePadding(dp5);
// disable if it has the wrong api_version
- if (mProviderList.get(position).apiVersion == REQUIRED_API_VERSION) {
+ if (mProviderList.get(position).apiVersion == OpenPgpConstants.REQUIRED_API_VERSION) {
tv.setEnabled(true);
} else {
tv.setEnabled(false);
- tv.setText(tv.getText() + " (API v"
- + mProviderList.get(position).apiVersion + ", needs v"
- + REQUIRED_API_VERSION + ")");
+ tv.setText(tv.getText() + " (API v" + mProviderList.get(position).apiVersion
+ + ", needs v" + OpenPgpConstants.REQUIRED_API_VERSION + ")");
}
return v;
@@ -125,8 +121,8 @@ public class OpenPgpListPreference extends DialogPreference {
mSelectedPackage = mProviderList.get(which).packageName;
/*
- * Clicking on an item simulates the positive button
- * click, and dismisses the dialog.
+ * Clicking on an item simulates the positive button click, and dismisses
+ * the dialog.
*/
OpenPgpListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
@@ -134,9 +130,8 @@ public class OpenPgpListPreference extends DialogPreference {
});
/*
- * The typical interaction for list-based dialogs is to have
- * click-on-an-item dismiss the dialog instead of the user having to
- * press 'Ok'.
+ * The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
+ * dialog instead of the user having to press 'Ok'.
*/
builder.setPositiveButton(null, null);
}
diff --git a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpServiceConnection.java b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpServiceConnection.java
index 56a922d73..f7ba06aaf 100644
--- a/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpServiceConnection.java
+++ b/OpenPGP-Keychain/src/org/openintents/openpgp/OpenPgpServiceConnection.java
@@ -29,14 +29,12 @@ public class OpenPgpServiceConnection {
private Context mApplicationContext;
private IOpenPgpService mService;
- private boolean bound;
- private String cryptoProviderPackageName;
-
- private static final String TAG = "OpenPgpServiceConnection";
+ private boolean mBound;
+ private String mCryptoProviderPackageName;
public OpenPgpServiceConnection(Context context, String cryptoProviderPackageName) {
- mApplicationContext = context.getApplicationContext();
- this.cryptoProviderPackageName = cryptoProviderPackageName;
+ this.mApplicationContext = context.getApplicationContext();
+ this.mCryptoProviderPackageName = cryptoProviderPackageName;
}
public IOpenPgpService getService() {
@@ -44,20 +42,20 @@ public class OpenPgpServiceConnection {
}
public boolean isBound() {
- return bound;
+ return mBound;
}
private ServiceConnection mCryptoServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IOpenPgpService.Stub.asInterface(service);
- Log.d(TAG, "connected to service");
- bound = true;
+ Log.d(OpenPgpConstants.TAG, "connected to service");
+ mBound = true;
}
public void onServiceDisconnected(ComponentName name) {
mService = null;
- Log.d(TAG, "disconnected from service");
- bound = false;
+ Log.d(OpenPgpConstants.TAG, "disconnected from service");
+ mBound = false;
}
};
@@ -67,23 +65,23 @@ public class OpenPgpServiceConnection {
* @return
*/
public boolean bindToService() {
- if (mService == null && !bound) { // if not already connected
+ if (mService == null && !mBound) { // if not already connected
try {
- Log.d(TAG, "not bound yet");
+ Log.d(OpenPgpConstants.TAG, "not bound yet");
Intent serviceIntent = new Intent();
serviceIntent.setAction(IOpenPgpService.class.getName());
- serviceIntent.setPackage(cryptoProviderPackageName);
+ serviceIntent.setPackage(mCryptoProviderPackageName);
mApplicationContext.bindService(serviceIntent, mCryptoServiceConnection,
Context.BIND_AUTO_CREATE);
return true;
} catch (Exception e) {
- Log.d(TAG, "Exception", e);
+ Log.d(OpenPgpConstants.TAG, "Exception on binding", e);
return false;
}
- } else { // already connected
- Log.d(TAG, "already bound... ");
+ } else {
+ Log.d(OpenPgpConstants.TAG, "already bound");
return true;
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
index ea4ca377c..82bb473f6 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -55,6 +55,7 @@ public class KeychainContract {
interface ApiAppsColumns {
String PACKAGE_NAME = "package_name";
+ String PACKAGE_SIGNATURE = "package_signature";
String KEY_ID = "key_id"; // not a database id
String ENCRYPTION_ALGORITHM = "encryption_algorithm";
String HASH_ALORITHM = "hash_algorithm";
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 0f962967d..60c5c91a8 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -31,7 +31,7 @@ import android.provider.BaseColumns;
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "apg.db";
- private static final int DATABASE_VERSION = 5;
+ private static final int DATABASE_VERSION = 6;
public interface Tables {
String KEY_RINGS = "key_rings";
@@ -66,9 +66,10 @@ public class KeychainDatabase extends SQLiteOpenHelper {
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " + ApiAppsColumns.KEY_ID + " INT64, "
- + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + ApiAppsColumns.HASH_ALORITHM
- + " INTEGER, " + ApiAppsColumns.COMPRESSION + " INTEGER)";
+ + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " + ApiAppsColumns.PACKAGE_SIGNATURE
+ + " BLOB, " + ApiAppsColumns.KEY_ID + " INT64, " + ApiAppsColumns.ENCRYPTION_ALGORITHM
+ + " INTEGER, " + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
+ + ApiAppsColumns.COMPRESSION + " INTEGER)";
KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -110,6 +111,10 @@ public class KeychainDatabase extends SQLiteOpenHelper {
break;
case 4:
db.execSQL(CREATE_API_APPS);
+ case 5:
+ // new column: package_signature
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
+ db.execSQL(CREATE_API_APPS);
default:
break;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 7ef61c15b..f12048277 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -742,6 +742,7 @@ public class ProviderHelper {
private static ContentValues contentValueForApiApps(AppSettings appSettings) {
ContentValues values = new ContentValues();
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
+ values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature());
values.put(ApiApps.KEY_ID, appSettings.getKeyId());
values.put(ApiApps.COMPRESSION, appSettings.getCompression());
values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm());
@@ -770,6 +771,8 @@ public class ProviderHelper {
settings = new AppSettings();
settings.setPackageName(cur.getString(cur
.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+ settings.setPackageSignature(cur.getBlob(cur
+ .getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID)));
settings.setCompression(cur.getInt(cur
.getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION)));
@@ -781,4 +784,26 @@ public class ProviderHelper {
return settings;
}
+
+ public static byte[] getApiAppSignature(Context context, String packageName) {
+ Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName);
+
+ String[] projection = new String[] { ApiApps.PACKAGE_SIGNATURE };
+
+ ContentResolver cr = context.getContentResolver();
+ Cursor cursor = cr.query(queryUri, projection, null, null, null);
+
+ byte[] signature = null;
+ if (cursor != null && cursor.moveToFirst()) {
+ int signatureCol = 0;
+
+ signature = cursor.getBlob(signatureCol);
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ return signature;
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java
new file mode 100644
index 000000000..cef002265
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/exception/WrongPackageSignatureException.java
@@ -0,0 +1,10 @@
+package org.sufficientlysecure.keychain.service.exception;
+
+public class WrongPackageSignatureException extends Exception {
+
+ private static final long serialVersionUID = -8294642703122196028L;
+
+ public WrongPackageSignatureException(String message) {
+ super(message);
+ }
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettings.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettings.java
index 381a4065c..9da4c8392 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettings.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettings.java
@@ -23,6 +23,7 @@ import org.sufficientlysecure.keychain.Id;
public class AppSettings {
private String packageName;
+ private byte[] packageSignature;
private long keyId = Id.key.none;
private int encryptionAlgorithm;
private int hashAlgorithm;
@@ -32,9 +33,10 @@ public class AppSettings {
}
- public AppSettings(String packageName) {
+ public AppSettings(String packageName, byte[] packageSignature) {
super();
this.packageName = packageName;
+ this.packageSignature = packageSignature;
// defaults:
this.encryptionAlgorithm = PGPEncryptedData.AES_256;
this.hashAlgorithm = HashAlgorithmTags.SHA512;
@@ -49,6 +51,14 @@ public class AppSettings {
this.packageName = packageName;
}
+ public byte[] getPackageSignature() {
+ return packageSignature;
+ }
+
+ public void setPackageSignature(byte[] packageSignature) {
+ this.packageSignature = packageSignature;
+ }
+
public long getKeyId() {
return keyId;
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
index 942f8eba8..e592f5d57 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
@@ -17,8 +17,12 @@
package org.sufficientlysecure.keychain.service.remote;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -67,6 +71,8 @@ public class AppSettingsFragment extends Fragment {
private Spinner mEncryptionAlgorithm;
private Spinner mHashAlgorithm;
private Spinner mCompression;
+ private TextView mPackageName;
+ private TextView mPackageSignature;
KeyValueSpinnerAdapter encryptionAdapter;
KeyValueSpinnerAdapter hashAdapter;
@@ -79,6 +85,19 @@ public class AppSettingsFragment extends Fragment {
public void setAppSettings(AppSettings appSettings) {
this.appSettings = appSettings;
setPackage(appSettings.getPackageName());
+ mPackageName.setText(appSettings.getPackageName());
+
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(appSettings.getPackageSignature());
+ byte[] digest = md.digest();
+ String signature = new String(Hex.encode(digest));
+
+ mPackageSignature.setText(signature);
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Constants.TAG, "Should not happen!", e);
+ }
+
updateSelectedKeyView(appSettings.getKeyId());
mEncryptionAlgorithm.setSelection(encryptionAdapter.getPosition(appSettings
.getEncryptionAlgorithm()));
@@ -110,6 +129,8 @@ public class AppSettingsFragment extends Fragment {
.findViewById(R.id.api_app_settings_encryption_algorithm);
mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression);
+ mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
+ mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
@@ -182,7 +203,7 @@ public class AppSettingsFragment extends Fragment {
public void onClick(View v) {
if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.INVISIBLE);
+ mAdvancedSettingsContainer.setVisibility(View.GONE);
mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced);
} else {
mAdvancedSettingsContainer.startAnimation(visibleAnimation);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java
index 4e8c4678a..0f28d96f6 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteService.java
@@ -18,18 +18,24 @@
package org.sufficientlysecure.keychain.service.remote;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.exception.WrongPackageSignatureException;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -38,7 +44,7 @@ import android.os.Message;
import android.os.Messenger;
/**
- * Abstract service for remote APIs that handle app registration and user input.
+ * Abstract service class for remote APIs that handle app registration and user input.
*/
public abstract class RemoteService extends Service {
Context mContext;
@@ -98,32 +104,57 @@ public abstract class RemoteService extends Service {
* @param r
*/
protected void checkAndEnqueue(Runnable r) {
- if (isCallerAllowed(false)) {
- mThreadPool.execute(r);
-
- Log.d(Constants.TAG, "Enqueued runnable…");
- } else {
- String[] callingPackages = getPackageManager()
- .getPackagesForUid(Binder.getCallingUid());
-
- Log.e(Constants.TAG, "Not allowed to use service! Starting activity for registration!");
- Bundle extras = new Bundle();
- // TODO: currently simply uses first entry
- extras.putString(RemoteServiceActivity.EXTRA_PACKAGE_NAME, callingPackages[0]);
-
- RegisterActivityCallback callback = new RegisterActivityCallback();
-
- pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_REGISTER, callback, extras);
-
- if (callback.isAllowed()) {
+ try {
+ if (isCallerAllowed(false)) {
mThreadPool.execute(r);
+
Log.d(Constants.TAG, "Enqueued runnable…");
} else {
- Log.d(Constants.TAG, "User disallowed app!");
+ String[] callingPackages = getPackageManager().getPackagesForUid(
+ Binder.getCallingUid());
+ // TODO: currently simply uses first entry
+ String packageName = callingPackages[0];
+
+ byte[] packageSignature;
+ try {
+ packageSignature = getPackageSignature(packageName);
+ } catch (NameNotFoundException e) {
+ Log.e(Constants.TAG, "Should not happen, returning!", e);
+ return;
+ }
+ Log.e(Constants.TAG,
+ "Not allowed to use service! Starting activity for registration!");
+ Bundle extras = new Bundle();
+ extras.putString(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
+ extras.putByteArray(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
+ RegisterActivityCallback callback = new RegisterActivityCallback();
+
+ pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_REGISTER, callback,
+ extras);
+
+ if (callback.isAllowed()) {
+ mThreadPool.execute(r);
+ Log.d(Constants.TAG, "Enqueued runnable…");
+ } else {
+ Log.d(Constants.TAG, "User disallowed app!");
+ }
}
+ } catch (WrongPackageSignatureException e) {
+ // TODO: Inform user about wrong signature!
+ Log.e(Constants.TAG, "RemoteService", e);
}
}
+ private byte[] getPackageSignature(String packageName) throws NameNotFoundException {
+ PackageInfo pkgInfo = getPackageManager().getPackageInfo(packageName,
+ PackageManager.GET_SIGNATURES);
+ Signature[] signatures = pkgInfo.signatures;
+ // TODO: Only first signature?!
+ byte[] packageSignature = signatures[0].toByteArray();
+
+ return packageSignature;
+ }
+
/**
* Locks current thread and pauses execution of runnables and starts activity for user input
*
@@ -200,15 +231,20 @@ public abstract class RemoteService extends Service {
packageName = msg.getData().getString(PACKAGE_NAME);
// resume threads
- if (isPackageAllowed(packageName, false)) {
- synchronized (userInputLock) {
- userInputLock.notifyAll();
+ try {
+ if (isPackageAllowed(packageName)) {
+ synchronized (userInputLock) {
+ userInputLock.notifyAll();
+ }
+ mThreadPool.resume();
+ } else {
+ // Should not happen!
+ Log.e(Constants.TAG, "Should not happen! Emergency shutdown!");
+ mThreadPool.shutdownNow();
}
- mThreadPool.resume();
- } else {
- // Should not happen!
- Log.e(Constants.TAG, "Should not happen! Emergency shutdown!");
- mThreadPool.shutdownNow();
+ } catch (WrongPackageSignatureException e) {
+ // TODO: Inform user about wrong signature!
+ Log.e(Constants.TAG, "RemoteService", e);
}
} else {
allowed = false;
@@ -230,15 +266,28 @@ public abstract class RemoteService extends Service {
* @param allowOnlySelf
* allow only Keychain app itself
* @return true if process is allowed to use this service
+ * @throws WrongPackageSignatureException
*/
- private boolean isCallerAllowed(boolean allowOnlySelf) {
- String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ private boolean isCallerAllowed(boolean allowOnlySelf) throws WrongPackageSignatureException {
+ return isUidAllowed(Binder.getCallingUid(), allowOnlySelf);
+ }
+
+ private boolean isUidAllowed(int uid, boolean allowOnlySelf)
+ throws WrongPackageSignatureException {
+ if (android.os.Process.myUid() == uid) {
+ return true;
+ }
+ if (allowOnlySelf) { // barrier
+ return false;
+ }
+
+ String[] callingPackages = getPackageManager().getPackagesForUid(uid);
// is calling package allowed to use this service?
for (int i = 0; i < callingPackages.length; i++) {
String currentPkg = callingPackages[i];
- if (isPackageAllowed(currentPkg, allowOnlySelf)) {
+ if (isPackageAllowed(currentPkg)) {
return true;
}
}
@@ -248,28 +297,39 @@ public abstract class RemoteService extends Service {
}
/**
- * Checks if packageName is a registered app for the API.
+ * Checks if packageName is a registered app for the API. Does not return true for own package!
*
* @param packageName
- * @param allowOnlySelf
- * allow only Keychain app itself
* @return
+ * @throws WrongPackageSignatureException
*/
- private boolean isPackageAllowed(String packageName, boolean allowOnlySelf) {
+ private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException {
Log.d(Constants.TAG, "packageName: " + packageName);
- ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(mContext);
+ ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(this);
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
// check if package is allowed to use our service
- if (allowedPkgs.contains(packageName) && (!allowOnlySelf)) {
+ if (allowedPkgs.contains(packageName)) {
Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
- return true;
- } else if (Constants.PACKAGE_NAME.equals(packageName)) {
- Log.d(Constants.TAG, "Package is OpenPGP Keychain! -> allowed!");
+ // check package signature
+ byte[] currentSig;
+ try {
+ currentSig = getPackageSignature(packageName);
+ } catch (NameNotFoundException e) {
+ throw new WrongPackageSignatureException(e.getMessage());
+ }
- return true;
+ byte[] storedSig = ProviderHelper.getApiAppSignature(this, packageName);
+ if (Arrays.equals(currentSig, storedSig)) {
+ Log.d(Constants.TAG,
+ "Package signature is correct! (equals signature from database)");
+ return true;
+ } else {
+ throw new WrongPackageSignatureException(
+ "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)");
+ }
}
return false;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
index 2c4bb4e97..bba8176b1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
@@ -55,6 +55,7 @@ public class RemoteServiceActivity extends SherlockFragmentActivity {
public static final String EXTRA_SECRET_KEY_ID = "secret_key_id";
// register action
public static final String EXTRA_PACKAGE_NAME = "package_name";
+ public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature";
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
@@ -110,6 +111,7 @@ public class RemoteServiceActivity extends SherlockFragmentActivity {
*/
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow,
@@ -166,7 +168,7 @@ public class RemoteServiceActivity extends SherlockFragmentActivity {
mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
R.id.api_app_settings_fragment);
- AppSettings settings = new AppSettings(packageName);
+ AppSettings settings = new AppSettings(packageName, packageSignature);
mSettingsFragment.setAppSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);