mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-24 01:32:16 -05:00
implemented security model
This commit is contained in:
parent
0f3164c5b5
commit
bbddc4c56d
10
README.md
10
README.md
@ -59,7 +59,7 @@ See http://docs.oseems.com/general/application/eclipse/fix-gc-overhead-limit-exc
|
|||||||
1. Open svg file in Inkscape
|
1. Open svg file in Inkscape
|
||||||
2. Extensions -> Color -> darker (2 times!)
|
2. Extensions -> Color -> darker (2 times!)
|
||||||
|
|
||||||
# Security Concept
|
# Security Model
|
||||||
|
|
||||||
## Basic goals
|
## Basic goals
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ Android primitives to exchange data: Intent, Intent with return values, Send (al
|
|||||||
* DECRYPT
|
* DECRYPT
|
||||||
* DECRYPT_FILE
|
* DECRYPT_FILE
|
||||||
|
|
||||||
### With permission
|
### With permission ACCESS_API
|
||||||
|
|
||||||
* CREATE_KEY
|
* CREATE_KEY
|
||||||
* ENCRYPT_AND_RETURN
|
* ENCRYPT_AND_RETURN
|
||||||
@ -100,9 +100,9 @@ Android primitives to exchange data: Intent, Intent with return values, Send (al
|
|||||||
|
|
||||||
## Remote Service
|
## Remote Service
|
||||||
|
|
||||||
* The whole service requires a permission
|
* The whole service requires the permission ACCESS_API
|
||||||
|
|
||||||
## Resulting permission
|
## Resulting permission
|
||||||
|
|
||||||
* Read key information (not the actual keys)(content provider)
|
* READ_KEY_DATABASE: Read key information (not the actual keys)(content provider)
|
||||||
* Encrypt/Sign/Decrypt/Create keys (intents, remote service) without user interaction
|
* ACCESS_API: Encrypt/Sign/Decrypt/Create keys without user interaction (intents, remote service)
|
@ -53,15 +53,23 @@
|
|||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
|
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
|
||||||
|
|
||||||
|
<permission-group
|
||||||
|
android:name="org.thialfihar.android.apg.permission-group.APG"
|
||||||
|
android:description="@string/permission_group_description"
|
||||||
|
android:icon="@drawable/icon"
|
||||||
|
android:label="@string/permission_group_label" />
|
||||||
|
|
||||||
<permission
|
<permission
|
||||||
android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
|
android:name="org.thialfihar.android.apg.permission.READ_KEY_DATABASE"
|
||||||
android:description="@string/permission_read_key_details_description"
|
android:description="@string/permission_read_key_database_description"
|
||||||
android:label="@string/permission_read_key_details_label"
|
android:label="@string/permission_read_key_database_label"
|
||||||
|
android:permissionGroup="org.thialfihar.android.apg.permission-group.APG"
|
||||||
android:protectionLevel="dangerous" />
|
android:protectionLevel="dangerous" />
|
||||||
<permission
|
<permission
|
||||||
android:name="org.thialfihar.android.apg.permission.STORE_BLOBS"
|
android:name="org.thialfihar.android.apg.permission.ACCESS_API"
|
||||||
android:description="@string/permission_store_blobs_description"
|
android:description="@string/permission_access_api_description"
|
||||||
android:label="@string/permission_store_blobs_label"
|
android:label="@string/permission_access_api_label"
|
||||||
|
android:permissionGroup="org.thialfihar.android.apg.permission-group.APG"
|
||||||
android:protectionLevel="dangerous" />
|
android:protectionLevel="dangerous" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
@ -341,7 +349,7 @@
|
|||||||
android:name=".service.ApgService"
|
android:name=".service.ApgService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
|
android:permission="org.thialfihar.android.apg.permission.ACCESS_API"
|
||||||
android:process=":remote" >
|
android:process=":remote" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.thialfihar.android.apg.service.IApgService" />
|
<action android:name="org.thialfihar.android.apg.service.IApgService" />
|
||||||
@ -353,16 +361,19 @@
|
|||||||
</service>
|
</service>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".provider.ApgProvider"
|
android:name=".provider.ApgProviderInternal"
|
||||||
|
android:authorities="org.thialfihar.android.apg.internal"
|
||||||
|
android:exported="false" />
|
||||||
|
<provider
|
||||||
|
android:name=".provider.ApgProviderExternal"
|
||||||
android:authorities="org.thialfihar.android.apg"
|
android:authorities="org.thialfihar.android.apg"
|
||||||
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" />
|
android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DATABASE" />
|
||||||
|
|
||||||
|
|
||||||
<!-- TODO: authority! -->
|
<!-- TODO: authority! -->
|
||||||
<provider
|
<provider
|
||||||
android:name=".provider.ApgServiceBlobProvider"
|
android:name=".provider.ApgServiceBlobProvider"
|
||||||
android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider"
|
android:authorities="org.thialfihar.android.apg.provider.apgserviceblobprovider"
|
||||||
android:permission="org.thialfihar.android.apg.permission.STORE_BLOBS" />
|
android:permission="org.thialfihar.android.apg.permission.ACCESS_API" />
|
||||||
|
|
||||||
<!-- DEPRECATED: -->
|
<!-- DEPRECATED: -->
|
||||||
<!-- <provider -->
|
<!-- <provider -->
|
||||||
|
@ -298,10 +298,12 @@
|
|||||||
<string name="progress_queryingServer">querying %s…</string>
|
<string name="progress_queryingServer">querying %s…</string>
|
||||||
|
|
||||||
<!-- permission strings -->
|
<!-- permission strings -->
|
||||||
<string name="permission_read_key_details_label">Read key details from APG.</string>
|
<string name="permission_group_label">APG</string>
|
||||||
<string name="permission_read_key_details_description">Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string>
|
<string name="permission_group_description">Permissions to use APG</string>
|
||||||
<string name="permission_store_blobs_label">Store blobs to en/decrypt with APG.</string>
|
<string name="permission_read_key_database_label">Read key details of public and secret keys (The keys themselves can NOT be read.)</string>
|
||||||
<string name="permission_store_blobs_description">Store and read files on the android file system through APG. It cannot read files of other applications.</string>
|
<string name="permission_read_key_database_description">Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string>
|
||||||
|
<string name="permission_access_api_label">Encrypt/Sign/Decrypt/Create keys without user interaction</string>
|
||||||
|
<string name="permission_access_api_description">Encrypt/Sign/Decrypt/Create keys (by using Intents or Remote Service) without user interaction</string>
|
||||||
|
|
||||||
<!-- action strings -->
|
<!-- action strings -->
|
||||||
<string name="action_encrypt">Encrypt</string>
|
<string name="action_encrypt">Encrypt</string>
|
||||||
|
@ -20,11 +20,15 @@ import android.os.Environment;
|
|||||||
|
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
|
|
||||||
|
public static final boolean DEBUG = true;
|
||||||
|
|
||||||
public static final String TAG = "APG";
|
public static final String TAG = "APG";
|
||||||
|
|
||||||
public static final String PACKAGE_NAME = "org.thialfihar.android.apg";
|
public static final String PACKAGE_NAME = "org.thialfihar.android.apg";
|
||||||
|
|
||||||
public static final boolean DEBUG = true;
|
public static final String PERMISSION_ACCESS_KEY_DATABASE = PACKAGE_NAME
|
||||||
|
+ ".permission.ACCESS_KEY_DATABASE";
|
||||||
|
public static final String PERMISSION_ACCESS_API = PACKAGE_NAME + ".permission.ACCESS_API";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
@ -37,7 +41,7 @@ public final class Constants {
|
|||||||
*
|
*
|
||||||
* - even better and shorter (without .android.): org.apg.action.DECRYPT
|
* - even better and shorter (without .android.): org.apg.action.DECRYPT
|
||||||
*/
|
*/
|
||||||
public static final String INTENT_PREFIX = "org.thialfihar.android.apg.intent.";
|
public static final String INTENT_PREFIX = PACKAGE_NAME + ".intent.";
|
||||||
|
|
||||||
public static final class path {
|
public static final class path {
|
||||||
public static final String APP_DIR = Environment.getExternalStorageDirectory() + "/APG";
|
public static final String APP_DIR = Environment.getExternalStorageDirectory() + "/APG";
|
||||||
|
@ -23,13 +23,17 @@ import java.util.Iterator;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.thialfihar.android.apg.Constants;
|
import org.thialfihar.android.apg.Constants;
|
||||||
|
import org.thialfihar.android.apg.R;
|
||||||
import org.thialfihar.android.apg.util.Log;
|
import org.thialfihar.android.apg.util.Log;
|
||||||
|
|
||||||
import com.actionbarsherlock.app.ActionBar;
|
import com.actionbarsherlock.app.ActionBar;
|
||||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
public class OtherHelper {
|
public class OtherHelper {
|
||||||
|
|
||||||
@ -117,6 +121,45 @@ public class OtherHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the calling package has the needed permission to invoke an intent with specific
|
||||||
|
* restricted actions.
|
||||||
|
*
|
||||||
|
* If pkgName is null, this will also deny the use of the given action
|
||||||
|
*
|
||||||
|
* @param activity
|
||||||
|
* @param pkgName
|
||||||
|
* @param permName
|
||||||
|
* @param action
|
||||||
|
* @param restrictedActions
|
||||||
|
*/
|
||||||
|
public static void checkPackagePermissionForActions(Activity activity, String pkgName,
|
||||||
|
String permName, String action, String[] restrictedActions) {
|
||||||
|
if (action != null) {
|
||||||
|
PackageManager pkgManager = activity.getPackageManager();
|
||||||
|
|
||||||
|
for (int i = 0; i < restrictedActions.length; i++) {
|
||||||
|
if (restrictedActions[i].equals(action)) {
|
||||||
|
if (pkgName != null
|
||||||
|
&& pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action "
|
||||||
|
+ action + " was granted!");
|
||||||
|
} else {
|
||||||
|
String error = pkgName + " does NOT have permission " + permName + ". Action "
|
||||||
|
+ action + " was NOT granted!";
|
||||||
|
Log.e(Constants.TAG, error);
|
||||||
|
Toast.makeText(activity, activity.getString(R.string.errorMessage, error),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
// end activity
|
||||||
|
activity.setResult(Activity.RESULT_CANCELED, null);
|
||||||
|
activity.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits userId string into naming part and email part
|
* Splits userId string into naming part and email part
|
||||||
*
|
*
|
||||||
|
@ -57,9 +57,11 @@ public class ApgContract {
|
|||||||
public static final int SECRET = 1;
|
public static final int SECRET = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME;
|
public static final String CONTENT_AUTHORITY_EXTERNAL = Constants.PACKAGE_NAME;
|
||||||
|
public static final String CONTENT_AUTHORITY_INTERNAL = Constants.PACKAGE_NAME + ".internal";
|
||||||
|
|
||||||
private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
|
private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://"
|
||||||
|
+ CONTENT_AUTHORITY_INTERNAL);
|
||||||
|
|
||||||
public static final String BASE_KEY_RINGS = "key_rings";
|
public static final String BASE_KEY_RINGS = "key_rings";
|
||||||
public static final String BASE_DATA = "data";
|
public static final String BASE_DATA = "data";
|
||||||
@ -75,7 +77,7 @@ public class ApgContract {
|
|||||||
public static final String PATH_KEYS = "keys";
|
public static final String PATH_KEYS = "keys";
|
||||||
|
|
||||||
public static class KeyRings implements KeyRingsColumns, BaseColumns {
|
public static class KeyRings implements KeyRingsColumns, BaseColumns {
|
||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_KEY_RINGS).build();
|
.appendPath(BASE_KEY_RINGS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/** Use if multiple items get returned */
|
||||||
@ -132,7 +134,7 @@ public class ApgContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Keys implements KeysColumns, BaseColumns {
|
public static class Keys implements KeysColumns, BaseColumns {
|
||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_KEY_RINGS).build();
|
.appendPath(BASE_KEY_RINGS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/** Use if multiple items get returned */
|
||||||
@ -163,7 +165,7 @@ public class ApgContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class UserIds implements UserIdsColumns, BaseColumns {
|
public static class UserIds implements UserIdsColumns, BaseColumns {
|
||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_KEY_RINGS).build();
|
.appendPath(BASE_KEY_RINGS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/** Use if multiple items get returned */
|
||||||
@ -194,8 +196,8 @@ public class ApgContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class DataStream {
|
public static class DataStream {
|
||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(BASE_DATA)
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.build();
|
.appendPath(BASE_DATA).build();
|
||||||
|
|
||||||
public static Uri buildDataStreamUri(String streamFilename) {
|
public static Uri buildDataStreamUri(String streamFilename) {
|
||||||
return CONTENT_URI.buildUpon().appendPath(streamFilename).build();
|
return CONTENT_URI.buildUpon().appendPath(streamFilename).build();
|
||||||
|
@ -47,8 +47,6 @@ import android.provider.BaseColumns;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
public class ApgProvider extends ContentProvider {
|
public class ApgProvider extends ContentProvider {
|
||||||
private static final UriMatcher sUriMatcher = buildUriMatcher();
|
|
||||||
|
|
||||||
private static final int PUBLIC_KEY_RING = 101;
|
private static final int PUBLIC_KEY_RING = 101;
|
||||||
private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102;
|
private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102;
|
||||||
private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103;
|
private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103;
|
||||||
@ -75,13 +73,22 @@ public class ApgProvider extends ContentProvider {
|
|||||||
|
|
||||||
private static final int DATA_STREAM = 301;
|
private static final int DATA_STREAM = 301;
|
||||||
|
|
||||||
|
protected static boolean sInternalProvider;
|
||||||
|
protected static UriMatcher sUriMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by
|
* Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by
|
||||||
* this {@link ContentProvider}.
|
* this {@link ContentProvider}.
|
||||||
*/
|
*/
|
||||||
private static UriMatcher buildUriMatcher() {
|
protected static UriMatcher buildUriMatcher(boolean internalProvider) {
|
||||||
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
|
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
final String authority = ApgContract.CONTENT_AUTHORITY;
|
|
||||||
|
String authority;
|
||||||
|
if (internalProvider) {
|
||||||
|
authority = ApgContract.CONTENT_AUTHORITY_INTERNAL;
|
||||||
|
} else {
|
||||||
|
authority = ApgContract.CONTENT_AUTHORITY_EXTERNAL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public key rings
|
* public key rings
|
||||||
@ -283,8 +290,66 @@ public class ApgProvider extends ContentProvider {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb,
|
/**
|
||||||
HashMap<String, String> projectionMap, int match, boolean isMasterKey, String sortOrder) {
|
* Set result of query to specific columns, don't show blob column for external content provider
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private HashMap<String, String> getProjectionMapForKeyRings() {
|
||||||
|
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
|
||||||
|
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
|
||||||
|
+ KeyRingsColumns.MASTER_KEY_ID);
|
||||||
|
// only give out keyRing blob when we are using the internal content provider
|
||||||
|
if (sInternalProvider) {
|
||||||
|
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
|
||||||
|
+ KeyRingsColumns.KEY_RING_DATA);
|
||||||
|
}
|
||||||
|
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
||||||
|
|
||||||
|
return projectionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set result of query to specific columns, don't show blob column for external content provider
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private HashMap<String, String> getProjectionMapForKeys() {
|
||||||
|
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
projectionMap.put(BaseColumns._ID, BaseColumns._ID);
|
||||||
|
projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID);
|
||||||
|
projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY);
|
||||||
|
projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM);
|
||||||
|
projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE);
|
||||||
|
projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN);
|
||||||
|
projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT);
|
||||||
|
projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED);
|
||||||
|
projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION);
|
||||||
|
projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY);
|
||||||
|
projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID);
|
||||||
|
// only give out keyRing blob when we are using the internal content provider
|
||||||
|
if (sInternalProvider) {
|
||||||
|
projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA);
|
||||||
|
}
|
||||||
|
projectionMap.put(KeysColumns.RANK, KeysColumns.RANK);
|
||||||
|
|
||||||
|
return projectionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds default query for keyRings: KeyRings table is joined with Keys and UserIds
|
||||||
|
*
|
||||||
|
* @param qb
|
||||||
|
* @param match
|
||||||
|
* @param isMasterKey
|
||||||
|
* @param sortOrder
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match,
|
||||||
|
boolean isMasterKey, String sortOrder) {
|
||||||
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
|
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
|
||||||
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
|
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
|
||||||
|
|
||||||
@ -300,14 +365,7 @@ public class ApgProvider extends ContentProvider {
|
|||||||
+ Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND "
|
+ Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND "
|
||||||
+ Tables.USER_IDS + "." + UserIdsColumns.RANK + " = '0')");
|
+ Tables.USER_IDS + "." + UserIdsColumns.RANK + " = '0')");
|
||||||
|
|
||||||
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
|
qb.setProjectionMap(getProjectionMapForKeyRings());
|
||||||
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
|
|
||||||
+ KeyRingsColumns.MASTER_KEY_ID);
|
|
||||||
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
|
|
||||||
+ KeyRingsColumns.KEY_RING_DATA);
|
|
||||||
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
|
||||||
|
|
||||||
qb.setProjectionMap(projectionMap);
|
|
||||||
|
|
||||||
return qb;
|
return qb;
|
||||||
}
|
}
|
||||||
@ -322,14 +380,12 @@ public class ApgProvider extends ContentProvider {
|
|||||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||||
SQLiteDatabase db = mApgDatabase.getReadableDatabase();
|
SQLiteDatabase db = mApgDatabase.getReadableDatabase();
|
||||||
|
|
||||||
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
|
||||||
|
|
||||||
int match = sUriMatcher.match(uri);
|
int match = sUriMatcher.match(uri);
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case PUBLIC_KEY_RING:
|
case PUBLIC_KEY_RING:
|
||||||
case SECRET_KEY_RING:
|
case SECRET_KEY_RING:
|
||||||
qb = buildKeyRingQuery(qb, projectionMap, match, true, sortOrder);
|
qb = buildKeyRingQuery(qb, match, true, sortOrder);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(sortOrder)) {
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
|
sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
|
||||||
@ -339,7 +395,7 @@ public class ApgProvider extends ContentProvider {
|
|||||||
|
|
||||||
case PUBLIC_KEY_RING_BY_ROW_ID:
|
case PUBLIC_KEY_RING_BY_ROW_ID:
|
||||||
case SECRET_KEY_RING_BY_ROW_ID:
|
case SECRET_KEY_RING_BY_ROW_ID:
|
||||||
qb = buildKeyRingQuery(qb, projectionMap, match, true, sortOrder);
|
qb = buildKeyRingQuery(qb, match, true, sortOrder);
|
||||||
|
|
||||||
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
|
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
@ -352,7 +408,7 @@ public class ApgProvider extends ContentProvider {
|
|||||||
|
|
||||||
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
|
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
|
||||||
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
|
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
|
||||||
qb = buildKeyRingQuery(qb, projectionMap, match, true, sortOrder);
|
qb = buildKeyRingQuery(qb, match, true, sortOrder);
|
||||||
|
|
||||||
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
|
qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
@ -365,7 +421,7 @@ public class ApgProvider extends ContentProvider {
|
|||||||
|
|
||||||
case SECRET_KEY_RING_BY_KEY_ID:
|
case SECRET_KEY_RING_BY_KEY_ID:
|
||||||
case PUBLIC_KEY_RING_BY_KEY_ID:
|
case PUBLIC_KEY_RING_BY_KEY_ID:
|
||||||
qb = buildKeyRingQuery(qb, projectionMap, match, false, sortOrder);
|
qb = buildKeyRingQuery(qb, match, false, sortOrder);
|
||||||
|
|
||||||
qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
|
qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
@ -389,15 +445,7 @@ public class ApgProvider extends ContentProvider {
|
|||||||
+ Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND "
|
+ Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " AND "
|
||||||
+ Tables.USER_IDS + "." + UserIdsColumns.RANK + " = '0')");
|
+ Tables.USER_IDS + "." + UserIdsColumns.RANK + " = '0')");
|
||||||
|
|
||||||
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
|
qb.setProjectionMap(getProjectionMapForKeyRings());
|
||||||
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
|
|
||||||
+ KeyRingsColumns.MASTER_KEY_ID);
|
|
||||||
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
|
|
||||||
+ KeyRingsColumns.KEY_RING_DATA);
|
|
||||||
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "."
|
|
||||||
+ UserIdsColumns.USER_ID);
|
|
||||||
|
|
||||||
qb.setProjectionMap(projectionMap);
|
|
||||||
|
|
||||||
String emails = uri.getLastPathSegment();
|
String emails = uri.getLastPathSegment();
|
||||||
String chunks[] = emails.split(" *, *");
|
String chunks[] = emails.split(" *, *");
|
||||||
@ -434,6 +482,8 @@ public class ApgProvider extends ContentProvider {
|
|||||||
qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
|
qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
|
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
|
||||||
|
|
||||||
|
qb.setProjectionMap(getProjectionMapForKeys());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
|
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
|
||||||
@ -448,6 +498,8 @@ public class ApgProvider extends ContentProvider {
|
|||||||
qb.appendWhere(" AND " + BaseColumns._ID + " = ");
|
qb.appendWhere(" AND " + BaseColumns._ID + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
|
|
||||||
|
qb.setProjectionMap(getProjectionMapForKeys());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_USER_ID:
|
case PUBLIC_KEY_RING_USER_ID:
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* 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 org.thialfihar.android.apg.provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The same content provider as ApgProviderInternal except that it does not give out keyRing and key
|
||||||
|
* blob data when querying.
|
||||||
|
*
|
||||||
|
* This provider is exported with a readPermission in AndroidManifest.xml
|
||||||
|
*/
|
||||||
|
public class ApgProviderExternal extends ApgProvider {
|
||||||
|
|
||||||
|
static {
|
||||||
|
sInternalProvider = false;
|
||||||
|
sUriMatcher = buildUriMatcher(sInternalProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* 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 org.thialfihar.android.apg.provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provider is NOT exported in AndroidManifest.xml as it also return the actual secret keys
|
||||||
|
* from the database
|
||||||
|
*/
|
||||||
|
public class ApgProviderInternal extends ApgProvider {
|
||||||
|
|
||||||
|
static {
|
||||||
|
sInternalProvider = true;
|
||||||
|
sUriMatcher = buildUriMatcher(sInternalProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -232,7 +232,7 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY, operations);
|
context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, operations);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(Constants.TAG, "applyBatch failed!", e);
|
Log.e(Constants.TAG, "applyBatch failed!", e);
|
||||||
} catch (OperationApplicationException e) {
|
} catch (OperationApplicationException e) {
|
||||||
@ -288,7 +288,7 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY, operations);
|
context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, operations);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(Constants.TAG, "applyBatch failed!", e);
|
Log.e(Constants.TAG, "applyBatch failed!", e);
|
||||||
} catch (OperationApplicationException e) {
|
} catch (OperationApplicationException e) {
|
||||||
|
@ -172,6 +172,12 @@ public class DecryptActivity extends SherlockFragmentActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// check permissions for intent actions without user interaction
|
||||||
|
String[] restrictedActions = new String[] { ACTION_DECRYPT_AND_RETURN };
|
||||||
|
OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(),
|
||||||
|
Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions);
|
||||||
|
|
||||||
setContentView(R.layout.decrypt);
|
setContentView(R.layout.decrypt);
|
||||||
|
|
||||||
// set actionbar without home button if called from another app
|
// set actionbar without home button if called from another app
|
||||||
|
@ -134,6 +134,12 @@ public class EditKeyActivity extends SherlockFragmentActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// check permissions for intent actions without user interaction
|
||||||
|
String[] restrictedActions = new String[] { ACTION_CREATE_KEY };
|
||||||
|
OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(),
|
||||||
|
Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions);
|
||||||
|
|
||||||
setContentView(R.layout.edit_key);
|
setContentView(R.layout.edit_key);
|
||||||
|
|
||||||
mActionBar = getSupportActionBar();
|
mActionBar = getSupportActionBar();
|
||||||
@ -422,15 +428,16 @@ public class EditKeyActivity extends SherlockFragmentActivity {
|
|||||||
data.putString(ApgIntentService.NEW_PASSPHRASE, mNewPassPhrase);
|
data.putString(ApgIntentService.NEW_PASSPHRASE, mNewPassPhrase);
|
||||||
data.putStringArrayList(ApgIntentService.USER_IDS, getUserIds(mUserIdsView));
|
data.putStringArrayList(ApgIntentService.USER_IDS, getUserIds(mUserIdsView));
|
||||||
ArrayList<PGPSecretKey> keys = getKeys(mKeysView);
|
ArrayList<PGPSecretKey> keys = getKeys(mKeysView);
|
||||||
data.putByteArray(ApgIntentService.KEYS, PGPConversionHelper.PGPSecretKeyArrayListToBytes(keys));
|
data.putByteArray(ApgIntentService.KEYS,
|
||||||
|
PGPConversionHelper.PGPSecretKeyArrayListToBytes(keys));
|
||||||
data.putIntegerArrayList(ApgIntentService.KEYS_USAGES, getKeysUsages(mKeysView));
|
data.putIntegerArrayList(ApgIntentService.KEYS_USAGES, getKeysUsages(mKeysView));
|
||||||
data.putLong(ApgIntentService.MASTER_KEY_ID, getMasterKeyId());
|
data.putLong(ApgIntentService.MASTER_KEY_ID, getMasterKeyId());
|
||||||
|
|
||||||
intent.putExtra(ApgIntentService.EXTRA_DATA, data);
|
intent.putExtra(ApgIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after saving is done in ApgService
|
// Message is received after saving is done in ApgService
|
||||||
ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_saving,
|
ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this,
|
||||||
ProgressDialog.STYLE_HORIZONTAL) {
|
R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
@ -197,6 +197,13 @@ public class EncryptActivity extends SherlockFragmentActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// check permissions for intent actions without user interaction
|
||||||
|
String[] restrictedActions = new String[] { ACTION_ENCRYPT_AND_RETURN,
|
||||||
|
ACTION_GENERATE_SIGNATURE };
|
||||||
|
OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(),
|
||||||
|
Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions);
|
||||||
|
|
||||||
setContentView(R.layout.encrypt);
|
setContentView(R.layout.encrypt);
|
||||||
|
|
||||||
// set actionbar without home button if called from another app
|
// set actionbar without home button if called from another app
|
||||||
@ -841,8 +848,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
|
|||||||
intent.putExtra(ApgIntentService.EXTRA_DATA, data);
|
intent.putExtra(ApgIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after encrypting is done in ApgService
|
// Message is received after encrypting is done in ApgService
|
||||||
ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_encrypting,
|
ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this,
|
||||||
ProgressDialog.STYLE_HORIZONTAL) {
|
R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard ApgHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="1.0" >
|
android:versionName="1.0" >
|
||||||
|
|
||||||
<!-- permission to access provider and service, intents are public! -->
|
<uses-permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DATABASE" />
|
||||||
<uses-permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS" />
|
<uses-permission android:name="org.thialfihar.android.apg.permission.ACCESS_API" />
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="7"
|
android:minSdkVersion="7"
|
||||||
|
Loading…
Reference in New Issue
Block a user