diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index ec975a232..06fa702fa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -58,6 +58,11 @@ public class KeychainContract { String SEEN_ON_KEYSERVERS = "seen_on_keyservers"; } + interface KeySignaturesColumns { + String MASTER_KEY_ID = "master_key_id"; // not a database id + String SIGNER_KEY_ID = "signer_key_id"; + } + interface UserPacketsColumns { String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID String TYPE = "type"; // not a database id @@ -113,6 +118,8 @@ public class KeychainContract { public static final String BASE_UPDATED_KEYS = "updated_keys"; + public static final String BASE_KEY_SIGNATURES = "key_signatures"; + public static final String PATH_UNIFIED = "unified"; public static final String PATH_FIND = "find"; @@ -120,6 +127,9 @@ public class KeychainContract { public static final String PATH_BY_SUBKEY = "subkey"; public static final String PATH_BY_USER_ID = "user_id"; + public static final String PATH_FILTER = "filter"; + public static final String PATH_BY_SIGNER = "signer"; + public static final String PATH_PUBLIC = "public"; public static final String PATH_SECRET = "secret"; public static final String PATH_USER_IDS = "user_ids"; @@ -198,6 +208,9 @@ public class KeychainContract { .appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build(); } + public static Uri buildUnifiedKeyRingsFilterBySigner() { + return CONTENT_URI.buildUpon().appendPath(PATH_FILTER).appendPath(PATH_BY_SIGNER).build(); + } } public static class KeyRingData implements KeyRingsColumns, BaseColumns { @@ -271,6 +284,14 @@ public class KeychainContract { = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.updated_keys"; } + public static class KeySignatures implements KeySignaturesColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_SIGNATURES).build(); + + public static final String CONTENT_TYPE + = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.key_signatures"; + } + public static class UserPackets implements UserPacketsColumns, BaseColumns { public static final String VERIFIED = "verified"; public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index a340623c4..54ad080aa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPee import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignaturesColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.OverriddenWarnings; import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; @@ -54,7 +55,7 @@ import org.sufficientlysecure.keychain.util.Log; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 23; + private static final int DATABASE_VERSION = 24; private Context mContext; public interface Tables { @@ -62,6 +63,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { String KEY_RINGS_SECRET = "keyrings_secret"; String KEYS = "keys"; String UPDATED_KEYS = "updated_keys"; + String KEY_SIGNATURES = "key_signatures"; String USER_PACKETS = "user_packets"; String CERTS = "certs"; String API_APPS = "api_apps"; @@ -159,6 +161,15 @@ public class KeychainDatabase extends SQLiteOpenHelper { + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; + private static final String CREATE_KEY_SIGNATURES = + "CREATE TABLE IF NOT EXISTS " + Tables.KEY_SIGNATURES + " (" + + KeySignaturesColumns.MASTER_KEY_ID + " INTEGER NOT NULL, " + + KeySignaturesColumns.SIGNER_KEY_ID + " INTEGER NOT NULL, " + + "PRIMARY KEY(" + KeySignaturesColumns.MASTER_KEY_ID + ", " + KeySignaturesColumns.SIGNER_KEY_ID + "), " + + "FOREIGN KEY(" + KeySignaturesColumns.MASTER_KEY_ID + ") REFERENCES " + + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + + ")"; + private static final String CREATE_API_AUTOCRYPT_PEERS = "CREATE TABLE IF NOT EXISTS " + Tables.API_AUTOCRYPT_PEERS + " (" + ApiAutocryptPeerColumns.PACKAGE_NAME + " TEXT NOT NULL, " @@ -213,6 +224,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_USER_PACKETS); db.execSQL(CREATE_CERTS); db.execSQL(CREATE_UPDATE_KEYS); + db.execSQL(CREATE_KEY_SIGNATURES); db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); db.execSQL(CREATE_OVERRIDDEN_WARNINGS); @@ -387,7 +399,16 @@ public class KeychainDatabase extends SQLiteOpenHelper { + "FOREIGN KEY(package_name) REFERENCES api_apps(package_name) ON DELETE CASCADE" + ")"); - if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20 || oldVersion == 21 || oldVersion == 22) { + case 23: + db.execSQL("CREATE TABLE IF NOT EXISTS key_signatures (" + + "master_key_id INTEGER NOT NULL, " + + "signer_key_id INTEGER NOT NULL, " + + "PRIMARY KEY(master_key_id, signer_key_id), " + + "FOREIGN KEY(master_key_id) REFERENCES keyrings_public(master_key_id) ON DELETE CASCADE" + + ")"); + + if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20 || oldVersion == 21 || oldVersion == 22 || + oldVersion == 23) { return; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index cab6dea30..795ac7487 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPee import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignatures; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; @@ -77,6 +78,7 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RINGS_FIND_BY_EMAIL = 400; private static final int KEY_RINGS_FIND_BY_SUBKEY = 401; private static final int KEY_RINGS_FIND_BY_USER_ID = 402; + private static final int KEY_RINGS_FILTER_BY_SIGNER = 403; private static final int UPDATED_KEYS = 500; private static final int UPDATED_KEYS_SPECIFIC = 501; @@ -85,6 +87,8 @@ public class KeychainProvider extends ContentProvider { private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME = 602; private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID = 603; + private static final int KEY_SIGNATURES = 700; + protected UriMatcher mUriMatcher; /** @@ -135,6 +139,9 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_USER_ID + "/*", KEY_RINGS_FIND_BY_USER_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + + KeychainContract.PATH_FILTER + "/" + KeychainContract.PATH_BY_SIGNER, + KEY_RINGS_FILTER_BY_SIGNER); /** * list key_ring specifics @@ -219,6 +226,10 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS); matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS + "/*", UPDATED_KEYS_SPECIFIC); + + matcher.addURI(authority, KeychainContract.BASE_KEY_SIGNATURES, KEY_SIGNATURES); + + return matcher; } @@ -260,9 +271,13 @@ public class KeychainProvider extends ContentProvider { case UPDATED_KEYS: return UpdatedKeys.CONTENT_TYPE; + case UPDATED_KEYS_SPECIFIC: return UpdatedKeys.CONTENT_ITEM_TYPE; + case KEY_SIGNATURES: + return KeySignatures.CONTENT_TYPE; + case API_APPS: return ApiApps.CONTENT_TYPE; @@ -297,7 +312,8 @@ public class KeychainProvider extends ContentProvider { case KEY_RINGS_UNIFIED: case KEY_RINGS_FIND_BY_EMAIL: case KEY_RINGS_FIND_BY_SUBKEY: - case KEY_RINGS_FIND_BY_USER_ID: { + case KEY_RINGS_FIND_BY_USER_ID: + case KEY_RINGS_FILTER_BY_SIGNER: { HashMap projectionMap = new HashMap<>(); projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id"); projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); @@ -446,6 +462,23 @@ public class KeychainProvider extends ContentProvider { } break; } + case KEY_RINGS_FILTER_BY_SIGNER: { + StringBuilder signerKeyIds = new StringBuilder(); + signerKeyIds.append(selectionArgs[0]); + for (int i = 1; i < selectionArgs.length; i++) { + signerKeyIds.append(',').append(selectionArgs[i]); + } + + qb.appendWhere(" AND EXISTS (SELECT 1 FROM " + Tables.KEY_SIGNATURES + " WHERE " + + Tables.KEY_SIGNATURES + "." + KeySignatures.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " AND " + + Tables.KEY_SIGNATURES + "." + KeySignatures.SIGNER_KEY_ID + " IN (" + signerKeyIds + ")" + + ")"); + + selection = null; + selectionArgs = null; + break; + } case KEY_RINGS_FIND_BY_EMAIL: case KEY_RINGS_FIND_BY_USER_ID: { String chunks[] = uri.getLastPathSegment().split(" *, *"); @@ -860,6 +893,11 @@ public class KeychainProvider extends ContentProvider { rowUri = UpdatedKeys.CONTENT_URI; break; } + case KEY_SIGNATURES: { + db.insert(Tables.KEY_SIGNATURES, null, values); + rowUri = KeySignatures.CONTENT_URI; + break; + } case API_APPS: { db.insertOrThrow(Tables.API_APPS, null, values); break;