diff options
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider')
6 files changed, 910 insertions, 1595 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 404c128a1..2b40300d7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -19,46 +19,47 @@ package org.sufficientlysecure.keychain.provider; import android.net.Uri; import android.provider.BaseColumns; + import org.sufficientlysecure.keychain.Constants; public class KeychainContract { interface KeyRingsColumns { String MASTER_KEY_ID = "master_key_id"; // not a database id - String TYPE = "type"; // see KeyTypes String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob } interface KeysColumns { + String MASTER_KEY_ID = "master_key_id"; // not a database id + String RANK = "rank"; + String KEY_ID = "key_id"; // not a database id - String TYPE = "type"; // see KeyTypes - String IS_MASTER_KEY = "is_master_key"; String ALGORITHM = "algorithm"; + String FINGERPRINT = "fingerprint"; + String KEY_SIZE = "key_size"; - String CAN_CERTIFY = "can_certify"; String CAN_SIGN = "can_sign"; String CAN_ENCRYPT = "can_encrypt"; + String CAN_CERTIFY = "can_certify"; String IS_REVOKED = "is_revoked"; + String CREATION = "creation"; String EXPIRY = "expiry"; - String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID - String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob - String RANK = "rank"; - String FINGERPRINT = "fingerprint"; } interface UserIdsColumns { - String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID + String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID String USER_ID = "user_id"; // not a database id - String RANK = "rank"; + String RANK = "rank"; // ONLY used for sorting! no key, no nothing! + String IS_PRIMARY = "is_primary"; } interface CertsColumns { - String KEY_RING_ROW_ID = "key_ring_row_id"; // verified id, foreign key to key_rings._ID + String MASTER_KEY_ID = "master_key_id"; // verified id, foreign key to key_rings._ID String RANK = "rank"; // rank of verified key - String KEY_ID = "key_id"; // verified id, not a database id String KEY_ID_CERTIFIER = "key_id_certifier"; // verifying id, not a database id String CREATION = "creation"; + String EXPIRY = "expiry"; String VERIFIED = "verified"; String KEY_DATA = "key_data"; // certification blob } @@ -66,10 +67,15 @@ public class KeychainContract { interface ApiAppsColumns { String PACKAGE_NAME = "package_name"; String PACKAGE_SIGNATURE = "package_signature"; + } + + interface ApiAppsAccountsColumns { + String ACCOUNT_NAME = "account_name"; String KEY_ID = "key_id"; // not a database id String ENCRYPTION_ALGORITHM = "encryption_algorithm"; String HASH_ALORITHM = "hash_algorithm"; String COMPRESSION = "compression"; + String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name } public static final class KeyTypes { @@ -85,97 +91,81 @@ public class KeychainContract { public static final String BASE_KEY_RINGS = "key_rings"; public static final String BASE_DATA = "data"; - public static final String PATH_PUBLIC = "public"; - public static final String PATH_SECRET = "secret"; + public static final String PATH_UNIFIED = "unified"; - public static final String PATH_BY_MASTER_KEY_ID = "master_key_id"; - public static final String PATH_BY_KEY_ID = "key_id"; - public static final String PATH_BY_KEY_ROW_ID = "key_row_id"; - public static final String PATH_BY_CERTIFIER_ID = "certifier_id"; - public static final String PATH_BY_EMAILS = "emails"; - public static final String PATH_BY_LIKE_EMAIL = "like_email"; + public static final String PATH_FIND = "find"; + public static final String PATH_BY_EMAIL = "email"; + public static final String PATH_BY_SUBKEY = "subkey"; + public static final String PATH_PUBLIC = "public"; + public static final String PATH_SECRET = "secret"; public static final String PATH_USER_IDS = "user_ids"; public static final String PATH_KEYS = "keys"; + public static final String PATH_CERTS = "certs"; public static final String BASE_API_APPS = "api_apps"; - public static final String PATH_BY_PACKAGE_NAME = "package_name"; + public static final String PATH_ACCOUNTS = "accounts"; - public static final String BASE_CERTS = "certs"; + public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns { + public static final String MASTER_KEY_ID = "master_key_id"; + public static final String HAS_SECRET = "has_secret"; - public static class KeyRings implements KeyRingsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); - /** - * Use if multiple items get returned - */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring"; - - /** - * Use if a single item is returned - */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key_ring"; + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key_ring"; public static Uri buildUnifiedKeyRingsUri() { - return CONTENT_URI; + return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build(); } - public static Uri buildPublicKeyRingsUri() { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); + public static Uri buildGenericKeyRingUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).build(); } - - public static Uri buildPublicKeyRingsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build(); + public static Uri buildUnifiedKeyRingUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build(); } - - public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC) - .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); + public static Uri buildUnifiedKeyRingUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build(); } - public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID) - .appendPath(keyId).build(); + public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) { + return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build(); } - - public static Uri buildPublicKeyRingsByEmailsUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS) - .appendPath(emails).build(); + public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) { + return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build(); } - public static Uri buildPublicKeyRingsByLikeEmailUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_LIKE_EMAIL) - .appendPath(emails).build(); - } + } - public static Uri buildSecretKeyRingsUri() { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build(); - } + public static class KeyRingData implements KeyRingsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); - public static Uri buildSecretKeyRingsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build(); - } + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key_ring_data"; + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key_ring_data"; - public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET) - .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); + public static Uri buildPublicKeyRingUri() { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); } - - public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID) - .appendPath(keyId).build(); + public static Uri buildPublicKeyRingUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_PUBLIC).build(); } - - public static Uri buildSecretKeyRingsByEmailsUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) - .appendPath(emails).build(); + public static Uri buildPublicKeyRingUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_PUBLIC).build(); } - public static Uri buildSecretKeyRingsByLikeEmails(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_LIKE_EMAIL) - .appendPath(emails).build(); + public static Uri buildSecretKeyRingUri() { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build(); + } + public static Uri buildSecretKeyRingUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_SECRET).build(); + } + public static Uri buildSecretKeyRingUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_SECRET).build(); } + } public static class Keys implements KeysColumns, BaseColumns { @@ -185,131 +175,101 @@ public class KeychainContract { /** * Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key"; /** * Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key"; - - public static Uri buildPublicKeysUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).build(); - } - - public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).appendPath(keyRowId).build(); - } - - public static Uri buildSecretKeysUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).build(); - } + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key"; - public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).appendPath(keyRowId).build(); + public static Uri buildKeysUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_KEYS).build(); } - - public static Uri buildKeysUri(Uri keyRingUri) { - return keyRingUri.buildUpon().appendPath(PATH_KEYS).build(); + public static Uri buildKeysUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_KEYS).build(); } - public static Uri buildKeysUri(Uri keyRingUri, String keyRowId) { - return keyRingUri.buildUpon().appendPath(PATH_KEYS).appendPath(keyRowId).build(); - } } public static class UserIds implements UserIdsColumns, BaseColumns { + public static final String VERIFIED = "verified"; public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); /** * Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.user_id"; /** * Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.user_id"; - public static Uri buildPublicUserIdsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).build(); + public static Uri buildUserIdsUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_USER_IDS).build(); } - - public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); + public static Uri buildUserIdsUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_USER_IDS).build(); } + } - public static Uri buildSecretUserIdsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).build(); - } + public static class ApiApps implements ApiAppsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_API_APPS).build(); - public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); - } + /** + * Use if multiple items get returned + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.api_apps"; - public static Uri buildUserIdsUri(Uri keyRingUri) { - return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).build(); - } + /** + * Use if a single item is returned + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.api_app"; - public static Uri buildUserIdsUri(Uri keyRingUri, String userIdRowId) { - return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); + public static Uri buildByPackageNameUri(String packageName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build(); } } - public static class ApiApps implements ApiAppsColumns, BaseColumns { + public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_API_APPS).build(); /** * Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.api_app.accounts"; /** * Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps"; + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.api_app.account"; - public static Uri buildIdUri(String rowId) { - return CONTENT_URI.buildUpon().appendPath(rowId).build(); + public static Uri buildBaseUri(String packageName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) + .build(); } - public static Uri buildByPackageNameUri(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) - .build(); + public static Uri buildByPackageAndAccountUri(String packageName, String accountName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) + .appendEncodedPath(accountName).build(); } } public static class Certs implements CertsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_CERTS).build(); + public static final String USER_ID = UserIdsColumns.USER_ID; + public static final String SIGNER_UID = "signer_user_id"; - // do we even need this one...? just using it as default for database insert notifications~ - public static Uri buildCertsUri(String rowId) { - return CONTENT_URI.buildUpon().appendPath(rowId).build(); - } - - public static Uri buildCertsByKeyRowIdUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ROW_ID) - .appendPath(keyRingRowId).build(); - } + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); - public static Uri buildCertsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(keyId) - .build(); + public static Uri buildCertsUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).build(); } - - public static Uri buildCertsByCertifierKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_CERTIFIER_ID).appendPath(keyId) - .build(); + public static Uri buildCertsUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_CERTS).build(); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index cc6a1f1e1..fda1783cb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -18,97 +18,156 @@ package org.sufficientlysecure.keychain.provider; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.util.Log; +import java.io.IOException; + public class KeychainDatabase extends SQLiteOpenHelper { - private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 8; + private static final String DATABASE_NAME = "openkeychain.db"; + private static final int DATABASE_VERSION = 1; + static Boolean apg_hack = false; public interface Tables { - String KEY_RINGS = "key_rings"; + String KEY_RINGS_PUBLIC = "keyrings_public"; + String KEY_RINGS_SECRET = "keyrings_secret"; String KEYS = "keys"; String USER_IDS = "user_ids"; - String API_APPS = "api_apps"; String CERTS = "certs"; + String API_APPS = "api_apps"; + String API_ACCOUNTS = "api_accounts"; } - private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + KeyRingsColumns.MASTER_KEY_ID + " INT64, " - + KeyRingsColumns.TYPE + " INTEGER, " - + KeyRingsColumns.KEY_RING_DATA + " BLOB)"; - - private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" - + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + KeysColumns.KEY_ID + " INT64, " - + KeysColumns.TYPE + " INTEGER, " - + KeysColumns.IS_MASTER_KEY + " INTEGER, " - + KeysColumns.ALGORITHM + " INTEGER, " - + KeysColumns.KEY_SIZE + " INTEGER, " - + KeysColumns.CAN_CERTIFY + " INTEGER, " - + KeysColumns.CAN_SIGN + " INTEGER, " - + KeysColumns.CAN_ENCRYPT + " INTEGER, " - + KeysColumns.IS_REVOKED + " INTEGER, " - + KeysColumns.CREATION + " INTEGER, " - + KeysColumns.EXPIRY + " INTEGER, " - + KeysColumns.KEY_DATA + " BLOB," - + KeysColumns.RANK + " INTEGER, " - + KeysColumns.FINGERPRINT + " BLOB, " - + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; - - private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + UserIdsColumns.USER_ID + " TEXT, " - + UserIdsColumns.RANK + " INTEGER, " - + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; + private static final String CREATE_KEYRINGS_PUBLIC = + "CREATE TABLE IF NOT EXISTS keyrings_public (" + + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY," + + KeyRingsColumns.KEY_RING_DATA + " BLOB" + + ")"; + + private static final String CREATE_KEYRINGS_SECRET = + "CREATE TABLE IF NOT EXISTS keyrings_secret (" + + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY," + + KeyRingsColumns.KEY_RING_DATA + " BLOB," + + "FOREIGN KEY(" + KeyRingsColumns.MASTER_KEY_ID + ") " + + "REFERENCES keyrings_public(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + + ")"; + + private static final String CREATE_KEYS = + "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" + + KeysColumns.MASTER_KEY_ID + " INTEGER, " + + KeysColumns.RANK + " INTEGER, " + + + KeysColumns.KEY_ID + " INTEGER, " + + KeysColumns.KEY_SIZE + " INTEGER, " + + KeysColumns.ALGORITHM + " INTEGER, " + + KeysColumns.FINGERPRINT + " BLOB, " + + + KeysColumns.CAN_CERTIFY + " BOOLEAN, " + + KeysColumns.CAN_SIGN + " BOOLEAN, " + + KeysColumns.CAN_ENCRYPT + " BOOLEAN, " + + KeysColumns.IS_REVOKED + " BOOLEAN, " + + + KeysColumns.CREATION + " INTEGER, " + + KeysColumns.EXPIRY + " INTEGER, " + + + "PRIMARY KEY(" + KeysColumns.MASTER_KEY_ID + ", " + KeysColumns.RANK + ")," + + "FOREIGN KEY(" + KeysColumns.MASTER_KEY_ID + ") REFERENCES " + + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + + ")"; + + private static final String CREATE_USER_IDS = + "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + "(" + + UserIdsColumns.MASTER_KEY_ID + " INTEGER, " + + UserIdsColumns.USER_ID + " CHARMANDER, " + + + UserIdsColumns.IS_PRIMARY + " BOOLEAN, " + + UserIdsColumns.RANK+ " INTEGER, " + + + "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), " + + "UNIQUE (" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + "), " + + "FOREIGN KEY(" + UserIdsColumns.MASTER_KEY_ID + ") REFERENCES " + + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + + ")"; + + private static final String CREATE_CERTS = + "CREATE TABLE IF NOT EXISTS " + Tables.CERTS + "(" + + CertsColumns.MASTER_KEY_ID + " INTEGER," + + CertsColumns.RANK + " INTEGER, " // rank of certified uid + + + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key + + CertsColumns.CREATION + " INTEGER, " + + CertsColumns.EXPIRY + " INTEGER, " + + CertsColumns.VERIFIED + " INTEGER, " + + + CertsColumns.KEY_DATA + " BLOB," + + "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", " + + CertsColumns.KEY_ID_CERTIFIER + "), " + + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES " + + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE," + + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES " + + Tables.USER_IDS + "(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + ") ON DELETE CASCADE" + + ")"; 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.PACKAGE_SIGNATURE + " BLOB, " - + ApiAppsColumns.KEY_ID + " INT64, " - + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " - + ApiAppsColumns.HASH_ALORITHM + " INTEGER, " - + ApiAppsColumns.COMPRESSION + " INTEGER)"; - - private static final String CREATE_CERTS = "CREATE TABLE IF NOT EXISTS " + Tables.CERTS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + CertsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL " - + " REFERENCES " + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE, " - + CertsColumns.KEY_ID + " INTEGER, " // certified key - + CertsColumns.RANK + " INTEGER, " // key rank of certified uid - + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key - + CertsColumns.CREATION + " INTEGER, " - + CertsColumns.VERIFIED + " INTEGER, " - + CertsColumns.KEY_DATA + " BLOB)"; + + ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, " + + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)"; + private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, " + + ApiAppsAccountsColumns.KEY_ID + " INT64, " + + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " + + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, " + + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, " + + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", " + + ApiAppsAccountsColumns.PACKAGE_NAME + "), " + + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES " + + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); + + // make sure this is only done once, on the first instance! + boolean iAmIt = false; + synchronized(apg_hack) { + if(!apg_hack) { + iAmIt = true; + apg_hack = true; + } + } + // if it's us, do the import + if(iAmIt) + checkAndImportApg(context); } @Override public void onCreate(SQLiteDatabase db) { Log.w(Constants.TAG, "Creating database..."); - db.execSQL(CREATE_KEY_RINGS); + db.execSQL(CREATE_KEYRINGS_PUBLIC); + db.execSQL(CREATE_KEYRINGS_SECRET); db.execSQL(CREATE_KEYS); db.execSQL(CREATE_USER_IDS); - db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_CERTS); + db.execSQL(CREATE_API_APPS); + db.execSQL(CREATE_API_APPS_ACCOUNTS); } @Override @@ -117,47 +176,100 @@ public class KeychainDatabase extends SQLiteOpenHelper { if (!db.isReadOnly()) { // Enable foreign key constraints db.execSQL("PRAGMA foreign_keys=ON;"); + // TODO remove, once we remove the "always migrate" debug stuff + // db.execSQL("DROP TABLE certs;"); + // db.execSQL("DROP TABLE user_ids;"); + db.execSQL(CREATE_USER_IDS); + db.execSQL(CREATE_CERTS); } } @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); - - // Upgrade from oldVersion through all cases to newest one - for (int version = oldVersion; version < newVersion; ++version) { - Log.w(Constants.TAG, "Upgrading database to version " + version); - - switch (version) { - case 3: - db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY - + " INTEGER DEFAULT 0;"); - db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY - + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;"); - break; - case 4: - db.execSQL(CREATE_API_APPS); - break; - case 5: - // new column: package_signature - db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS); - db.execSQL(CREATE_API_APPS); - break; - case 6: - // new column: fingerprint - db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT - + " BLOB;"); - break; - case 7: - // new table: certs - db.execSQL(CREATE_CERTS); + public void onUpgrade(SQLiteDatabase db, int old, int nu) { + // don't care (this is version 1) + } + /** This method tries to import data from a provided database. + * + * The sole assumptions made on this db are that there is a key_rings table + * with a key_ring_data and a type column, the latter of which should be bigger + * for secret keys. + */ + public void checkAndImportApg(Context context) { + + boolean hasApgDb = false; { + // It's the Java way =( + String[] dbs = context.databaseList(); + for(String db : dbs) { + if(db.equals("apg.db")) { + hasApgDb = true; break; - default: - break; + } + } + } + + if(!hasApgDb) + return; + Log.d(Constants.TAG, "apg.db exists! Importing..."); + + SQLiteDatabase db = new SQLiteOpenHelper(context, "apg.db", null, 1) { + @Override + public void onCreate(SQLiteDatabase db) { + // should never happen + assert false; + } + @Override + public void onDowngrade(SQLiteDatabase db, int old, int nu) { + // don't care + } + @Override + public void onUpgrade(SQLiteDatabase db, int old, int nu) { + // don't care either } + }.getReadableDatabase(); + + // kill current! + { // TODO don't kill current. + Log.d(Constants.TAG, "Truncating db..."); + SQLiteDatabase d = getWritableDatabase(); + d.execSQL("DELETE FROM keyrings_public"); + d.close(); + Log.d(Constants.TAG, "Ok."); } + + Cursor c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY type ASC", null); + try { + // import from old database + Log.d(Constants.TAG, "Importing " + c.getCount() + " keyrings from apg.db..."); + for(int i = 0; i < c.getCount(); i++) { + c.moveToPosition(i); + byte[] data = c.getBlob(0); + PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data); + if(ring instanceof PGPPublicKeyRing) + ProviderHelper.saveKeyRing(context, (PGPPublicKeyRing) ring); + else if(ring instanceof PGPSecretKeyRing) + ProviderHelper.saveKeyRing(context, (PGPSecretKeyRing) ring); + else { + Log.e(Constants.TAG, "Unknown blob data type!"); + } + } + + } catch(IOException e) { + Log.e(Constants.TAG, "Error importing apg db!", e); + return; + } finally { + if(c != null) + c.close(); + if(db != null) + db.close(); + } + + // TODO delete old db, if we are sure this works + // context.deleteDatabase("apg.db"); + Log.d(Constants.TAG, "All done, (not) deleting apg.db"); + + } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index f34423a71..83b3dd744 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,10 +26,16 @@ import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; -import android.provider.BaseColumns; import android.text.TextUtils; + import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.provider.KeychainContract.*; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.Log; @@ -37,54 +43,29 @@ import java.util.Arrays; import java.util.HashMap; public class KeychainProvider extends ContentProvider { - // public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME - // + ".action.DATABASE_CHANGE"; - // - // public static final String EXTRA_BROADCAST_KEY_TYPE = "key_type"; - // public static final String EXTRA_BROADCAST_CONTENT_ITEM_TYPE = "contentItemType"; - - 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_MASTER_KEY_ID = 103; - private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104; - private static final int PUBLIC_KEY_RING_BY_EMAILS = 105; - private static final int PUBLIC_KEY_RING_BY_LIKE_EMAIL = 106; - - private static final int PUBLIC_KEY_RING_KEY = 111; - private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112; - - private static final int PUBLIC_KEY_RING_USER_ID = 121; - private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122; - private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID = 123; - - private static final int SECRET_KEY_RING = 201; - private static final int SECRET_KEY_RING_BY_ROW_ID = 202; - private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203; - private static final int SECRET_KEY_RING_BY_KEY_ID = 204; - private static final int SECRET_KEY_RING_BY_EMAILS = 205; - private static final int SECRET_KEY_RING_BY_LIKE_EMAIL = 206; - - private static final int SECRET_KEY_RING_KEY = 211; - private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212; - - private static final int SECRET_KEY_RING_USER_ID = 221; - private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; + + private static final int KEY_RINGS_UNIFIED = 101; + private static final int KEY_RINGS_PUBLIC = 102; + private static final int KEY_RINGS_SECRET = 103; + + private static final int KEY_RING_UNIFIED = 200; + private static final int KEY_RING_KEYS = 201; + private static final int KEY_RING_USER_IDS = 202; + private static final int KEY_RING_PUBLIC = 203; + private static final int KEY_RING_SECRET = 204; + private static final int KEY_RING_CERTS = 205; private static final int API_APPS = 301; - private static final int API_APPS_BY_ROW_ID = 302; private static final int API_APPS_BY_PACKAGE_NAME = 303; + private static final int API_ACCOUNTS = 304; + private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306; - private static final int UNIFIED_KEY_RING = 401; + private static final int KEY_RINGS_FIND_BY_EMAIL = 400; + private static final int KEY_RINGS_FIND_BY_SUBKEY = 401; - private static final int CERTS = 401; - private static final int CERTS_BY_KEY_ID = 402; - private static final int CERTS_BY_ROW_ID = 403; - private static final int CERTS_BY_KEY_ROW_ID = 404; - private static final int CERTS_BY_KEY_ROW_ID_ALL = 405; - private static final int CERTS_BY_CERTIFIER_ID = 406; - private static final int CERTS_BY_KEY_ROW_ID_HAS_SECRET = 407; + private static final int CERTS_FIND_BY_CERTIFIER_ID = 501; - // private static final int DATA_STREAM = 401; + // private static final int DATA_STREAM = 501; protected UriMatcher mUriMatcher; @@ -98,171 +79,85 @@ public class KeychainProvider extends ContentProvider { String authority = KeychainContract.CONTENT_AUTHORITY; /** - * unified key rings - * - * <pre> - * key_rings - * </pre> - */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS, UNIFIED_KEY_RING); - - /** - * public key rings + * list key_rings * * <pre> + * key_rings/unified * key_rings/public - * key_rings/public/# - * key_rings/public/master_key_id/_ - * key_rings/public/key_id/_ - * key_rings/public/emails/_ - * key_rings/public/like_email/_ * </pre> */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC, PUBLIC_KEY_RING); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/#", PUBLIC_KEY_RING_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_MASTER_KEY_ID - + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_KEY_ID + "/*", - PUBLIC_KEY_RING_BY_KEY_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_EMAILS + "/*", - PUBLIC_KEY_RING_BY_EMAILS); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_EMAILS, - PUBLIC_KEY_RING_BY_EMAILS); // without emails specified - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_LIKE_EMAIL + "/*", - PUBLIC_KEY_RING_BY_LIKE_EMAIL); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + + "/" + KeychainContract.PATH_UNIFIED, + KEY_RINGS_UNIFIED); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + + "/" + KeychainContract.PATH_PUBLIC, + KEY_RINGS_PUBLIC); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + + "/" + KeychainContract.PATH_SECRET, + KEY_RINGS_SECRET); /** - * public keys + * find by criteria other than master key id * - * <pre> - * key_rings/public/#/keys - * key_rings/public/#/keys/# - * </pre> - */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_KEYS, - PUBLIC_KEY_RING_KEY); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_KEYS + "/#", - PUBLIC_KEY_RING_KEY_BY_ROW_ID); - - /** - * public user ids + * key_rings/find/email/_ + * key_rings/find/subkey/_ * - * <pre> - * key_rings/public/#/user_ids - * key_rings/public/#/user_ids/# - * key_rings/public/master_key_id/#/user_ids - * </pre> */ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS, - PUBLIC_KEY_RING_USER_ID); + + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_EMAIL + "/*", + KEY_RINGS_FIND_BY_EMAIL); matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#", - PUBLIC_KEY_RING_USER_ID_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_PUBLIC + "/" - + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*/" + KeychainContract.PATH_USER_IDS, - PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID); + + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_SUBKEY + "/*", + KEY_RINGS_FIND_BY_SUBKEY); /** - * secret key rings + * list key_ring specifics * * <pre> - * key_rings/secret - * key_rings/secret/# - * key_rings/secret/master_key_id/_ - * key_rings/secret/key_id/_ - * key_rings/secret/emails/_ - * key_rings/secret/like_email/_ + * key_rings/_/unified + * key_rings/_/keys + * key_rings/_/user_ids + * key_rings/_/public + * key_rings/_/secret * </pre> */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET, SECRET_KEY_RING); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/#", SECRET_KEY_RING_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_MASTER_KEY_ID - + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_KEY_ID + "/*", - SECRET_KEY_RING_BY_KEY_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_EMAILS + "/*", - SECRET_KEY_RING_BY_EMAILS); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_EMAILS, - SECRET_KEY_RING_BY_EMAILS); // without emails specified - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_LIKE_EMAIL + "/*", - SECRET_KEY_RING_BY_LIKE_EMAIL); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_UNIFIED, + KEY_RING_UNIFIED); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_KEYS, + KEY_RING_KEYS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_USER_IDS, + KEY_RING_USER_IDS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_PUBLIC, + KEY_RING_PUBLIC); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_SECRET, + KEY_RING_SECRET); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_CERTS, + KEY_RING_CERTS); /** - * secret keys + * API apps * * <pre> - * key_rings/secret/#/keys - * key_rings/secret/#/keys/# - * </pre> - */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_KEYS, - SECRET_KEY_RING_KEY); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_KEYS + "/#", - SECRET_KEY_RING_KEY_BY_ROW_ID); - - /** - * secret user ids + * api_apps + * api_apps/_ (package name) * - * <pre> - * key_rings/secret/#/user_ids - * key_rings/secret/#/user_ids/# + * api_apps/_/accounts + * api_apps/_/accounts/_ (account name) * </pre> */ - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_USER_IDS, - SECRET_KEY_RING_USER_ID); - matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" - + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_USER_IDS + "/#", - SECRET_KEY_RING_USER_ID_BY_ROW_ID); - - /** - * API apps - */ matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" - + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME); - /** - /** - * certifications - * - * <pre> - * </pre> - * - */ - matcher.addURI(authority, KeychainContract.BASE_CERTS, CERTS); - matcher.addURI(authority, KeychainContract.BASE_CERTS + "/#", CERTS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" - + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" - + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL); - matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" - + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/has_secret", CERTS_BY_KEY_ROW_ID_HAS_SECRET); - matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" - + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID); - matcher.addURI(authority, KeychainContract.BASE_CERTS + "/" - + KeychainContract.PATH_BY_CERTIFIER_ID + "/#", CERTS_BY_CERTIFIER_ID); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME); /** * data stream @@ -276,7 +171,7 @@ public class KeychainProvider extends ContentProvider { return matcher; } - private KeychainDatabase mApgDatabase; + private KeychainDatabase mKeychainDatabase; /** * {@inheritDoc} @@ -284,10 +179,15 @@ public class KeychainProvider extends ContentProvider { @Override public boolean onCreate() { mUriMatcher = buildUriMatcher(); - mApgDatabase = new KeychainDatabase(getContext()); return true; } + public KeychainDatabase getDb() { + if(mKeychainDatabase == null) + mKeychainDatabase = new KeychainDatabase(getContext()); + return mKeychainDatabase; + } + /** * {@inheritDoc} */ @@ -295,247 +195,44 @@ public class KeychainProvider extends ContentProvider { public String getType(Uri uri) { final int match = mUriMatcher.match(uri); switch (match) { - case PUBLIC_KEY_RING: - case PUBLIC_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_LIKE_EMAIL: - case SECRET_KEY_RING: - case SECRET_KEY_RING_BY_EMAILS: - case SECRET_KEY_RING_BY_LIKE_EMAIL: - return KeyRings.CONTENT_TYPE; - - case PUBLIC_KEY_RING_BY_ROW_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - case SECRET_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_KEY_ID: + case KEY_RING_PUBLIC: return KeyRings.CONTENT_ITEM_TYPE; - case PUBLIC_KEY_RING_KEY: - case SECRET_KEY_RING_KEY: + case KEY_RING_KEYS: return Keys.CONTENT_TYPE; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - return Keys.CONTENT_ITEM_TYPE; - - case PUBLIC_KEY_RING_USER_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: - case SECRET_KEY_RING_USER_ID: + case KEY_RING_USER_IDS: return UserIds.CONTENT_TYPE; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - return UserIds.CONTENT_ITEM_TYPE; + case KEY_RING_SECRET: + return KeyRings.CONTENT_ITEM_TYPE; case API_APPS: return ApiApps.CONTENT_TYPE; - case API_APPS_BY_ROW_ID: case API_APPS_BY_PACKAGE_NAME: return ApiApps.CONTENT_ITEM_TYPE; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } + case API_ACCOUNTS: + return ApiAccounts.CONTENT_TYPE; - /** - * Returns type of the query (secret/public) - * - * @param match - * @return - */ - private int getKeyType(int match) { - int type; - switch (match) { - case PUBLIC_KEY_RING: - case PUBLIC_KEY_RING_BY_ROW_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - case PUBLIC_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_LIKE_EMAIL: - case PUBLIC_KEY_RING_KEY: - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case PUBLIC_KEY_RING_USER_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - type = KeyTypes.PUBLIC; - break; - - case SECRET_KEY_RING: - case SECRET_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_KEY_ID: - case SECRET_KEY_RING_BY_EMAILS: - case SECRET_KEY_RING_BY_LIKE_EMAIL: - case SECRET_KEY_RING_KEY: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - type = KeyTypes.SECRET; - break; + case API_ACCOUNTS_BY_ACCOUNT_NAME: + return ApiAccounts.CONTENT_ITEM_TYPE; default: - Log.e(Constants.TAG, "Unknown match " + match); - type = -1; - break; - } - - return type; - } - - /** - * Set result of query to specific columns, don't show blob column - * - * @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.KEY_RING_DATA, Tables.KEY_RINGS + "." - + KeyRingsColumns.KEY_RING_DATA); - projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." - + KeyRingsColumns.MASTER_KEY_ID); - // TODO: deprecated master key id - //projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID); - - projectionMap.put(KeysColumns.ALGORITHM, Tables.KEYS + "." + KeysColumns.ALGORITHM); - projectionMap.put(KeysColumns.KEY_SIZE, Tables.KEYS + "." + KeysColumns.KEY_SIZE); - projectionMap.put(KeysColumns.CREATION, Tables.KEYS + "." + KeysColumns.CREATION); - projectionMap.put(KeysColumns.EXPIRY, Tables.KEYS + "." + KeysColumns.EXPIRY); - projectionMap.put(KeysColumns.KEY_RING_ROW_ID, Tables.KEYS + "." + KeysColumns.KEY_RING_ROW_ID); - projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT); - projectionMap.put(KeysColumns.IS_REVOKED, Tables.KEYS + "." + KeysColumns.IS_REVOKED); - - projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); - - // type attribute is special: if there is any grouping, choose secret over public type - projectionMap.put(KeyRingsColumns.TYPE, - "MAX(" + Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + ") AS " + KeyRingsColumns.TYPE); - - return projectionMap; - } - - /** - * Set result of query to specific columns, don't show blob column - * - * @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_CERTIFY, KeysColumns.CAN_CERTIFY); - 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); - projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA); - projectionMap.put(KeysColumns.RANK, KeysColumns.RANK); - projectionMap.put(KeysColumns.FINGERPRINT, KeysColumns.FINGERPRINT); - - return projectionMap; - } - - private HashMap<String, String> getProjectionMapForUserIds() { - HashMap<String, String> projectionMap = new HashMap<String, String>(); - - projectionMap.put(BaseColumns._ID, Tables.USER_IDS + "." + BaseColumns._ID); - projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); - projectionMap.put(UserIdsColumns.RANK, Tables.USER_IDS + "." + UserIdsColumns.RANK); - projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." - + KeyRingsColumns.MASTER_KEY_ID); - - // this is the count of known secret keys who certified this uid - projectionMap.put("verified", "COUNT(" + Tables.KEYS + "." + Keys._ID + ") AS verified"); - - return projectionMap; - } - private HashMap<String, String> getProjectionMapForCerts() { - - HashMap<String, String> pmap = new HashMap<String, String>(); - pmap.put(Certs._ID, Tables.CERTS + "." + Certs._ID); - pmap.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID); - pmap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); - pmap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); - pmap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); - pmap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); - pmap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); - // verified key data - pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); - // verifying key data - pmap.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid"); - - return pmap; - } - - /** - * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys - */ - private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) { - if (match != UNIFIED_KEY_RING) { - // public or secret keyring - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + throw new UnsupportedOperationException("Unknown uri: " + uri); } - - // join keyrings with keys and userIds - // Only get user id and key with rank 0 (main user id and main key) - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." - + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "." - + KeysColumns.RANK + " = '0') " + " INNER JOIN " + Tables.USER_IDS + " ON " - + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." - + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." - + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - return qb; - } - - /** - * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys - * <p/> - * Here only one key should be selected in the query to return a single keyring! - */ - private SQLiteQueryBuilder buildKeyRingQueryWithSpecificKey(SQLiteQueryBuilder qb, int match) { - // public or secret keyring - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - // join keyrings with keys and userIds to every keyring - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." - + KeysColumns.KEY_RING_ROW_ID + ") " + " INNER JOIN " + Tables.USER_IDS + " ON " - + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." - + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." - + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - return qb; } /** * {@inheritDoc} */ - @SuppressWarnings("deprecation") @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - SQLiteDatabase db = mApgDatabase.getReadableDatabase(); int match = mUriMatcher.match(uri); @@ -543,227 +240,224 @@ public class KeychainProvider extends ContentProvider { String groupBy = null, having = null; switch (match) { - case UNIFIED_KEY_RING: - qb = buildKeyRingQuery(qb, match); - - // GROUP BY so we don't get duplicates - groupBy = Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID; - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = KeyRings.TYPE + " DESC, " + - Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + case KEY_RING_UNIFIED: + case KEY_RINGS_UNIFIED: + case KEY_RINGS_FIND_BY_EMAIL: + case KEY_RINGS_FIND_BY_SUBKEY: { + HashMap<String, String> projectionMap = new HashMap<String, String>(); + projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id"); + projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); + projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID); + projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE); + projectionMap.put(KeyRings.IS_REVOKED, Keys.IS_REVOKED); + projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY); + projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT); + projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN); + projectionMap.put(KeyRings.CREATION, Keys.CREATION); + projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY); + projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM); + projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT); + projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID); + projectionMap.put(KeyRings.HAS_SECRET, "(" + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL) AS " + KeyRings.HAS_SECRET); + qb.setProjectionMap(projectionMap); + + qb.setTables( + Tables.KEYS + + " INNER JOIN " + Tables.USER_IDS + " ON (" + + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " = " + + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0" + + ") LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON (" + + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " = " + + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + + ")" + ); + qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0"); + + switch(match) { + case KEY_RING_UNIFIED: { + qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + break; + } + case KEY_RINGS_FIND_BY_SUBKEY: { + try { + String subkey = Long.valueOf(uri.getLastPathSegment()).toString(); + qb.appendWhere(" AND EXISTS (" + + " SELECT 1 FROM " + Tables.KEYS + " AS tmp" + + " WHERE tmp." + UserIds.MASTER_KEY_ID + + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " AND tmp." + Keys.KEY_ID + " = " + subkey + "" + + ")"); + } catch(NumberFormatException e) { + Log.e(Constants.TAG, "Malformed find by subkey query!", e); + qb.appendWhere(" AND 0"); + } + break; + } + case KEY_RINGS_FIND_BY_EMAIL: { + String chunks[] = uri.getLastPathSegment().split(" *, *"); + boolean gotCondition = false; + String emailWhere = ""; + // JAVA ♥ + for (int i = 0; i < chunks.length; ++i) { + if (chunks[i].length() == 0) { + continue; + } + if (i != 0) { + emailWhere += " OR "; + } + emailWhere += "tmp." + UserIds.USER_ID + " LIKE "; + // match '*<email>', so it has to be at the *end* of the user id + emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); + gotCondition = true; + } + if(gotCondition) { + qb.appendWhere(" AND EXISTS (" + + " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp" + + " WHERE tmp." + UserIds.MASTER_KEY_ID + + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " AND (" + emailWhere + ")" + + ")"); + } else { + // TODO better way to do this? + Log.e(Constants.TAG, "Malformed find by email query!"); + qb.appendWhere(" AND 0"); + } + break; + } } - break; - - case PUBLIC_KEY_RING: - case SECRET_KEY_RING: - qb = buildKeyRingQuery(qb, match); - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + sortOrder = + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NULL ASC, " + + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC"; } - break; - - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - qb = buildKeyRingQuery(qb, match); - - qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } + // uri to watch is all /key_rings/ + uri = KeyRings.CONTENT_URI; break; + } - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - qb = buildKeyRingQuery(qb, match); - - qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); + case KEY_RING_KEYS: { + HashMap<String, String> projectionMap = new HashMap<String, String>(); + projectionMap.put(Keys._ID, Tables.KEYS + ".oid AS _id"); + projectionMap.put(Keys.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); + projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK); + projectionMap.put(Keys.KEY_ID, Keys.KEY_ID); + projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE); + projectionMap.put(Keys.IS_REVOKED, Keys.IS_REVOKED); + projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY); + projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT); + projectionMap.put(Keys.CAN_SIGN, Keys.CAN_SIGN); + projectionMap.put(Keys.CREATION, Keys.CREATION); + projectionMap.put(Keys.EXPIRY, Keys.EXPIRY); + projectionMap.put(Keys.ALGORITHM, Keys.ALGORITHM); + projectionMap.put(Keys.FINGERPRINT, Keys.FINGERPRINT); + qb.setProjectionMap(projectionMap); - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } + qb.setTables(Tables.KEYS); + qb.appendWhere(Keys.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; + } - case SECRET_KEY_RING_BY_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - qb = buildKeyRingQueryWithSpecificKey(qb, match); + case KEY_RING_USER_IDS: { + HashMap<String, String> projectionMap = new HashMap<String, String>(); + projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id"); + projectionMap.put(UserIds.MASTER_KEY_ID, UserIds.MASTER_KEY_ID); + projectionMap.put(UserIds.USER_ID, UserIds.USER_ID); + projectionMap.put(UserIds.RANK, UserIds.RANK); + projectionMap.put(UserIds.IS_PRIMARY, UserIds.IS_PRIMARY); + projectionMap.put(UserIds.VERIFIED, "0 AS " + UserIds.VERIFIED); + qb.setProjectionMap(projectionMap); - qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); + qb.setTables(Tables.USER_IDS); + qb.appendWhere(UserIds.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + sortOrder = UserIds.RANK + " ASC"; } break; - case SECRET_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_EMAILS: - qb = buildKeyRingQuery(qb, match); - - String emails = uri.getLastPathSegment(); - String chunks[] = emails.split(" *, *"); - boolean gotCondition = false; - String emailWhere = ""; - for (int i = 0; i < chunks.length; ++i) { - if (chunks[i].length() == 0) { - continue; - } - if (i != 0) { - emailWhere += " OR "; - } - emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE "; - // match '*<email>', so it has to be at the *end* of the user id - emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); - gotCondition = true; - } - - if (gotCondition) { - qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " - + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID - + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere - + "))"); - } - - break; - - case SECRET_KEY_RING_BY_LIKE_EMAIL: - case PUBLIC_KEY_RING_BY_LIKE_EMAIL: - qb = buildKeyRingQuery(qb, match); - - String likeEmail = uri.getLastPathSegment(); - - String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE " - + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>"); - - qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " - + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID - + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere - + "))"); - - break; + } - case PUBLIC_KEY_RING_KEY: - case SECRET_KEY_RING_KEY: - qb.setTables(Tables.KEYS); - qb.appendWhere(KeysColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + case KEY_RINGS_PUBLIC: + case KEY_RING_PUBLIC: { + HashMap<String, String> projectionMap = new HashMap<String, String>(); + projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_PUBLIC + ".oid AS _id"); + projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID); + projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA); + qb.setProjectionMap(projectionMap); - qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + qb.setTables(Tables.KEY_RINGS_PUBLIC); - qb.setProjectionMap(getProjectionMapForKeys()); + if(match == KEY_RING_PUBLIC) { + qb.appendWhere(KeyRings.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + } break; + } - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - qb.setTables(Tables.KEYS); - qb.appendWhere(KeysColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - qb.setProjectionMap(getProjectionMapForKeys()); + case KEY_RINGS_SECRET: + case KEY_RING_SECRET: { + HashMap<String, String> projectionMap = new HashMap<String, String>(); + projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_SECRET + ".oid AS _id"); + projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID); + projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA); + qb.setProjectionMap(projectionMap); - break; + qb.setTables(Tables.KEY_RINGS_SECRET); - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: - case PUBLIC_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID: - qb.setTables(Tables.USER_IDS - + " INNER JOIN " + Tables.KEY_RINGS + " ON (" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " - + Tables.USER_IDS + "." + KeysColumns.KEY_RING_ROW_ID - + ") LEFT JOIN " + Tables.CERTS + " ON (" - + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + " = " - + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID - + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = " - + Tables.CERTS + "." + Certs.RANK - + ") LEFT JOIN " + Tables.KEYS + " ON (" - + Tables.KEYS + "." + Keys.KEY_ID + " = " - + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER - // might introduce a "trusted" flag later? for now, we simply assume - // every private key's signature is good. - + " AND " + Tables.KEYS + "." + Keys.TYPE - + " == " + KeyTypes.SECRET - + ")"); - - groupBy = Tables.USER_IDS + "." + UserIds.RANK; - - qb.setProjectionMap(getProjectionMapForUserIds()); - - if(match == PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID) { - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(3)); - } else { - qb.appendWhere(Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + if(match == KEY_RING_SECRET) { + qb.appendWhere(KeyRings.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); } break; + } - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - break; + case KEY_RING_CERTS: + HashMap<String, String> projectionMap = new HashMap<String, String>(); + projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID); + projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID); + projectionMap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK); + projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); + projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); + projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED); + projectionMap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA); + // verified key data + projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); + // verifying key data + projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); + qb.setProjectionMap(projectionMap); - case CERTS_BY_ROW_ID: - case CERTS_BY_KEY_ROW_ID_ALL: - case CERTS_BY_KEY_ROW_ID_HAS_SECRET: - case CERTS_BY_KEY_ROW_ID: qb.setTables(Tables.CERTS + " JOIN " + Tables.USER_IDS + " ON (" - + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = " - + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " AND " + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_IDS + "." + UserIds.RANK - // noooooooot sure about this~ database design - + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "") - + " JOIN " + Tables.KEYS + " ON (" - + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " - + Tables.KEYS + "." + Keys.KEY_ID - + (match == CERTS_BY_KEY_ROW_ID_HAS_SECRET ? - " AND " + Tables.KEYS + "." + Keys.TYPE + " = " + KeyTypes.SECRET : "" - ) + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" - + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = " - + "signer." + UserIds.KEY_RING_ROW_ID + + Tables.CERTS + "." + Keys.MASTER_KEY_ID + " = " + + "signer." + UserIds.MASTER_KEY_ID + " AND " + "signer." + Keys.RANK + " = 0" + ")"); - qb.setProjectionMap(getProjectionMapForCerts()); - groupBy = Tables.CERTS + "." + Certs.RANK + ", " + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER; - if(match == CERTS_BY_ROW_ID) { - qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(1)); - } else { - qb.appendWhere(Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - } + qb.appendWhere(Tables.CERTS + "." + KeyRings.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; @@ -771,22 +465,29 @@ public class KeychainProvider extends ContentProvider { qb.setTables(Tables.API_APPS); break; - case API_APPS_BY_ROW_ID: + case API_APPS_BY_PACKAGE_NAME: qb.setTables(Tables.API_APPS); - - qb.appendWhere(BaseColumns._ID + " = "); + qb.appendWhere(ApiApps.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); break; - case API_APPS_BY_PACKAGE_NAME: - qb.setTables(Tables.API_APPS); - qb.appendWhere(ApiApps.PACKAGE_NAME + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + case API_ACCOUNTS: + qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; + case API_ACCOUNTS_BY_ACCOUNT_NAME: + qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + + qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + break; default: - throw new IllegalArgumentException("Unknown URI " + uri); + throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")"); } @@ -798,6 +499,7 @@ public class KeychainProvider extends ContentProvider { orderBy = sortOrder; } + SQLiteDatabase db = getDb().getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy); // Tell the cursor what uri to watch, so it knows when its source data changes @@ -821,78 +523,72 @@ public class KeychainProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = getDb().getWritableDatabase(); Uri rowUri = null; - long rowId = -1; + Long keyId = null; try { final int match = mUriMatcher.match(uri); switch (match) { - case PUBLIC_KEY_RING: - values.put(KeyRings.TYPE, KeyTypes.PUBLIC); - - rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); - rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); - + case KEY_RING_PUBLIC: + db.insertOrThrow(Tables.KEY_RINGS_PUBLIC, null, values); + keyId = values.getAsLong(KeyRings.MASTER_KEY_ID); break; - case PUBLIC_KEY_RING_KEY: - values.put(Keys.TYPE, KeyTypes.PUBLIC); - rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + case KEY_RING_SECRET: + db.insertOrThrow(Tables.KEY_RINGS_SECRET, null, values); + keyId = values.getAsLong(KeyRings.MASTER_KEY_ID); + break; + case KEY_RING_KEYS: + Log.d(Constants.TAG, "keys"); + db.insertOrThrow(Tables.KEYS, null, values); + keyId = values.getAsLong(Keys.MASTER_KEY_ID); break; - case PUBLIC_KEY_RING_USER_ID: - rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + case KEY_RING_USER_IDS: + db.insertOrThrow(Tables.USER_IDS, null, values); + keyId = values.getAsLong(UserIds.MASTER_KEY_ID); break; - case SECRET_KEY_RING: - values.put(KeyRings.TYPE, KeyTypes.SECRET); - rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); - rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + case KEY_RING_CERTS: + db.insertOrThrow(Tables.CERTS, null, values); + keyId = values.getAsLong(Certs.MASTER_KEY_ID); + break; + case API_APPS: + db.insertOrThrow(Tables.API_APPS, null, values); break; - case SECRET_KEY_RING_KEY: - values.put(Keys.TYPE, KeyTypes.SECRET); - rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + case API_ACCOUNTS: + // set foreign key automatically based on given uri + // e.g., api_apps/com.example.app/accounts/ + String packageName = uri.getPathSegments().get(1); + values.put(ApiAccounts.PACKAGE_NAME, packageName); - break; - case SECRET_KEY_RING_USER_ID: - rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); + Log.d(Constants.TAG, "provider packageName: " + packageName); - break; - case API_APPS: - rowId = db.insertOrThrow(Tables.API_APPS, null, values); - rowUri = ApiApps.buildIdUri(Long.toString(rowId)); + db.insertOrThrow(Tables.API_ACCOUNTS, null, values); + // TODO: this is wrong: +// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId)); break; - case CERTS_BY_KEY_ROW_ID: - rowId = db.insertOrThrow(Tables.CERTS, null, values); - // kinda useless.. should this be buildCertsByKeyRowIdUri? - // rowUri = Certs.buildCertsUri(Long.toString(rowId)); - rowUri = uri; - break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } + if(keyId != null) { + uri = KeyRings.buildGenericKeyRingUri(keyId.toString()); + rowUri = uri; + } + // notify of changes in db getContext().getContentResolver().notifyChange(uri, null); } catch (SQLiteConstraintException e) { - Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?"); + Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?", e); } return rowUri; @@ -902,51 +598,43 @@ public class KeychainProvider extends ContentProvider { * {@inheritDoc} */ @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { + public int delete(Uri uri, String additionalSelection, String[] selectionArgs) { Log.v(Constants.TAG, "delete(uri=" + uri + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = getDb().getWritableDatabase(); int count; final int match = mUriMatcher.match(uri); - String defaultSelection = null; switch (match) { - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); - // corresponding keys and userIds are deleted by ON DELETE CASCADE - count = db.delete(Tables.KEY_RINGS, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), - selectionArgs); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); - break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); + case KEY_RING_PUBLIC: { + @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above + String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1); + if (!TextUtils.isEmpty(additionalSelection)) { + selection += " AND (" + additionalSelection + ")"; + } // corresponding keys and userIds are deleted by ON DELETE CASCADE - count = db.delete(Tables.KEY_RINGS, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), - selectionArgs); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); - break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - count = db.delete(Tables.KEYS, - buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); + count = db.delete(Tables.KEY_RINGS_PUBLIC, selection, selectionArgs); + uri = KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1)); break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), - selectionArgs); + } + case KEY_RING_SECRET: { + @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above + String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1); + if (!TextUtils.isEmpty(additionalSelection)) { + selection += " AND (" + additionalSelection + ")"; + } + count = db.delete(Tables.KEY_RINGS_SECRET, selection, selectionArgs); + uri = KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1)); break; - case API_APPS_BY_ROW_ID: - count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection), + } + + case API_APPS_BY_PACKAGE_NAME: + count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, additionalSelection), selectionArgs); break; - case API_APPS_BY_PACKAGE_NAME: - count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection), + case API_ACCOUNTS_BY_ACCOUNT_NAME: + count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection), selectionArgs); break; default: @@ -966,58 +654,20 @@ public class KeychainProvider extends ContentProvider { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); String defaultSelection = null; int count = 0; try { final int match = mUriMatcher.match(uri); switch (match) { - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); - - count = db.update( - Tables.KEY_RINGS, - values, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), - selection), selectionArgs); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); - - break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); - - count = db.update( - Tables.KEY_RINGS, - values, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), - selection), selectionArgs); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); - - break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - count = db - .update(Tables.KEYS, values, - buildDefaultKeysSelection(uri, getKeyType(match), selection), - selectionArgs); - sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); - - break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - count = db.update(Tables.USER_IDS, values, - buildDefaultUserIdsSelection(uri, selection), selectionArgs); - break; - case API_APPS_BY_ROW_ID: - count = db.update(Tables.API_APPS, values, - buildDefaultApiAppsSelection(uri, false, selection), selectionArgs); - break; case API_APPS_BY_PACKAGE_NAME: count = db.update(Tables.API_APPS, values, - buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); + buildDefaultApiAppsSelection(uri, selection), selectionArgs); + break; + case API_ACCOUNTS_BY_ACCOUNT_NAME: + count = db.update(Tables.API_ACCOUNTS, values, + buildDefaultApiAccountsSelection(uri, selection), selectionArgs); break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); @@ -1034,125 +684,36 @@ public class KeychainProvider extends ContentProvider { } /** - * Build default selection statement for KeyRings. If no extra selection is specified only build - * where clause with rowId - * - * @param defaultSelection - * @param keyType - * @param selection - * @return - */ - private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType, - String selection) { - String andType = ""; - if (keyType != null) { - andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType; - } - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return defaultSelection + andType + andSelection; - } - - /** - * Build default selection statement for Keys. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) { - String rowId = uri.getLastPathSegment(); - - String foreignKeyRingRowId = uri.getPathSegments().get(2); - String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " - + foreignKeyRingRowId; - - String andType = ""; - if (keyType != null) { - andType = " AND " + KeysColumns.TYPE + "=" + keyType; - } - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection; - } - - /** - * Build default selection statement for UserIds. If no extra selection is specified only build + * Build default selection statement for API apps. If no extra selection is specified only build * where clause with rowId * * @param uri * @param selection * @return */ - private String buildDefaultUserIdsSelection(Uri uri, String selection) { - String rowId = uri.getLastPathSegment(); - - String foreignKeyRingRowId = uri.getPathSegments().get(2); - String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " - + foreignKeyRingRowId; + private String buildDefaultApiAppsSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); String andSelection = ""; if (!TextUtils.isEmpty(selection)) { andSelection = " AND (" + selection + ")"; } - return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection; + return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection; } - /** - * Build default selection statement for API apps. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) { - String lastPathSegment = uri.getLastPathSegment(); + private String buildDefaultApiAccountsSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1)); + String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); String andSelection = ""; if (!TextUtils.isEmpty(selection)) { andSelection = " AND (" + selection + ")"; } - if (packageSelection) { - return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection; - } else { - return BaseColumns._ID + "=" + lastPathSegment + andSelection; - } + return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND " + + ApiAccounts.ACCOUNT_NAME + "=" + accountName + + andSelection; } - // @Override - // public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - // int match = mUriMatcher.match(uri); - // if (match != DATA_STREAM) { - // throw new FileNotFoundException(); - // } - // String fileName = uri.getLastPathSegment(); - // File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName); - // return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); - // } - - /** - * This broadcast is send system wide to inform other application that a keyring was inserted, - * updated, or deleted - */ - private void sendBroadcastDatabaseChange(int keyType, String contentItemType) { - // TODO: Disabled, old API - // Intent intent = new Intent(); - // intent.setAction(ACTION_BROADCAST_DATABASE_CHANGE); - // intent.putExtra(EXTRA_BROADCAST_KEY_TYPE, keyType); - // intent.putExtra(EXTRA_BROADCAST_CONTENT_ITEM_TYPE, contentItemType); - // - // getContext().sendBroadcast(intent, Constants.PERMISSION_ACCESS_API); - } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java index da1bcb2d9..bc7de0b37 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java @@ -25,7 +25,7 @@ import android.provider.BaseColumns; import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns; public class KeychainServiceBlobDatabase extends SQLiteOpenHelper { - private static final String DATABASE_NAME = "apg_blob.db"; + private static final String DATABASE_NAME = "openkeychain_blob.db"; private static final int DATABASE_VERSION = 2; public static final String TABLE = "data"; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java index 6ac61e157..aa30e845d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java @@ -38,7 +38,7 @@ import java.util.List; import java.util.UUID; public class KeychainServiceBlobProvider extends ContentProvider { - private static final String STORE_PATH = Constants.Path.APP_DIR + "/ApgBlobs"; + private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs"; private KeychainServiceBlobDatabase mBlobDatabase = null; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 1d249c67e..503fed3c9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -19,27 +19,36 @@ package org.sufficientlysecure.keychain.provider; import java.security.SignatureException; import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.UserAttributePacket; -import org.spongycastle.bcpg.UserAttributeSubpacket; -import android.content.*; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.openpgp.*; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.service.remote.AppSettings; +import org.sufficientlysecure.keychain.remote.AccountSettings; +import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -49,27 +58,82 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map; - +import java.util.HashSet; +import java.util.Set; public class ProviderHelper { - /** - * Private helper method to get PGPKeyRing from database - */ + // If we ever switch to api level 11, we can ditch this whole mess! + public static final int FIELD_TYPE_NULL = 1; + // this is called integer to stay coherent with the constants in Cursor (api level 11) + public static final int FIELD_TYPE_INTEGER = 2; + public static final int FIELD_TYPE_FLOAT = 3; + public static final int FIELD_TYPE_STRING = 4; + public static final int FIELD_TYPE_BLOB = 5; + + public static Object getGenericData(Context context, Uri uri, String column, int type) { + return getGenericData(context, uri, new String[] { column }, new int[] { type }).get(column); + } + public static HashMap<String,Object> getGenericData(Context context, Uri uri, String[] proj, int[] types) { + Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null); + + HashMap<String, Object> result = new HashMap<String, Object>(proj.length); + if (cursor != null && cursor.moveToFirst()) { + int pos = 0; + for(String p : proj) { + switch(types[pos]) { + case FIELD_TYPE_NULL: result.put(p, cursor.isNull(pos)); break; + case FIELD_TYPE_INTEGER: result.put(p, cursor.getLong(pos)); break; + case FIELD_TYPE_FLOAT: result.put(p, cursor.getFloat(pos)); break; + case FIELD_TYPE_STRING: result.put(p, cursor.getString(pos)); break; + case FIELD_TYPE_BLOB: result.put(p, cursor.getBlob(pos)); break; + } + pos += 1; + } + } + + if (cursor != null) { + cursor.close(); + } + + return result; + } + + public static Object getUnifiedData(Context context, long masterKeyId, String column, int type) { + return getUnifiedData(context, masterKeyId, new String[] { column }, new int[] { type }).get(column); + } + public static HashMap<String,Object> getUnifiedData(Context context, long masterKeyId, String[] proj, int[] types) { + return getGenericData(context, KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types); + } + + public static long getMasterKeyId(Context context, Uri queryUri) { + // try extracting from the uri first + String firstSegment = queryUri.getPathSegments().get(1); + if(!firstSegment.equals("find")) try { + return Long.parseLong(firstSegment); + } catch(NumberFormatException e) { + // didn't work? oh well. + Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying..."); + } + Object data = getGenericData(context, queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER); + if(data != null) + return (Long) data; + // TODO better error handling? + return 0L; + } + public static Map<Long, PGPKeyRing> getPGPKeyRings(Context context, Uri queryUri) { Cursor cursor = context.getContentResolver().query(queryUri, - new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA}, null, null, null); + new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA }, + null, null, null); Map<Long, PGPKeyRing> result = new HashMap<Long, PGPKeyRing>(cursor.getCount()); if (cursor != null && cursor.moveToFirst()) do { - int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); - int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - - byte[] data = cursor.getBlob(keyRingDataCol); + long masterKeyId = cursor.getLong(0); + byte[] data = cursor.getBlob(1); if (data != null) { - result.put(cursor.getLong(masterKeyIdCol), PgpConversionHelper.BytesToPGPKeyRing(data)); + result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data)); } - } while(cursor.moveToNext()); if (cursor != null) { @@ -79,82 +143,46 @@ public class ProviderHelper { return result; } public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) { - return getPGPKeyRings(context, queryUri).values().iterator().next(); + Map<Long, PGPKeyRing> result = getPGPKeyRings(context, queryUri); + if(result.isEmpty()) + return null; + return result.values().iterator().next(); } - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + public static PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(Context context, long keyId) { + Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)); + long masterKeyId = getMasterKeyId(context, uri); + if(masterKeyId != 0) + return getPGPPublicKeyRing(context, masterKeyId); + return null; + } + public static PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(Context context, long keyId) { + Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)); + long masterKeyId = getMasterKeyId(context, uri); + if(masterKeyId != 0) + return getPGPSecretKeyRing(context, masterKeyId); + return null; } /** * Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId */ - public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, + public static PGPPublicKeyRing getPGPPublicKeyRing(Context context, long masterKeyId) { - Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key - * with this keyId - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) { - Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId)); + Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); } /** - * Retrieves the actual PGPPublicKey object from the database blob associated with a key with - * this keyId - */ - public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { - PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); - - return (keyRing == null) ? null : keyRing.getPublicKey(keyId); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId */ - public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context, + public static PGPSecretKeyRing getPGPSecretKeyRing(Context context, long masterKeyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); } /** - * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key - * with this keyId - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKey object from the database blob associated with a key with - * this keyId - */ - public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { - PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); - - return (keyRing == null) ? null : keyRing.getSecretKey(keyId); - } - - /** * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") @@ -162,21 +190,12 @@ public class ProviderHelper { PGPPublicKey masterKey = keyRing.getPublicKey(); long masterKeyId = masterKey.getKeyID(); - Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - - // get current _ID of key - long currentRowId = -1; - Cursor oldQuery = context.getContentResolver() - .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); - if (oldQuery != null && oldQuery.moveToFirst()) { - currentRowId = oldQuery.getLong(0); - } else { - Log.e(Constants.TAG, "Key could not be found! Something wrong is happening!"); - } + // IF there is a secret key, preserve it! + PGPSecretKeyRing secretRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId); // delete old version of this keyRing, which also deletes all keys and userIds on cascade try { - context.getContentResolver().delete(deleteUri, null, null); + context.getContentResolver().delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); } catch (UnsupportedOperationException e) { Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); } @@ -186,32 +205,28 @@ public class ProviderHelper { // NOTE: If we would not use the same _ID again, // getting back to the ViewKeyActivity would result in Nullpointer, // because the currently loaded key would be gone from the database - if (currentRowId != -1) { - values.put(KeyRings._ID, currentRowId); - } - values.put(KeyRings.MASTER_KEY_ID, masterKeyId); - values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); // insert new version of this keyRing - Uri uri = KeyRings.buildPublicKeyRingsUri(); + Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); Uri insertedUri = context.getContentResolver().insert(uri, values); - long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); // save all keys and userIds included in keyRing object in database ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); int rank = 0; for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) { - operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); + operations.add(buildPublicKeyOperations(context, masterKeyId, key, rank)); ++rank; } // get a list of owned secret keys, for verification filtering - Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(context, KeyRings.buildSecretKeyRingsUri()); + Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(context, KeyRingData.buildSecretKeyRingUri()); int userIdRank = 0; for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { - operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank)); + operations.add(buildUserIdOperations(context, masterKeyId, userId, userIdRank)); // look through signatures for this specific key for (PGPSignature cert : new IterableIterator<PGPSignature>( @@ -244,8 +259,8 @@ public class ProviderHelper { + PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()) ); // regardless of verification, save the certification - operations.add(buildPublicCertOperations( - context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, verified)); + operations.add(buildCertOperations( + context, masterKeyId, userIdRank, masterKey.getKeyID(), cert, verified)); } ++userIdRank; @@ -258,99 +273,74 @@ public class ProviderHelper { } catch (OperationApplicationException e) { Log.e(Constants.TAG, "applyBatch failed!", e); } + + // Save the saved keyring (if any) + if(secretRing != null) { + saveKeyRing(context, secretRing); + } + } /** - * Saves PGPSecretKeyRing with its keys and userIds in DB + * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring + * is already in the database! */ @SuppressWarnings("unchecked") public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException { - PGPSecretKey masterKey = keyRing.getSecretKey(); - long masterKeyId = masterKey.getKeyID(); - - Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - - // get current _ID of key - long currentRowId = -1; - Cursor oldQuery = context.getContentResolver() - .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); - if (oldQuery != null && oldQuery.moveToFirst()) { - currentRowId = oldQuery.getLong(0); - } else { - Log.e(Constants.TAG, "Key could not be found! Something wrong is happening!"); - } - - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - try { - context.getContentResolver().delete(deleteUri, null, null); - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); - } + long masterKeyId = keyRing.getPublicKey().getKeyID(); + // save secret keyring ContentValues values = new ContentValues(); - // use exactly the same _ID again to replace key in-place. - // NOTE: If we would not use the same _ID again, - // getting back to the ViewKeyActivity would result in Nullpointer, - // because the currently loaded key would be gone from the database - if (currentRowId != -1) { - values.put(KeyRings._ID, currentRowId); - } - values.put(KeyRings.MASTER_KEY_ID, masterKeyId); - values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); - + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); // insert new version of this keyRing - Uri uri = KeyRings.buildSecretKeyRingsUri(); - Uri insertedUri = context.getContentResolver().insert(uri, values); - long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); + Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); + context.getContentResolver().insert(uri, values); - // save all keys and userIds included in keyRing object in database - ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); + } - int rank = 0; - for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { - operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank)); - ++rank; - } + /** + * Saves (or updates) a pair of public and secret KeyRings in the database + */ + @SuppressWarnings("unchecked") + public static void saveKeyRing(Context context, PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException { + long masterKeyId = pubRing.getPublicKey().getKeyID(); - int userIdRank = 0; - for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { - operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank)); - ++userIdRank; - } + // delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below) + context.getContentResolver().delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null); - try { - context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); - } catch (RemoteException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } catch (OperationApplicationException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } + // save public keyring + saveKeyRing(context, pubRing); + saveKeyRing(context, privRing); } /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ private static ContentProviderOperation buildPublicKeyOperations(Context context, - long keyRingRowId, PGPPublicKey key, int rank) throws IOException { + long masterKeyId, PGPPublicKey key, int rank) throws IOException { + ContentValues values = new ContentValues(); + values.put(Keys.MASTER_KEY_ID, masterKeyId); + values.put(Keys.RANK, rank); + values.put(Keys.KEY_ID, key.getKeyID()); - values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); - values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.KEY_SIZE, key.getBitStrength()); - values.put(Keys.CAN_SIGN, PgpKeyHelper.isSigningKey(key)); + values.put(Keys.ALGORITHM, key.getAlgorithm()); + values.put(Keys.FINGERPRINT, key.getFingerprint()); + + values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key))); + values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key))); values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key)); values.put(Keys.IS_REVOKED, key.isRevoked()); + values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); Date expiryDate = PgpKeyHelper.getExpiryDate(key); if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); } - values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); - values.put(Keys.KEY_DATA, key.getEncoded()); - values.put(Keys.RANK, rank); - values.put(Keys.FINGERPRINT, key.getFingerprint()); - Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId)); + Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); return ContentProviderOperation.newInsert(uri).withValues(values).build(); } @@ -358,23 +348,23 @@ public class ProviderHelper { /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ - private static ContentProviderOperation buildPublicCertOperations(Context context, - long keyRingRowId, + private static ContentProviderOperation buildCertOperations(Context context, + long masterKeyId, int rank, long keyId, PGPSignature cert, boolean verified) throws IOException { ContentValues values = new ContentValues(); - values.put(Certs.KEY_RING_ROW_ID, keyRingRowId); + values.put(Certs.MASTER_KEY_ID, masterKeyId); values.put(Certs.RANK, rank); - values.put(Certs.KEY_ID, keyId); values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID()); values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000); + values.put(Certs.EXPIRY, (String) null); // TODO values.put(Certs.VERIFIED, verified); values.put(Certs.KEY_DATA, cert.getEncoded()); - Uri uri = Certs.buildCertsByKeyRowIdUri(Long.toString(keyRingRowId)); + Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId)); return ContentProviderOperation.newInsert(uri).withValues(values).build(); } @@ -382,351 +372,28 @@ public class ProviderHelper { /** * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing */ - private static ContentProviderOperation buildPublicUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { - ContentValues values = new ContentValues(); - values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); - values.put(UserIds.USER_ID, userId); - values.put(UserIds.RANK, rank); - - Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing - */ - private static ContentProviderOperation buildSecretKeyOperations(Context context, - long keyRingRowId, PGPSecretKey key, int rank) throws IOException { - ContentValues values = new ContentValues(); - - boolean hasPrivate = true; - if (key.isMasterKey()) { - if (key.isPrivateKeyEmpty()) { - hasPrivate = false; - } - } - - values.put(Keys.KEY_ID, key.getKeyID()); - values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); - values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm()); - values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength()); - values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate)); - values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate)); - values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key)); - values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); - values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); - Date expiryDate = PgpKeyHelper.getExpiryDate(key); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); - values.put(Keys.KEY_DATA, key.getEncoded()); - values.put(Keys.RANK, rank); - values.put(Keys.FINGERPRINT, key.getPublicKey().getFingerprint()); - - Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing - */ - private static ContentProviderOperation buildSecretUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { + private static ContentProviderOperation buildUserIdOperations(Context context, + long masterKeyId, String userId, int rank) { ContentValues values = new ContentValues(); - values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); + values.put(UserIds.MASTER_KEY_ID, masterKeyId); values.put(UserIds.USER_ID, userId); values.put(UserIds.RANK, rank); - Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId)); + Uri uri = UserIds.buildUserIdsUri(Long.toString(masterKeyId)); return ContentProviderOperation.newInsert(uri).withValues(values).build(); } - /** - * Private helper method - */ - private static ArrayList<Long> getKeyRingsMasterKeyIds(Context context, Uri queryUri) { - Cursor cursor = context.getContentResolver().query(queryUri, - new String[]{KeyRings.MASTER_KEY_ID}, null, null, null); - - ArrayList<Long> masterKeyIds = new ArrayList<Long>(); - if (cursor != null) { - int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - if (cursor.moveToFirst()) { - do { - masterKeyIds.add(cursor.getLong(masterKeyIdCol)); - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - return masterKeyIds; - } - - /** - * Private helper method - */ - private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) { - Cursor cursor = context.getContentResolver().query(queryUri, - new String[]{KeyRings._ID}, null, null, null); - - ArrayList<Long> rowIds = new ArrayList<Long>(); - if (cursor != null) { - int idCol = cursor.getColumnIndex(KeyRings._ID); - if (cursor.moveToFirst()) { - do { - rowIds.add(cursor.getLong(idCol)); - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - return rowIds; - } - - /** - * Retrieves ids of all SecretKeyRings - */ - public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(); - return getKeyRingsMasterKeyIds(context, queryUri); - } - - /** - * Retrieves ids of all PublicKeyRings - */ - public static ArrayList<Long> getPublicKeyRingsMasterKeyIds(Context context) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(); - return getKeyRingsMasterKeyIds(context, queryUri); - } - - /** - * Retrieves ids of all SecretKeyRings - */ - public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(); - return getKeyRingsRowIds(context, queryUri); - } - - /** - * Retrieves ids of all PublicKeyRings - */ - public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(); - return getKeyRingsRowIds(context, queryUri); - } - - public static void deletePublicKeyRing(Context context, long rowId) { - ContentResolver cr = context.getContentResolver(); - cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null); - } - - public static void deleteSecretKeyRing(Context context, long rowId) { - ContentResolver cr = context.getContentResolver(); - cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); - } - - /** - * Get master key id of keyring by its row id - */ - public static long getPublicMasterKeyId(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyId(context, queryUri); - } - - /** - * Get empty status of master key of keyring by its row id - */ - public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyCanSign(context, queryUri); - } - - /** - * Private helper method to get master key private empty status of keyring by its row id - */ - public static boolean getMasterKeyCanSign(Context context, Uri queryUri) { - String[] projection = new String[]{ - KeyRings.MASTER_KEY_ID, - "(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS sign_keys WHERE sign_keys." + Keys.KEY_RING_ROW_ID + " = " - + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID - + " AND sign_keys." + Keys.CAN_SIGN + " = '1' AND " + Keys.IS_MASTER_KEY - + " = 1) AS sign", }; - - ContentResolver cr = context.getContentResolver(); - Cursor cursor = cr.query(queryUri, projection, null, null, null); - - long masterKeyId = -1; - if (cursor != null && cursor.moveToFirst()) { - int masterKeyIdCol = cursor.getColumnIndex("sign"); - - masterKeyId = cursor.getLong(masterKeyIdCol); - } - - if (cursor != null) { - cursor.close(); - } - - return (masterKeyId > 0); - } - - public static boolean hasSecretKeyByMasterKeyId(Context context, long masterKeyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - // see if we can get our master key id back from the uri - return getMasterKeyId(context, queryUri) == masterKeyId; - } - - /** - * Get master key id of keyring by its row id - */ - public static long getSecretMasterKeyId(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyId(context, queryUri); - } - - /** - * Get master key id of key - */ - public static long getMasterKeyId(Context context, Uri queryUri) { - String[] projection = new String[]{KeyRings.MASTER_KEY_ID}; - Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null); - - long masterKeyId = 0; - try { - if (cursor != null && cursor.moveToFirst()) { - int masterKeyIdCol = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID); - - masterKeyId = cursor.getLong(masterKeyIdCol); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - return masterKeyId; - } - - public static long getRowId(Context context, Uri queryUri) { - String[] projection = new String[]{KeyRings._ID}; - Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null); - - long rowId = 0; - try { - if (cursor != null && cursor.moveToFirst()) { - int idCol = cursor.getColumnIndexOrThrow(KeyRings._ID); - - rowId = cursor.getLong(idCol); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - return rowId; - } - - /** - * Get fingerprint of key - */ - public static byte[] getFingerprint(Context context, Uri queryUri) { - String[] projection = new String[]{Keys.FINGERPRINT}; - Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null); - - byte[] fingerprint = null; - try { - if (cursor != null && cursor.moveToFirst()) { - int col = cursor.getColumnIndexOrThrow(Keys.FINGERPRINT); - - fingerprint = cursor.getBlob(col); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - // FALLBACK: If fingerprint is not in database, get it from key blob! - // this could happen if the key was saved by a previous version of Keychain! - if (fingerprint == null) { - Log.d(Constants.TAG, "FALLBACK: fingerprint is not in database, get it from key blob!"); - - // get master key id - projection = new String[]{KeyRings.MASTER_KEY_ID}; - cursor = context.getContentResolver().query(queryUri, projection, null, null, null); - long masterKeyId = 0; - try { - if (cursor != null && cursor.moveToFirst()) { - int col = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID); - - masterKeyId = cursor.getLong(col); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, masterKeyId); - // if it is no public key get it from your own keys... - if (key == null) { - PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, masterKeyId); - if (secretKey == null) { - Log.e(Constants.TAG, "Key could not be found!"); - return null; - } - key = secretKey.getPublicKey(); - } - - fingerprint = key.getFingerprint(); - } - - return fingerprint; - } - - public static String getUserId(Context context, Uri queryUri) { - String[] projection = new String[]{UserIds.USER_ID}; - Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null); - - String userId = null; - try { - if (cursor != null && cursor.moveToFirst()) { - int col = cursor.getColumnIndexOrThrow(UserIds.USER_ID); - - userId = cursor.getString(col); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - return userId; - } - - public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri, - long[] masterKeyIds) { + public static ArrayList<String> getKeyRingsAsArmoredString(Context context, long[] masterKeyIds) { ArrayList<String> output = new ArrayList<String>(); if (masterKeyIds != null && masterKeyIds.length > 0) { - Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds); + Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, masterKeyIds); if (cursor != null) { - int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); + int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID); + int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA); if (cursor.moveToFirst()) { do { Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); @@ -776,48 +443,11 @@ public class ProviderHelper { return null; } } - - public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - if (masterKeyIds != null && masterKeyIds.length > 0) { - - Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds); - - if (cursor != null) { - int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); - if (cursor.moveToFirst()) { - do { - Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); - - // get actual keyring data blob and write it to ByteArrayOutputStream - try { - bos.write(cursor.getBlob(dataCol)); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException", e); - } - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - } else { - Log.e(Constants.TAG, "No master keys given!"); - } - - return bos.toByteArray(); - } - - private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, Uri baseUri, - long[] masterKeyIds) { + private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, long[] masterKeyIds) { Cursor cursor = null; if (masterKeyIds != null && masterKeyIds.length > 0) { - String inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; + String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN ("; for (int i = 0; i < masterKeyIds.length; ++i) { if (i != 0) { inMasterKeyList += ", "; @@ -826,9 +456,9 @@ public class ProviderHelper { } inMasterKeyList += ")"; - cursor = context.getContentResolver().query(baseUri, - new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA}, - inMasterKeyList, null, null); + cursor = context.getContentResolver().query(KeyRingData.buildPublicKeyRingUri(), new String[] { + KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA + }, inMasterKeyList, null, null); } return cursor; @@ -859,19 +489,28 @@ public class ProviderHelper { 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()); - values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm()); + return values; + } + private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) { + ContentValues values = new ContentValues(); + values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName()); + values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId()); + values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression()); + values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm()); + values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm()); return values; } public static void insertApiApp(Context context, AppSettings appSettings) { - context.getContentResolver().insert(ApiApps.CONTENT_URI, + context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI, contentValueForApiApps(appSettings)); } + public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) { + context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings)); + } + public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) { if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null, null) <= 0) { @@ -879,30 +518,73 @@ public class ProviderHelper { } } + public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) { + if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null, + null) <= 0) { + throw new RuntimeException(); + } + } + + /** + * Must be an uri pointing to an account + * + * @param context + * @param uri + * @return + */ public static AppSettings getApiAppSettings(Context context, Uri uri) { AppSettings settings = null; Cursor cur = context.getContentResolver().query(uri, null, null, null, null); if (cur != null && cur.moveToFirst()) { 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))); - settings.setHashAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM))); - settings.setEncryptionAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM))); + settings.setPackageName(cur.getString( + cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME))); + settings.setPackageSignature(cur.getBlob( + cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE))); } return settings; } + public static AccountSettings getApiAccountSettings(Context context, Uri accountUri) { + AccountSettings settings = null; + + Cursor cur = context.getContentResolver().query(accountUri, null, null, null, null); + if (cur != null && cur.moveToFirst()) { + settings = new AccountSettings(); + + settings.setAccountName(cur.getString( + cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME))); + settings.setKeyId(cur.getLong( + cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID))); + settings.setCompression(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION))); + settings.setHashAlgorithm(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM))); + settings.setEncryptionAlgorithm(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM))); + } + + return settings; + } + + public static Set<Long> getAllKeyIdsForApp(Context context, Uri uri) { + Set<Long> keyIds = new HashSet<Long>(); + + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); + if (cursor != null) { + int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID); + while (cursor.moveToNext()) { + keyIds.add(cursor.getLong(keyIdColumn)); + } + } + + return keyIds; + } + public static byte[] getApiAppSignature(Context context, String packageName) { - Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName); + Uri queryUri = ApiApps.buildByPackageNameUri(packageName); String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE}; |