aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java106
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java172
4 files changed, 232 insertions, 91 deletions
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 2c02e429d..f4e00c36c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -51,9 +51,11 @@ public class KeychainContract {
String EXPIRY = "expiry";
}
- interface UserIdsColumns {
+ interface UserPacketsColumns {
String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
+ String TYPE = "type"; // not a database id
String USER_ID = "user_id"; // not a database id
+ String ATTRIBUTE_DATA = "attribute_data"; // not a database id
String RANK = "rank"; // ONLY used for sorting! no key, no nothing!
String IS_PRIMARY = "is_primary";
String IS_REVOKED = "is_revoked";
@@ -105,7 +107,7 @@ public class KeychainContract {
public static final String BASE_API_APPS = "api_apps";
public static final String PATH_ACCOUNTS = "accounts";
- public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns {
+ public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns {
public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID;
public static final String IS_REVOKED = KeysColumns.IS_REVOKED;
public static final String VERIFIED = CertsColumns.VERIFIED;
@@ -225,7 +227,7 @@ public class KeychainContract {
}
- public static class UserIds implements UserIdsColumns, BaseColumns {
+ public static class UserPackets implements UserPacketsColumns, BaseColumns {
public static final String VERIFIED = "verified";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@@ -304,7 +306,7 @@ public class KeychainContract {
}
public static class Certs implements CertsColumns, BaseColumns {
- public static final String USER_ID = UserIdsColumns.USER_ID;
+ public static final String USER_ID = UserPacketsColumns.USER_ID;
public static final String SIGNER_UID = "signer_user_id";
public static final int UNVERIFIED = 0;
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 84a50dc65..5ce5eec17 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -33,7 +33,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
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.UserIdsColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log;
@@ -52,7 +52,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
- private static final int DATABASE_VERSION = 6;
+ private static final int DATABASE_VERSION = 7;
static Boolean apgHack = false;
private Context mContext;
@@ -60,7 +60,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
String KEY_RINGS_PUBLIC = "keyrings_public";
String KEY_RINGS_SECRET = "keyrings_secret";
String KEYS = "keys";
- String USER_IDS = "user_ids";
+ String USER_PACKETS = "user_ids";
String CERTS = "certs";
String API_APPS = "api_apps";
String API_ACCOUNTS = "api_accounts";
@@ -106,18 +106,20 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ 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 + " TEXT, "
+ private static final String CREATE_USER_PACKETS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.USER_PACKETS + "("
+ + UserPacketsColumns.MASTER_KEY_ID + " INTEGER, "
+ + UserPacketsColumns.TYPE + " INT, "
+ + UserPacketsColumns.USER_ID + " TEXT, "
+ + UserPacketsColumns.ATTRIBUTE_DATA + " BLOB, "
- + UserIdsColumns.IS_PRIMARY + " INTEGER, "
- + UserIdsColumns.IS_REVOKED + " INTEGER, "
- + UserIdsColumns.RANK+ " INTEGER, "
+ + UserPacketsColumns.IS_PRIMARY + " INTEGER, "
+ + UserPacketsColumns.IS_REVOKED + " INTEGER, "
+ + UserPacketsColumns.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 "
+ + "PRIMARY KEY(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.USER_ID + "), "
+ + "UNIQUE (" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + "), "
+ + "FOREIGN KEY(" + UserPacketsColumns.MASTER_KEY_ID + ") REFERENCES "
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ ")";
@@ -138,7 +140,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ "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"
+ + Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE"
+ ")";
private static final String CREATE_API_APPS =
@@ -189,7 +191,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_KEYRINGS_PUBLIC);
db.execSQL(CREATE_KEYRINGS_SECRET);
db.execSQL(CREATE_KEYS);
- db.execSQL(CREATE_USER_IDS);
+ db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS);
db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_API_APPS_ACCOUNTS);
@@ -238,6 +240,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
case 5:
// do consolidate for 3.0 beta3
// fall through
+ case 6:
+ db.execSQL("ALTER TABLE user_ids ADD COLUMN type INTEGER");
+ db.execSQL("ALTER TABLE user_ids ADD COLUMN attribute_data BLOB");
}
// always do consolidate after upgrade
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 d40287690..72475472e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -37,7 +37,8 @@ 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.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@@ -205,7 +206,7 @@ public class KeychainProvider extends ContentProvider {
return Keys.CONTENT_TYPE;
case KEY_RING_USER_IDS:
- return UserIds.CONTENT_TYPE;
+ return UserPackets.CONTENT_TYPE;
case KEY_RING_SECRET:
return KeyRings.CONTENT_ITEM_TYPE;
@@ -247,7 +248,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_UNIFIED:
case KEY_RINGS_FIND_BY_EMAIL:
case KEY_RINGS_FIND_BY_SUBKEY: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
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, Tables.KEYS + "." + Keys.KEY_ID);
@@ -262,7 +263,7 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
- projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
+ projectionMap.put(KeyRings.USER_ID, UserPackets.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
projectionMap.put(KeyRings.PUBKEY_DATA,
Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
@@ -296,11 +297,12 @@ public class KeychainProvider extends ContentProvider {
qb.setTables(
Tables.KEYS
- + " INNER JOIN " + Tables.USER_IDS + " ON ("
+ + " INNER JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
- + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0"
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ // we KNOW that the rank zero user packet is a user id!
+ + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = 0"
+ ") LEFT JOIN " + Tables.CERTS + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
@@ -376,7 +378,7 @@ public class KeychainProvider extends ContentProvider {
String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
qb.appendWhere(" AND EXISTS ("
+ " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
- + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
+ ")");
@@ -398,15 +400,15 @@ public class KeychainProvider extends ContentProvider {
if (i != 0) {
emailWhere += " OR ";
}
- emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
+ emailWhere += "tmp." + UserPackets.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
+ + " SELECT 1 FROM " + Tables.USER_PACKETS + " AS tmp"
+ + " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND (" + emailWhere + ")"
+ ")");
@@ -420,7 +422,7 @@ public class KeychainProvider extends ContentProvider {
}
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIds.USER_ID + " ASC";
+ sortOrder = Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC";
}
// uri to watch is all /key_rings/
@@ -430,7 +432,7 @@ public class KeychainProvider extends ContentProvider {
}
case KEY_RING_KEYS: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
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);
@@ -458,37 +460,45 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_USER_IDS:
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, Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID);
- projectionMap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
- projectionMap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK);
- projectionMap.put(UserIds.IS_PRIMARY, Tables.USER_IDS + "." + UserIds.IS_PRIMARY);
- projectionMap.put(UserIds.IS_REVOKED, Tables.USER_IDS + "." + UserIds.IS_REVOKED);
+ HashMap<String, String> projectionMap = new HashMap<>();
+ projectionMap.put(UserPackets._ID, Tables.USER_PACKETS + ".oid AS _id");
+ projectionMap.put(UserPackets.MASTER_KEY_ID, Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID);
+ projectionMap.put(UserPackets.TYPE, Tables.USER_PACKETS + "." + UserPackets.TYPE);
+ projectionMap.put(UserPackets.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
+ projectionMap.put(UserPackets.ATTRIBUTE_DATA, Tables.USER_PACKETS + "." + UserPackets.ATTRIBUTE_DATA);
+ projectionMap.put(UserPackets.RANK, Tables.USER_PACKETS + "." + UserPackets.RANK);
+ projectionMap.put(UserPackets.IS_PRIMARY, Tables.USER_PACKETS + "." + UserPackets.IS_PRIMARY);
+ projectionMap.put(UserPackets.IS_REVOKED, Tables.USER_PACKETS + "." + UserPackets.IS_REVOKED);
// we take the minimum (>0) here, where "1" is "verified by known secret key"
- projectionMap.put(UserIds.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserIds.VERIFIED);
+ projectionMap.put(UserPackets.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserPackets.VERIFIED);
qb.setProjectionMap(projectionMap);
- qb.setTables(Tables.USER_IDS
+ qb.setTables(Tables.USER_PACKETS
+ " LEFT JOIN " + Tables.CERTS + " ON ("
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = "
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID
- + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = "
+ + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = "
+ Tables.CERTS + "." + Certs.RANK
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
+ ")");
- groupBy = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
- + ", " + Tables.USER_IDS + "." + UserIds.RANK;
+ groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ + ", " + Tables.USER_PACKETS + "." + UserPackets.RANK;
+
+ // for now, we only respect user ids here, so TYPE must be NULL
+ // TODO expand with KEY_RING_USER_PACKETS query type which lifts this restriction
+ qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL");
// If we are searching for a particular keyring's ids, add where
if (match == KEY_RING_USER_IDS) {
- qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = ");
+ // TODO remove with the thing above
+ qb.appendWhere(" AND ");
+ qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
}
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC"
- + "," + Tables.USER_IDS + "." + UserIds.RANK + " ASC";
+ sortOrder = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
+ + "," + Tables.USER_PACKETS + "." + UserPackets.RANK + " ASC";
}
break;
@@ -497,7 +507,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_PUBLIC:
case KEY_RING_PUBLIC: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
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);
@@ -515,7 +525,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_SECRET:
case KEY_RING_SECRET: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
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);
@@ -533,7 +543,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_CERTS:
case KEY_RING_CERTS_SPECIFIC: {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
+ HashMap<String, String> projectionMap = new HashMap<>();
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);
@@ -542,20 +552,24 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA);
- projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
- projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
+ projectionMap.put(Certs.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
+ projectionMap.put(Certs.SIGNER_UID, "signer." + UserPackets.USER_ID + " AS " + Certs.SIGNER_UID);
qb.setProjectionMap(projectionMap);
qb.setTables(Tables.CERTS
- + " JOIN " + Tables.USER_IDS + " ON ("
+ + " JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = "
- + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " AND "
+ Tables.CERTS + "." + Certs.RANK + " = "
- + Tables.USER_IDS + "." + UserIds.RANK
- + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON ("
+ + Tables.USER_PACKETS + "." + UserPackets.RANK
+ // for now, we only return user ids here, so TYPE must be NULL
+ // TODO at some point, we should lift this restriction
+ + " AND "
+ + Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"
+ + ") LEFT JOIN " + Tables.USER_PACKETS + " AS signer ON ("
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = "
- + "signer." + UserIds.MASTER_KEY_ID
+ + "signer." + UserPackets.MASTER_KEY_ID
+ " AND "
+ "signer." + Keys.RANK + " = 0"
+ ")");
@@ -662,8 +676,18 @@ public class KeychainProvider extends ContentProvider {
break;
case KEY_RING_USER_IDS:
- db.insertOrThrow(Tables.USER_IDS, null, values);
- keyId = values.getAsLong(UserIds.MASTER_KEY_ID);
+ // iff TYPE is null, user_id MUST be null as well
+ if ( ! (values.get(UserPacketsColumns.TYPE) == null
+ ? (values.get(UserPacketsColumns.USER_ID) != null && values.get(UserPacketsColumns.ATTRIBUTE_DATA) == null)
+ : (values.get(UserPacketsColumns.ATTRIBUTE_DATA) != null && values.get(UserPacketsColumns.USER_ID) == null)
+ )) {
+ throw new AssertionError("Incorrect type for user packet! This is a bug!");
+ }
+ if (values.get(UserPacketsColumns.RANK) == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
+ throw new AssertionError("Rank 0 user packet must be a user id!");
+ }
+ db.insertOrThrow(Tables.USER_PACKETS, null, values);
+ keyId = values.getAsLong(UserPackets.MASTER_KEY_ID);
break;
case KEY_RING_CERTS:
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 4f1b4b6c1..a229f454f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -31,6 +31,8 @@ import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences;
@@ -53,7 +55,6 @@ 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.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@@ -169,7 +170,7 @@ public class ProviderHelper {
Cursor cursor = mContentResolver.query(uri, proj, selection, null, null);
try {
- HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
+ HashMap<String, Object> result = new HashMap<>(proj.length);
if (cursor != null && cursor.moveToFirst()) {
int pos = 0;
for (String p : proj) {
@@ -220,7 +221,7 @@ public class ProviderHelper {
}, KeyRings.HAS_ANY_SECRET + " = 1", null, null);
try {
- LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<CanonicalizedPublicKey>();
+ LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<>();
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
@@ -349,7 +350,7 @@ public class ProviderHelper {
mIndent += 1;
// save all keys and userIds included in keyRing object in database
- operations = new ArrayList<ContentProviderOperation>();
+ operations = new ArrayList<>();
log(LogType.MSG_IP_INSERT_KEYRING);
{ // insert keyring
@@ -439,18 +440,18 @@ public class ProviderHelper {
// classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved.
+ List<UserPacketItem> uids = new ArrayList<>();
+
if (trustedKeys.size() == 0) {
log(LogType.MSG_IP_UID_CLASSIFYING_ZERO);
} else {
log(LogType.MSG_IP_UID_CLASSIFYING, trustedKeys.size());
}
mIndent += 1;
- List<UserIdItem> uids = new ArrayList<UserIdItem>();
- for (byte[] rawUserId : new IterableIterator<byte[]>(
- masterKey.getUnorderedRawUserIds().iterator())) {
+ for (byte[] rawUserId : masterKey.getUnorderedRawUserIds()) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
- UserIdItem item = new UserIdItem();
+ UserPacketItem item = new UserPacketItem();
uids.add(item);
item.userId = userId;
@@ -459,7 +460,7 @@ public class ProviderHelper {
log(LogType.MSG_IP_UID_PROCESSING, userId);
mIndent += 1;
// look through signatures for this specific key
- for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
+ for (WrappedSignature cert : new IterableIterator<>(
masterKey.getSignaturesForRawId(rawUserId))) {
long certId = cert.getKeyId();
// self signature
@@ -533,6 +534,105 @@ public class ProviderHelper {
}
mIndent -= 1;
+ ArrayList<WrappedUserAttribute> userAttributes = masterKey.getUnorderedUserAttributes();
+ // Don't spam the log if there aren't even any attributes
+ if ( ! userAttributes.isEmpty()) {
+ log(LogType.MSG_IP_UAT_CLASSIFYING);
+ }
+
+ mIndent += 1;
+ for (WrappedUserAttribute userAttribute : userAttributes) {
+
+ UserPacketItem item = new UserPacketItem();
+ uids.add(item);
+ item.type = userAttribute.getType();
+ item.attributeData = userAttribute.getEncoded();
+
+ int unknownCerts = 0;
+
+ switch (item.type) {
+ case WrappedUserAttribute.UAT_IMAGE:
+ log(LogType.MSG_IP_UAT_PROCESSING_IMAGE);
+ break;
+ default:
+ log(LogType.MSG_IP_UAT_PROCESSING_UNKNOWN);
+ break;
+ }
+ mIndent += 1;
+ // look through signatures for this specific key
+ for (WrappedSignature cert : new IterableIterator<>(
+ masterKey.getSignaturesForUserAttribute(userAttribute))) {
+ long certId = cert.getKeyId();
+ // self signature
+ if (certId == masterKeyId) {
+
+ // NOTE self-certificates are already verified during canonicalization,
+ // AND we know there is at most one cert plus at most one revocation
+ if (!cert.isRevocation()) {
+ item.selfCert = cert;
+ } else {
+ item.isRevoked = true;
+ log(LogType.MSG_IP_UAT_REVOKED);
+ }
+ continue;
+
+ }
+
+ // do we have a trusted key for this?
+ if (trustedKeys.indexOfKey(certId) < 0) {
+ unknownCerts += 1;
+ continue;
+ }
+
+ // verify signatures from known private keys
+ CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);
+
+ try {
+ cert.init(trustedKey);
+ // if it doesn't certify, leave a note and skip
+ if ( ! cert.verifySignature(masterKey, userAttribute)) {
+ log(LogType.MSG_IP_UAT_CERT_BAD);
+ continue;
+ }
+
+ log(cert.isRevocation()
+ ? LogType.MSG_IP_UAT_CERT_GOOD_REVOKE
+ : LogType.MSG_IP_UAT_CERT_GOOD,
+ KeyFormattingUtils.convertKeyIdToHexShort(trustedKey.getKeyId())
+ );
+
+ // check if there is a previous certificate
+ WrappedSignature prev = item.trustedCerts.get(cert.getKeyId());
+ if (prev != null) {
+ // if it's newer, skip this one
+ if (prev.getCreationTime().after(cert.getCreationTime())) {
+ log(LogType.MSG_IP_UAT_CERT_OLD);
+ continue;
+ }
+ // if the previous one was a non-revokable certification, no need to look further
+ if (!prev.isRevocation() && !prev.isRevokable()) {
+ log(LogType.MSG_IP_UAT_CERT_NONREVOKE);
+ continue;
+ }
+ log(LogType.MSG_IP_UAT_CERT_NEW);
+ }
+ item.trustedCerts.put(cert.getKeyId(), cert);
+
+ } catch (PgpGeneralException e) {
+ log(LogType.MSG_IP_UAT_CERT_ERROR,
+ KeyFormattingUtils.convertKeyIdToHex(cert.getKeyId()));
+ }
+
+ }
+
+ if (unknownCerts > 0) {
+ log(LogType.MSG_IP_UAT_CERTS_UNKNOWN, unknownCerts);
+ }
+ mIndent -= 1;
+
+ }
+ mIndent -= 1;
+
progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 65, 100);
log(LogType.MSG_IP_UID_REORDER);
// primary before regular before revoked (see UserIdItem.compareTo)
@@ -540,7 +640,7 @@ public class ProviderHelper {
Collections.sort(uids);
// iterate and put into db
for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) {
- UserIdItem item = uids.get(userIdRank);
+ UserPacketItem item = uids.get(userIdRank);
operations.add(buildUserIdOperations(masterKeyId, item, userIdRank));
if (item.selfCert != null) {
// TODO get rid of "self verified" status? this cannot even happen anymore!
@@ -605,23 +705,31 @@ public class ProviderHelper {
}
- private static class UserIdItem implements Comparable<UserIdItem> {
+ private static class UserPacketItem implements Comparable<UserPacketItem> {
+ Integer type;
String userId;
+ byte[] attributeData;
boolean isPrimary = false;
boolean isRevoked = false;
WrappedSignature selfCert;
- LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<WrappedSignature>();
+ LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<>();
@Override
- public int compareTo(UserIdItem o) {
- // if one key is primary but the other isn't, the primary one always comes first
- if (isPrimary != o.isPrimary) {
- return isPrimary ? -1 : 1;
- }
+ public int compareTo(UserPacketItem o) {
// revoked keys always come last!
if (isRevoked != o.isRevoked) {
return isRevoked ? 1 : -1;
}
+ // if one is a user id, but the other isn't, the user id always comes first.
+ // we compare for null values here, so != is the correct operator!
+ // noinspection NumberEquality
+ if (type != o.type) {
+ return type == null ? -1 : 1;
+ }
+ // if one key is primary but the other isn't, the primary one always comes first
+ if (isPrimary != o.isPrimary) {
+ return isPrimary ? -1 : 1;
+ }
return 0;
}
}
@@ -967,7 +1075,7 @@ public class ProviderHelper {
// No keys existing might be a legitimate option, we write an empty file in that case
cursor.moveToFirst();
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_secret.pcl");
+ new ParcelableFileCache<>(mContext, "consolidate_secret.pcl");
cache.writeCache(cursor.getCount(), new Iterator<ParcelableKeyRing>() {
ParcelableKeyRing ring;
@@ -1029,7 +1137,7 @@ public class ProviderHelper {
// No keys existing might be a legitimate option, we write an empty file in that case
cursor.moveToFirst();
ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_public.pcl");
+ new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
cache.writeCache(cursor.getCount(), new Iterator<ParcelableKeyRing>() {
ParcelableKeyRing ring;
@@ -1112,9 +1220,9 @@ public class ProviderHelper {
mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null);
ParcelableFileCache<ParcelableKeyRing> cacheSecret =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_secret.pcl");
+ new ParcelableFileCache<>(mContext, "consolidate_secret.pcl");
ParcelableFileCache<ParcelableKeyRing> cachePublic =
- new ParcelableFileCache<ParcelableKeyRing>(mContext, "consolidate_public.pcl");
+ new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
// Set flag that we have a cached consolidation here
try {
@@ -1234,15 +1342,17 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
*/
private ContentProviderOperation
- buildUserIdOperations(long masterKeyId, UserIdItem item, int rank) {
+ buildUserIdOperations(long masterKeyId, UserPacketItem item, int rank) {
ContentValues values = new ContentValues();
- values.put(UserIds.MASTER_KEY_ID, masterKeyId);
- values.put(UserIds.USER_ID, item.userId);
- values.put(UserIds.IS_PRIMARY, item.isPrimary);
- values.put(UserIds.IS_REVOKED, item.isRevoked);
- values.put(UserIds.RANK, rank);
+ values.put(UserPackets.MASTER_KEY_ID, masterKeyId);
+ values.put(UserPackets.TYPE, item.type);
+ values.put(UserPackets.USER_ID, item.userId);
+ values.put(UserPackets.ATTRIBUTE_DATA, item.attributeData);
+ values.put(UserPackets.IS_PRIMARY, item.isPrimary);
+ values.put(UserPackets.IS_REVOKED, item.isRevoked);
+ values.put(UserPackets.RANK, rank);
- Uri uri = UserIds.buildUserIdsUri(masterKeyId);
+ Uri uri = UserPackets.buildUserIdsUri(masterKeyId);
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -1270,7 +1380,7 @@ public class ProviderHelper {
public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
- ArrayList<String> packageNames = new ArrayList<String>();
+ ArrayList<String> packageNames = new ArrayList<>();
try {
if (cursor != null) {
int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
@@ -1375,7 +1485,7 @@ public class ProviderHelper {
}
public Set<Long> getAllKeyIdsForApp(Uri uri) {
- Set<Long> keyIds = new HashSet<Long>();
+ Set<Long> keyIds = new HashSet<>();
Cursor cursor = mContentResolver.query(uri, null, null, null, null);
try {
@@ -1395,7 +1505,7 @@ public class ProviderHelper {
}
public Set<String> getAllFingerprints(Uri uri) {
- Set<String> fingerprints = new HashSet<String>();
+ Set<String> fingerprints = new HashSet<>();
String[] projection = new String[]{KeyRings.FINGERPRINT};
Cursor cursor = mContentResolver.query(uri, projection, null, null, null);
try {