diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java index 4d5987423..340c636fe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/AutocryptPeerDataAccessObject.java @@ -119,37 +119,30 @@ public class AutocryptPeerDataAccessObject { return null; } - public void setMasterKeyIdForAutocryptPeer(String autocryptId, long masterKeyId, Date date) { - Date lastUpdated = getLastSeen(autocryptId); - if (lastUpdated != null && lastUpdated.after(date)) { - throw new IllegalArgumentException("Database entry was newer than the one to be inserted! Cannot backdate"); - } - - ContentValues cv = new ContentValues(); - cv.put(ApiAutocryptPeer.MASTER_KEY_ID, masterKeyId); - cv.put(ApiAutocryptPeer.LAST_SEEN_KEY, date.getTime()); - mQueryInterface.update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); + public void updateToResetState(String autocryptId, Date effectiveDate) { + updateAutocryptState(autocryptId, effectiveDate, null, ApiAutocryptPeer.RESET); } - public void updateToResetState(String autocryptId, Date effectiveDate) { - updateAutocryptState(autocryptId, effectiveDate, ApiAutocryptPeer.RESET); + public void updateToGossipState(String autocryptId, Date effectiveDate, long masterKeyId) { + updateAutocryptState(autocryptId, effectiveDate, masterKeyId, ApiAutocryptPeer.GOSSIP); } public void updateToMutualState(String autocryptId, Date effectiveDate, long masterKeyId) { - setMasterKeyIdForAutocryptPeer(autocryptId, masterKeyId, effectiveDate); - updateAutocryptState(autocryptId, effectiveDate, ApiAutocryptPeer.MUTUAL); + updateAutocryptState(autocryptId, effectiveDate, masterKeyId, ApiAutocryptPeer.MUTUAL); } public void updateToAvailableState(String autocryptId, Date effectiveDate, long masterKeyId) { - setMasterKeyIdForAutocryptPeer(autocryptId, masterKeyId, effectiveDate); - updateAutocryptState(autocryptId, effectiveDate, ApiAutocryptPeer.AVAILABLE); + updateAutocryptState(autocryptId, effectiveDate, masterKeyId, ApiAutocryptPeer.AVAILABLE); } - private void updateAutocryptState(String autocryptId, Date date, int status) { + private void updateAutocryptState(String autocryptId, Date date, Long masterKeyId, int status) { ContentValues cv = new ContentValues(); - cv.put(ApiAutocryptPeer.MASTER_KEY_ID, (Integer) null); + cv.put(ApiAutocryptPeer.MASTER_KEY_ID, masterKeyId); cv.put(ApiAutocryptPeer.LAST_SEEN, date.getTime()); - cv.put(ApiAutocryptPeer.STATUS, status); + if (masterKeyId != null) { + cv.put(ApiAutocryptPeer.LAST_SEEN_KEY, masterKeyId); + } + cv.put(ApiAutocryptPeer.STATE, status); mQueryInterface.update(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), cv, null, null); } } 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 384321329..24e96b71f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -100,7 +100,7 @@ public class KeychainContract { String IDENTIFIER = "identifier"; String LAST_SEEN = "last_updated"; String LAST_SEEN_KEY = "last_seen_key"; - String STATUS = "status"; + String STATE = "state"; String MASTER_KEY_ID = "master_key_id"; } 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 6466d4293..2833805cb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -164,7 +164,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + ApiAutocryptPeerColumns.IDENTIFIER + " TEXT NOT NULL, " + ApiAutocryptPeerColumns.LAST_SEEN + " INTEGER NOT NULL, " + ApiAutocryptPeerColumns.LAST_SEEN_KEY + " INTEGER NOT NULL, " - + ApiAutocryptPeerColumns.STATUS + " INTEGER NOT NULL, " + + ApiAutocryptPeerColumns.STATE + " INTEGER NOT NULL, " + ApiAutocryptPeerColumns.MASTER_KEY_ID + " INTEGER NULL, " + "PRIMARY KEY(" + ApiAutocryptPeerColumns.PACKAGE_NAME + ", " + ApiAutocryptPeerColumns.IDENTIFIER + "), " diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java index 4ef39f896..e47a51521 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java @@ -26,9 +26,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPee public class KeychainExternalContract { - public static final int KEY_STATUS_UNAVAILABLE = 0; - public static final int KEY_STATUS_UNVERIFIED = 1; - public static final int KEY_STATUS_VERIFIED = 2; // this is in KeychainExternalContract already, but we want to be double // sure this isn't mixed up with the internal one! @@ -36,15 +33,31 @@ public class KeychainExternalContract { private static final Uri BASE_CONTENT_URI_EXTERNAL = Uri .parse("content://" + CONTENT_AUTHORITY_EXTERNAL); public static final String BASE_EMAIL_STATUS = "email_status"; - public static final String BASE_AUTOCRYPT_PEER_STATUS = "autocrypt_status"; public static final String BASE_AUTOCRYPT_PEERS = "autocrypt_peers"; public static class EmailStatus implements BaseColumns { - public static final String EMAIL_ADDRESS = "email_address"; - public static final String USER_ID = "user_id"; - public static final String USER_ID_STATUS = "email_status"; - public static final String MASTER_KEY_ID = "master_key_id"; + public static final String ADDRESS = "address"; + + public static final String UID_ADDRESS = "uid_address"; + public static final String UID_KEY_STATUS = "uid_key_status"; + public static final String UID_MASTER_KEY_ID = "uid_master_key_id"; + public static final String UID_CANDIDATES = "uid_candidates"; + + public static final String AUTOCRYPT_MASTER_KEY_ID = "autocrypt_master_key_id"; + public static final String AUTOCRYPT_KEY_STATUS = "autocrypt_key_status"; + public static final String AUTOCRYPT_PEER_STATE = "autocrypt_peer_state"; + public static final String AUTOCRYPT_LAST_SEEN = "autocrypt_last_seen"; + public static final String AUTOCRYPT_LAST_SEEN_KEY = "autocrypt_last_seen_key"; + + public static final int KEY_STATUS_UNAVAILABLE = 0; + public static final int KEY_STATUS_UNVERIFIED = 1; + public static final int KEY_STATUS_VERIFIED = 2; + + public static final int AUTOCRYPT_PEER_RESET = 0; + public static final int AUTOCRYPT_PEER_GOSSIP = 1; + public static final int AUTOCRYPT_PEER_AVAILABLE = 2; + public static final int AUTOCRYPT_PEER_MUTUAL = 3; public static final Uri CONTENT_URI = BASE_CONTENT_URI_EXTERNAL.buildUpon() .appendPath(BASE_EMAIL_STATUS).build(); @@ -53,19 +66,6 @@ public class KeychainExternalContract { "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.email_status"; } - public static class AutocryptPeerStatus implements BaseColumns { - public static final String EMAIL_ADDRESS = "email_address"; - public static final String MASTER_KEY_ID = "master_key_id"; - public static final String AUTOCRYPT_PEER_LAST_SEEN = "autocrypt_peer_last_seen"; - public static final String AUTOCRYPT_PEER_STATUS = "autocrypt_peer_state"; - - public static final Uri CONTENT_URI = BASE_CONTENT_URI_EXTERNAL.buildUpon() - .appendPath(BASE_EMAIL_STATUS).build(); - - public static final String CONTENT_TYPE = - "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.autocrypt_status"; - } - public static class ApiAutocryptPeer implements ApiAutocryptPeerColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_EXTERNAL.buildUpon() .appendPath(BASE_AUTOCRYPT_PEERS).build(); 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 47d701722..7631051a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -999,7 +999,6 @@ public class KeychainProvider extends ContentProvider { } case TRUST_IDS_BY_PACKAGE_NAME_AND_TRUST_ID: { Long masterKeyId = values.getAsLong(ApiAutocryptPeer.MASTER_KEY_ID); - long updateTime = values.getAsLong(ApiAutocryptPeer.LAST_SEEN); if (masterKeyId == null) { throw new IllegalArgumentException("master_key_id must be a non-null value!"); } @@ -1009,7 +1008,20 @@ public class KeychainProvider extends ContentProvider { actualValues.put(ApiAutocryptPeer.PACKAGE_NAME, packageName); actualValues.put(ApiAutocryptPeer.IDENTIFIER, uri.getLastPathSegment()); actualValues.put(ApiAutocryptPeer.MASTER_KEY_ID, masterKeyId); - actualValues.put(ApiAutocryptPeer.LAST_SEEN, updateTime); + + Long newLastSeen = values.getAsLong(ApiAutocryptPeer.LAST_SEEN); + if (newLastSeen != null) { + actualValues.put(ApiAutocryptPeer.LAST_SEEN, newLastSeen); + } + + if (values.containsKey(ApiAutocryptPeer.LAST_SEEN_KEY)) { + actualValues.put(ApiAutocryptPeer.LAST_SEEN_KEY, + values.getAsLong(ApiAutocryptPeer.LAST_SEEN_KEY)); + } + if (values.containsKey(ApiAutocryptPeer.STATE)) { + actualValues.put(ApiAutocryptPeer.STATE, + values.getAsLong(ApiAutocryptPeer.STATE)); + } try { db.replace(Tables.API_AUTOCRYPT_PEERS, null, actualValues); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java index 46bc56bdd..bca9d1331 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java @@ -21,7 +21,6 @@ import java.security.AccessControlException; import java.util.Arrays; import java.util.Date; import java.util.HashMap; -import java.util.List; import android.content.ContentProvider; import android.content.ContentValues; @@ -48,7 +47,6 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainExternalContract; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.ApiAutocryptPeer; -import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptPeerStatus; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus; import org.sufficientlysecure.keychain.provider.SimpleContentResolverInterface; import org.sufficientlysecure.keychain.util.Log; @@ -59,8 +57,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC private static final int AUTOCRYPT_PEER = 201; private static final int API_APPS = 301; private static final int API_APPS_BY_PACKAGE_NAME = 302; - private static final int AUTOCRYPT_PEER_STATUS = 401; - private static final int AUTOCRYPT_PEER_STATUS_INTERNAL = 402; public static final String TEMP_TABLE_QUERIED_ADDRESSES = "queried_addresses"; public static final String TEMP_TABLE_COLUMN_ADDRES = "address"; @@ -91,9 +87,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_PEERS + "/*", AUTOCRYPT_PEER); - matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_PEER_STATUS, AUTOCRYPT_PEER_STATUS); - matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_PEER_STATUS + "/*", AUTOCRYPT_PEER_STATUS_INTERNAL); - // can only query status of calling app - for internal use only! matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME); @@ -120,9 +113,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC final int match = mUriMatcher.match(uri); switch (match) { case EMAIL_STATUS: - case EMAIL_STATUS_INTERNAL: - case AUTOCRYPT_PEER_STATUS: - case AUTOCRYPT_PEER_STATUS_INTERNAL: return EmailStatus.CONTENT_TYPE; case API_APPS: @@ -178,21 +168,38 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC HashMap projectionMap = new HashMap<>(); projectionMap.put(EmailStatus._ID, "email AS _id"); - projectionMap.put(EmailStatus.EMAIL_ADDRESS, // this is actually the queried address - TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES + " AS " + EmailStatus.EMAIL_ADDRESS); - projectionMap.put(EmailStatus.USER_ID, - Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + EmailStatus.USER_ID); + projectionMap.put(EmailStatus.ADDRESS, // this is actually the queried address + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES + " AS " + EmailStatus.ADDRESS); + + projectionMap.put(EmailStatus.UID_ADDRESS, + Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + EmailStatus.UID_ADDRESS); // we take the minimum (>0) here, where "1" is "verified by known secret key", "2" is "self-certified" - projectionMap.put(EmailStatus.USER_ID_STATUS, "CASE ( MIN (certs_user_id." + Certs.VERIFIED + " ) ) " + projectionMap.put(EmailStatus.UID_KEY_STATUS, "CASE ( MIN (certs_user_id." + Certs.VERIFIED + " ) ) " // remap to keep this provider contract independent from our internal representation - + " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED - + " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED + + " WHEN " + Certs.VERIFIED_SELF + " THEN " + EmailStatus.KEY_STATUS_UNVERIFIED + + " WHEN " + Certs.VERIFIED_SECRET + " THEN " + EmailStatus.KEY_STATUS_VERIFIED + " WHEN NULL THEN NULL" - + " END AS " + EmailStatus.USER_ID_STATUS); - projectionMap.put(EmailStatus.MASTER_KEY_ID, - Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + EmailStatus.MASTER_KEY_ID); - projectionMap.put(EmailStatus.USER_ID, - Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + EmailStatus.USER_ID); + + " END AS " + EmailStatus.UID_KEY_STATUS); + projectionMap.put(EmailStatus.UID_MASTER_KEY_ID, + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + EmailStatus.UID_MASTER_KEY_ID); + projectionMap.put(EmailStatus.UID_CANDIDATES, + "COUNT(DISTINCT " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + + ") AS " + EmailStatus.UID_CANDIDATES); + + projectionMap.put(EmailStatus.AUTOCRYPT_KEY_STATUS, "CASE ( MIN (certs_autocrypt_peer." + Certs.VERIFIED + " ) ) " + // remap to keep this provider contract independent from our internal representation + + " WHEN " + Certs.VERIFIED_SELF + " THEN " + EmailStatus.KEY_STATUS_UNVERIFIED + + " WHEN " + Certs.VERIFIED_SECRET + " THEN " + EmailStatus.KEY_STATUS_VERIFIED + + " WHEN NULL THEN NULL" + + " END AS " + EmailStatus.AUTOCRYPT_KEY_STATUS); + projectionMap.put(EmailStatus.AUTOCRYPT_MASTER_KEY_ID, + Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.MASTER_KEY_ID + " AS " + EmailStatus.AUTOCRYPT_MASTER_KEY_ID); + projectionMap.put(EmailStatus.AUTOCRYPT_PEER_STATE, Tables.API_AUTOCRYPT_PEERS + "." + + ApiAutocryptPeer.STATE + " AS " + EmailStatus.AUTOCRYPT_LAST_SEEN_KEY); + projectionMap.put(EmailStatus.AUTOCRYPT_LAST_SEEN, Tables.API_AUTOCRYPT_PEERS + "." + + ApiAutocryptPeer.LAST_SEEN + " AS " + EmailStatus.AUTOCRYPT_LAST_SEEN); + projectionMap.put(EmailStatus.AUTOCRYPT_LAST_SEEN_KEY, Tables.API_AUTOCRYPT_PEERS + "." + + ApiAutocryptPeer.LAST_SEEN_KEY + " AS " + EmailStatus.AUTOCRYPT_LAST_SEEN_KEY); qb.setProjectionMap(projectionMap); if (projection == null) { @@ -209,66 +216,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = certs_user_id." + Certs.MASTER_KEY_ID + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = certs_user_id." + Certs.RANK + ")" - ); - // in case there are multiple verifying certificates - groupBy = TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES; - List plist = Arrays.asList(projection); - if (plist.contains(EmailStatus.USER_ID)) { - groupBy += ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID; - } - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = EmailStatus.EMAIL_ADDRESS; - } - - // uri to watch is all /key_rings/ - uri = KeyRings.CONTENT_URI; - break; - } - - case AUTOCRYPT_PEER_STATUS_INTERNAL: - if (!BuildConfig.APPLICATION_ID.equals(callingPackageName)) { - throw new AccessControlException("This URI can only be called internally!"); - } - - // override package name to use any external - // callingPackageName = uri.getLastPathSegment(); - - case AUTOCRYPT_PEER_STATUS: { - boolean callerIsAllowed = (match == AUTOCRYPT_PEER_STATUS_INTERNAL) || mApiPermissionHelper.isAllowedIgnoreErrors(); - if (!callerIsAllowed) { - throw new AccessControlException("An application must register before use of KeychainExternalProvider!"); - } - - db.execSQL("CREATE TEMPORARY TABLE " + TEMP_TABLE_QUERIED_ADDRESSES + " (" + TEMP_TABLE_COLUMN_ADDRES + " TEXT);"); - ContentValues cv = new ContentValues(); - for (String address : selectionArgs) { - cv.put(TEMP_TABLE_COLUMN_ADDRES, address); - db.insert(TEMP_TABLE_QUERIED_ADDRESSES, null, cv); - } - - HashMap projectionMap = new HashMap<>(); - projectionMap.put(AutocryptPeerStatus._ID, "email AS _id"); - projectionMap.put(AutocryptPeerStatus.EMAIL_ADDRESS, // this is actually the queried address - TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES + " AS " + AutocryptPeerStatus.EMAIL_ADDRESS); - projectionMap.put(AutocryptPeerStatus.AUTOCRYPT_PEER_STATUS, "CASE ( MIN (certs_autocrypt_peer." + Certs.VERIFIED + " ) ) " - // remap to keep this provider contract independent from our internal representation - + " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED - + " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED - + " WHEN NULL THEN NULL" - + " END AS " + AutocryptPeerStatus.AUTOCRYPT_PEER_STATUS); - projectionMap.put(AutocryptPeerStatus.MASTER_KEY_ID, - Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + AutocryptPeerStatus.MASTER_KEY_ID); - projectionMap.put(AutocryptPeerStatus.AUTOCRYPT_PEER_LAST_SEEN, Tables.API_AUTOCRYPT_PEERS + "." + - ApiAutocryptPeer.LAST_SEEN + " AS " + AutocryptPeerStatus.AUTOCRYPT_PEER_LAST_SEEN); - qb.setProjectionMap(projectionMap); - - if (projection == null) { - throw new IllegalArgumentException("Please provide a projection!"); - } - - qb.setTables( - TEMP_TABLE_QUERIED_ADDRESSES + " LEFT JOIN " + Tables.API_AUTOCRYPT_PEERS + " ON (" + Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.IDENTIFIER + " LIKE queried_addresses.address" + " AND " + Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = \"" + callingPackageName + "\"" @@ -279,10 +226,9 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC ); // in case there are multiple verifying certificates groupBy = TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES; - List plist = Arrays.asList(projection); if (TextUtils.isEmpty(sortOrder)) { - sortOrder = TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES; + sortOrder = EmailStatus.ADDRESS; } // uri to watch is all /key_rings/ @@ -305,6 +251,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC projectionMap.put(ApiAutocryptPeer.IDENTIFIER, ApiAutocryptPeer.IDENTIFIER); projectionMap.put(ApiAutocryptPeer.MASTER_KEY_ID, ApiAutocryptPeer.MASTER_KEY_ID); projectionMap.put(ApiAutocryptPeer.LAST_SEEN, ApiAutocryptPeer.LAST_SEEN); + projectionMap.put(ApiAutocryptPeer.LAST_SEEN_KEY, ApiAutocryptPeer.LAST_SEEN_KEY); qb.setProjectionMap(projectionMap); qb.setTables(Tables.API_AUTOCRYPT_PEERS); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 5a4de0038..7eb0d1719 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -606,7 +606,7 @@ public class OpenPgpService extends Service { long masterKeyId = signatureResult.getKeyId(); if (autocryptPeerMasterKeyId == null) { - autocryptPeerentityDao.setMasterKeyIdForAutocryptPeer(autocryptPeerentity, masterKeyId, new Date()); + autocryptPeerentityDao.updateToGossipState(autocryptPeerentity, new Date(), masterKeyId); return signatureResult.withAutocryptPeerResult(AutocryptPeerResult.NEW); } else if (masterKeyId == autocryptPeerMasterKeyId) { return signatureResult.withAutocryptPeerResult(AutocryptPeerResult.OK); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java index 5f88500ab..79e4790e6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractor.java @@ -4,7 +4,6 @@ package org.sufficientlysecure.keychain.remote; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Map.Entry; import android.app.PendingIntent; import android.content.ContentResolver; @@ -17,7 +16,6 @@ import android.support.annotation.VisibleForTesting; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.provider.KeychainExternalContract; -import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptPeerStatus; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -26,22 +24,21 @@ import org.sufficientlysecure.keychain.util.Log; class OpenPgpServiceKeyIdExtractor { @VisibleForTesting static final String[] PROJECTION_MAIL_STATUS = { - EmailStatus.EMAIL_ADDRESS, - EmailStatus.MASTER_KEY_ID, - EmailStatus.USER_ID_STATUS, + EmailStatus.ADDRESS, + EmailStatus.UID_MASTER_KEY_ID, + EmailStatus.UID_KEY_STATUS, + EmailStatus.UID_CANDIDATES, + EmailStatus.AUTOCRYPT_MASTER_KEY_ID, + EmailStatus.AUTOCRYPT_KEY_STATUS, + EmailStatus.AUTOCRYPT_PEER_STATE }; private static final int INDEX_EMAIL_ADDRESS = 0; private static final int INDEX_MASTER_KEY_ID = 1; private static final int INDEX_USER_ID_STATUS = 2; - - static final String[] PROJECTION_AUTOCRYPT = { - AutocryptPeerStatus.EMAIL_ADDRESS, - AutocryptPeerStatus.MASTER_KEY_ID, - AutocryptPeerStatus.AUTOCRYPT_PEER_STATUS, - }; - private static final int INDEX_AUTOCRYPT_ADDRESS = 0; - private static final int INDEX_AUTOCRYPT_MASTER_KEY_ID = 1; - private static final int INDEX_AUTOCRYPT_STATUS = 2; + private static final int INDEX_USER_ID_CANDIDATES = 3; + private static final int INDEX_AUTOCRYPT_MASTER_KEY_ID = 4; + private static final int INDEX_AUTOCRYPT_KEY_STATUS = 5; + private static final int INDEX_AUTOCRYPT_PEER_STATE = 6; private final ApiPendingIntentFactory apiPendingIntentFactory; @@ -97,33 +94,33 @@ class OpenPgpServiceKeyIdExtractor { ArrayList duplicateEmails = new ArrayList<>(); if (hasAddresses) { - HashMap keyRows = getStatusMapForQueriedAddresses(encryptionAddresses, callingPackageName); - HashMap autocryptRows = getAutocryptRecommendationsForQueriedAddresses( + HashMap userIdEntries = getStatusMapForQueriedAddresses( encryptionAddresses, callingPackageName); boolean anyKeyNotVerified = false; - for (Entry entry : keyRows.entrySet()) { - String queriedAddress = entry.getKey(); - UserIdStatus userIdStatus = entry.getValue(); + for (String queriedAddress : encryptionAddresses) { + AddressQueryResult addressQueryResult = userIdEntries.get(queriedAddress); + if (addressQueryResult == null) { + throw new IllegalStateException("No result for address - shouldn't happen!"); + } - if (userIdStatus.masterKeyId == null) { + if (addressQueryResult.uidMasterKeyId != null) { + keyIds.add(addressQueryResult.uidMasterKeyId); + + if (addressQueryResult.uidHasMultipleCandidates) { + duplicateEmails.add(queriedAddress); + } + } else { missingEmails.add(queriedAddress); continue; } - keyIds.add(userIdStatus.masterKeyId); - - if (userIdStatus.hasDuplicate) { - duplicateEmails.add(queriedAddress); - } - - if (userIdStatus.userIdVerified != KeychainExternalContract.KEY_STATUS_VERIFIED && - userIdStatus.autocryptPeerVerified != KeychainExternalContract.KEY_STATUS_VERIFIED) { + if (addressQueryResult.uidKeyStatus != EmailStatus.KEY_STATUS_VERIFIED) { anyKeyNotVerified = true; } } - if (keyRows.size() != encryptionAddresses.length) { + if (userIdEntries.size() != encryptionAddresses.length) { Log.e(Constants.TAG, "Number of rows doesn't match number of retrieved rows! Probably a bug?"); } @@ -151,8 +148,8 @@ class OpenPgpServiceKeyIdExtractor { * verification status exist, the first one is returned and marked as having a duplicate. */ @NonNull - private HashMap getStatusMapForQueriedAddresses(String[] encryptionUserIds, String callingPackageName) { - HashMap keyRows = new HashMap<>(); + private HashMap getStatusMapForQueriedAddresses(String[] encryptionUserIds, String callingPackageName) { + HashMap keyRows = new HashMap<>(); Uri queryUri = EmailStatus.CONTENT_URI.buildUpon().appendPath(callingPackageName).build(); Cursor cursor = contentResolver.query(queryUri, PROJECTION_MAIL_STATUS, null, encryptionUserIds, null); if (cursor == null) { @@ -162,36 +159,21 @@ class OpenPgpServiceKeyIdExtractor { try { while (cursor.moveToNext()) { String queryAddress = cursor.getString(INDEX_EMAIL_ADDRESS); - Long masterKeyId = cursor.isNull(INDEX_MASTER_KEY_ID) ? null : cursor.getLong(INDEX_MASTER_KEY_ID); - int userIdStatus = cursor.getInt(INDEX_USER_ID_STATUS); + Long uidMasterKeyId = + cursor.isNull(INDEX_MASTER_KEY_ID) ? null : cursor.getLong(INDEX_MASTER_KEY_ID); + int uidKeyStatus = cursor.getInt(INDEX_USER_ID_STATUS); + boolean uidHasMultipleCandidates = cursor.getInt(INDEX_USER_ID_CANDIDATES) > 1; - AutocryptRecommendation autocryptRecommendation; - if (cursor.getInt(INDEX_AUTOCRYPT_PEER_STATUS) == KeychainExternalContract.KEY_STATUS_UNAVAILABLE) { - autocryptRecommendation = AutocryptRecommendation.UNAVAILABLE; - } else { - // TODO encourage/discourage, based on gossip/ state - autocryptRecommendation = AutocryptRecommendation.AVAILABLE; - } - UserIdStatus status = new UserIdStatus(masterKeyId, userIdStatus, autocryptRecommendation); + Long autocryptMasterKeyId = + cursor.isNull(INDEX_AUTOCRYPT_MASTER_KEY_ID) ? null : cursor.getLong(INDEX_AUTOCRYPT_MASTER_KEY_ID); + int autocryptKeyStatus = cursor.getInt(INDEX_AUTOCRYPT_KEY_STATUS); + int autocryptPeerStatus = cursor.getInt(INDEX_AUTOCRYPT_PEER_STATE); - boolean seenBefore = keyRows.containsKey(queryAddress); - if (!seenBefore) { - keyRows.put(queryAddress, status); - continue; - } + AddressQueryResult + status = new AddressQueryResult(uidMasterKeyId, uidKeyStatus, uidHasMultipleCandidates, + autocryptMasterKeyId, autocryptKeyStatus, autocryptPeerStatus); - UserIdStatus previousUserIdStatus = keyRows.get(queryAddress); - if (previousUserIdStatus.autocryptPeerVerified != KeychainExternalContract.KEY_STATUS_UNAVAILABLE) { - continue; - } - - if (previousUserIdStatus.masterKeyId == null) { - keyRows.put(queryAddress, status); - } else if (previousUserIdStatus.userIdVerified < status.userIdVerified) { - keyRows.put(queryAddress, status); - } else if (previousUserIdStatus.userIdVerified == status.userIdVerified) { - previousUserIdStatus.hasDuplicate = true; - } + keyRows.put(queryAddress, status); } } finally { cursor.close(); @@ -199,51 +181,28 @@ class OpenPgpServiceKeyIdExtractor { return keyRows; } - /** This method queries the KeychainExternalProvider for all addresses given in encryptionUserIds. - * It returns a map with one UserIdStatus per queried address. If multiple key candidates exist, - * the one with the highest verification status is selected. If two candidates with the same - * verification status exist, the first one is returned and marked as having a duplicate. - */ - @NonNull - private HashMap getAutocryptRecommendationsForQueriedAddresses( - String[] encryptionUserIds, String callingPackageName) { - Uri queryUri = AutocryptPeerStatus.CONTENT_URI.buildUpon().appendPath(callingPackageName).build(); - Cursor cursor = contentResolver.query(queryUri, PROJECTION_AUTOCRYPT, null, encryptionUserIds, null); - if (cursor == null) { - throw new IllegalStateException("Internal error, received null cursor!"); - } + private static class AddressQueryResult { + private final Long uidMasterKeyId; + private final int uidKeyStatus; + private boolean uidHasMultipleCandidates; + private final Long autocryptMasterKeyId; + private final int autocryptKeyStatus; + private final int autocryptState; - try { - HashMap keyRows = new HashMap<>(); - while (cursor.moveToNext()) { - String queryAddress = cursor.getString(INDEX_AUTOCRYPT_ADDRESS); - Long masterKeyId = cursor.isNull(INDEX_AUTOCRYPT_MASTER_KEY_ID) ? - null : cursor.getLong(INDEX_AUTOCRYPT_MASTER_KEY_ID); - int autocryptStatus = cursor.getInt(INDEX_AUTOCRYPT_STATUS); - - AutocryptRecommendation autocryptRecommendation; - if (cursor.getInt(INDEX_AUTOCRYPT_STATUS) == KeychainExternalContract.KEY_STATUS_UNAVAILABLE) { - autocryptRecommendation = AutocryptRecommendation.UNAVAILABLE; - } else { - // TODO encourage/discourage, based on gossip/ state - autocryptRecommendation = AutocryptRecommendation.AVAILABLE; - } - } - } finally { - cursor.close(); + AddressQueryResult(Long uidMasterKeyId, int uidKeyStatus, boolean uidHasMultipleCandidates, Long autocryptMasterKeyId, + int autocryptKeyStatus, + int autocryptState) { + this.uidMasterKeyId = uidMasterKeyId; + this.uidKeyStatus = uidKeyStatus; + this.uidHasMultipleCandidates = uidHasMultipleCandidates; + this.autocryptMasterKeyId = autocryptMasterKeyId; + this.autocryptKeyStatus = autocryptKeyStatus; + this.autocryptState = autocryptState; } - return keyRows; } - private static class UserIdStatus { - private final Long masterKeyId; - private final int userIdVerified; - private boolean hasDuplicate; - - UserIdStatus(Long masterKeyId, int userIdVerified) { - this.masterKeyId = masterKeyId; - this.userIdVerified = userIdVerified; - } + enum AutocryptState { + UNAVAILABLE, DISCOURAGE, AVAILABLE, MUTUAL } static class KeyIdResult { @@ -317,10 +276,6 @@ class OpenPgpServiceKeyIdExtractor { } } - enum AutocryptRecommendation { - UNAVAILABLE, DISCOURAGE, AVAILABLE, MUTUAL - } - enum KeyIdResultStatus { OK, MISSING, DUPLICATE, NO_KEYS, NO_KEYS_ERROR } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java index cfa192bb7..ababf0222 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java @@ -15,22 +15,26 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; import org.robolectric.res.builder.RobolectricPackageManager; +import org.robolectric.shadows.ShadowLog; import org.sufficientlysecure.keychain.KeychainTestRunner; import org.sufficientlysecure.keychain.operations.CertifyOperation; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; +import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; +import org.sufficientlysecure.keychain.provider.KeyRepositorySaveTest; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus; -import org.sufficientlysecure.keychain.provider.KeyRepositorySaveTest; -import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.util.ProgressScaler; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; @SuppressWarnings("WeakerAccess") @@ -58,6 +62,8 @@ public class KeychainExternalProviderTest { @Before public void setUp() throws Exception { + ShadowLog.stream = System.out; + RobolectricPackageManager rpm = (RobolectricPackageManager) RuntimeEnvironment.getPackageManager(); rpm.setPackagesForUid(0, PACKAGE_NAME); PackageInfo packageInfo = new PackageInfo(); @@ -78,7 +84,7 @@ public class KeychainExternalProviderTest { contentResolver.query( EmailStatus.CONTENT_URI, - new String[] { EmailStatus.EMAIL_ADDRESS, EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID }, + new String[] { EmailStatus.ADDRESS, EmailStatus.ADDRESS, EmailStatus.UID_ADDRESS }, null, new String [] { }, null ); } @@ -87,7 +93,7 @@ public class KeychainExternalProviderTest { public void testPermission__withExplicitPackage() throws Exception { contentResolver.query( EmailStatus.CONTENT_URI.buildUpon().appendPath("fake_pkg").build(), - new String[] { EmailStatus.EMAIL_ADDRESS, EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID }, + new String[] { EmailStatus.ADDRESS, EmailStatus.ADDRESS, EmailStatus.UID_ADDRESS }, null, new String [] { }, null ); } @@ -99,7 +105,7 @@ public class KeychainExternalProviderTest { contentResolver.query( EmailStatus.CONTENT_URI, - new String[] { EmailStatus.EMAIL_ADDRESS, EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID }, + new String[] { EmailStatus.ADDRESS, EmailStatus.ADDRESS, EmailStatus.UID_ADDRESS }, null, new String [] { }, null ); } @@ -108,14 +114,14 @@ public class KeychainExternalProviderTest { public void testQuery__withNonExistentAddress() throws Exception { Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS }, null, new String [] { MAIL_ADDRESS_1 }, null ); assertNotNull(cursor); assertTrue(cursor.moveToFirst()); assertEquals(MAIL_ADDRESS_1, cursor.getString(0)); - assertEquals(0, cursor.getInt(1)); + assertEquals(EmailStatus.KEY_STATUS_UNAVAILABLE, cursor.getInt(1)); assertTrue(cursor.isNull(2)); } @@ -125,14 +131,14 @@ public class KeychainExternalProviderTest { Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS }, null, new String [] { MAIL_ADDRESS_1 }, null ); assertNotNull(cursor); assertTrue(cursor.moveToFirst()); assertEquals(MAIL_ADDRESS_1, cursor.getString(0)); - assertEquals(1, cursor.getInt(1)); + assertEquals(EmailStatus.KEY_STATUS_UNVERIFIED, cursor.getInt(1)); assertEquals("twi ", cursor.getString(2)); assertFalse(cursor.moveToNext()); } @@ -143,18 +149,18 @@ public class KeychainExternalProviderTest { Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS }, null, new String [] { MAIL_ADDRESS_1, MAIL_ADDRESS_2 }, null ); assertNotNull(cursor); assertTrue(cursor.moveToNext()); assertEquals(MAIL_ADDRESS_2, cursor.getString(0)); - assertEquals(0, cursor.getInt(1)); + assertEquals(EmailStatus.KEY_STATUS_UNAVAILABLE, cursor.getInt(1)); assertTrue(cursor.isNull(2)); assertTrue(cursor.moveToNext()); assertEquals(MAIL_ADDRESS_1, cursor.getString(0)); - assertEquals(1, cursor.getInt(1)); + assertEquals(EmailStatus.KEY_STATUS_UNVERIFIED, cursor.getInt(1)); assertEquals("twi ", cursor.getString(2)); assertFalse(cursor.moveToNext()); } @@ -165,7 +171,7 @@ public class KeychainExternalProviderTest { Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS }, null, new String [] { MAIL_ADDRESS_SEC_1 }, null ); @@ -182,21 +188,25 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.setMasterKeyIdForAutocryptPeer("tid", KEY_ID_PUBLIC, new Date()); + autocryptPeerDao.updateToAvailableState("tid", new Date(), KEY_ID_PUBLIC); Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID, - EmailStatus.AUTOCRYPT_PEER_STATUS }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS, + EmailStatus.AUTOCRYPT_KEY_STATUS, EmailStatus.AUTOCRYPT_MASTER_KEY_ID, + EmailStatus.AUTOCRYPT_PEER_STATE + }, null, new String [] { AUTOCRYPT_PEER }, null ); assertNotNull(cursor); assertTrue(cursor.moveToFirst()); assertEquals("tid", cursor.getString(0)); + assertTrue(cursor.isNull(1)); assertEquals(null, cursor.getString(2)); - assertEquals(0, cursor.getInt(1)); - assertEquals(1, cursor.getInt(3)); + assertEquals(EmailStatus.KEY_STATUS_UNVERIFIED, cursor.getInt(3)); + assertEquals(KEY_ID_PUBLIC, cursor.getLong(4)); + assertEquals(EmailStatus.AUTOCRYPT_PEER_AVAILABLE, cursor.getInt(5)); assertFalse(cursor.moveToNext()); } @@ -205,22 +215,48 @@ public class KeychainExternalProviderTest { insertSecretKeyringFrom("/test-keys/testring.sec"); insertPublicKeyringFrom("/test-keys/testring.pub"); - autocryptPeerDao.setMasterKeyIdForAutocryptPeer("tid", KEY_ID_PUBLIC, new Date()); + autocryptPeerDao.updateToAvailableState("tid", new Date(), KEY_ID_PUBLIC); certifyKey(KEY_ID_SECRET, KEY_ID_PUBLIC, USER_ID_1); Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID, - EmailStatus.AUTOCRYPT_PEER_STATUS }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS, + EmailStatus.AUTOCRYPT_KEY_STATUS, EmailStatus.AUTOCRYPT_PEER_STATE }, null, new String [] { AUTOCRYPT_PEER }, null ); assertNotNull(cursor); assertTrue(cursor.moveToFirst()); assertEquals("tid", cursor.getString(0)); + assertEquals(EmailStatus.KEY_STATUS_UNAVAILABLE, cursor.getInt(1)); assertEquals(null, cursor.getString(2)); - assertEquals(0, cursor.getInt(1)); - assertEquals(2, cursor.getInt(3)); + assertEquals(EmailStatus.KEY_STATUS_VERIFIED, cursor.getInt(3)); + assertEquals(EmailStatus.AUTOCRYPT_PEER_AVAILABLE, cursor.getInt(4)); + assertFalse(cursor.moveToNext()); + } + + @Test + public void testQuery__withAutocryptPeer__fromGossip() throws Exception { + insertSecretKeyringFrom("/test-keys/testring.sec"); + insertPublicKeyringFrom("/test-keys/testring.pub"); + + autocryptPeerDao.updateToGossipState("tid", new Date(), KEY_ID_PUBLIC); + certifyKey(KEY_ID_SECRET, KEY_ID_PUBLIC, USER_ID_1); + + Cursor cursor = contentResolver.query( + EmailStatus.CONTENT_URI, new String[] { + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS, + EmailStatus.AUTOCRYPT_KEY_STATUS, EmailStatus.AUTOCRYPT_PEER_STATE }, + null, new String [] { AUTOCRYPT_PEER }, null + ); + + assertNotNull(cursor); + assertTrue(cursor.moveToFirst()); + assertEquals("tid", cursor.getString(0)); + assertEquals(EmailStatus.KEY_STATUS_UNAVAILABLE, cursor.getInt(1)); + assertEquals(null, cursor.getString(2)); + assertEquals(EmailStatus.KEY_STATUS_VERIFIED, cursor.getInt(3)); + assertEquals(EmailStatus.AUTOCRYPT_PEER_GOSSIP, cursor.getInt(4)); assertFalse(cursor.moveToNext()); } @@ -233,7 +269,7 @@ public class KeychainExternalProviderTest { Cursor cursor = contentResolver.query( EmailStatus.CONTENT_URI, new String[] { - EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID }, + EmailStatus.ADDRESS, EmailStatus.UID_KEY_STATUS, EmailStatus.UID_ADDRESS }, null, new String [] { MAIL_ADDRESS_1 }, null ); @@ -241,7 +277,7 @@ public class KeychainExternalProviderTest { assertTrue(cursor.moveToFirst()); assertEquals(MAIL_ADDRESS_1, cursor.getString(0)); assertEquals(USER_ID_1, cursor.getString(2)); - assertEquals(2, cursor.getInt(1)); + assertEquals(EmailStatus.KEY_STATUS_VERIFIED, cursor.getInt(1)); assertFalse(cursor.moveToNext()); } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractorTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractorTest.java index c50a04278..46b98e9fc 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractorTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/OpenPgpServiceKeyIdExtractorTest.java @@ -74,7 +74,7 @@ public class OpenPgpServiceKeyIdExtractorTest { assertArrayEqualsSorted(KEY_IDS, keyIdResult.getKeyIds()); } - @Test + @Test(expected = IllegalStateException.class) public void returnKeyIdsFromIntent__withUserIds__withEmptyQueryResult() throws Exception { Intent intent = new Intent(); intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, USER_IDS); @@ -82,15 +82,10 @@ public class OpenPgpServiceKeyIdExtractorTest { setupContentResolverResult(); PendingIntent pendingIntent = mock(PendingIntent.class); - setupPendingIntentFactoryResult(pendingIntent); + setupSelectPubkeyPendingIntentFactoryResult(pendingIntent); - KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, - BuildConfig.APPLICATION_ID); - - - assertEquals(KeyIdResultStatus.NO_KEYS, keyIdResult.getStatus()); - assertTrue(keyIdResult.hasKeySelectionPendingIntent()); + openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, BuildConfig.APPLICATION_ID); } @Test @@ -99,7 +94,7 @@ public class OpenPgpServiceKeyIdExtractorTest { intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, new String[] { }); PendingIntent pendingIntent = mock(PendingIntent.class); - setupPendingIntentFactoryResult(pendingIntent); + setupSelectPubkeyPendingIntentFactoryResult(pendingIntent); KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, @@ -117,7 +112,7 @@ public class OpenPgpServiceKeyIdExtractorTest { intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, new String[0]); PendingIntent pendingIntent = mock(PendingIntent.class); - setupPendingIntentFactoryResult(pendingIntent); + setupSelectPubkeyPendingIntentFactoryResult(pendingIntent); KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, BuildConfig.APPLICATION_ID); @@ -135,7 +130,7 @@ public class OpenPgpServiceKeyIdExtractorTest { setupContentResolverResult(); PendingIntent pendingIntent = mock(PendingIntent.class); - setupPendingIntentFactoryResult(pendingIntent); + setupSelectPubkeyPendingIntentFactoryResult(pendingIntent); KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, true, @@ -152,7 +147,7 @@ public class OpenPgpServiceKeyIdExtractorTest { Intent intent = new Intent(); intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, USER_IDS); - setupContentResolverResult(USER_IDS, new Long[] { 123L, 234L }, new int[] { 0, 0 }); + setupContentResolverResult(USER_IDS, new Long[] { 123L, 234L }, new int[] { 0, 0 }, new int[] { 1, 1, 1 }); KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, @@ -170,11 +165,11 @@ public class OpenPgpServiceKeyIdExtractorTest { intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, USER_IDS); setupContentResolverResult(new String[] { - USER_IDS[0], USER_IDS[0], USER_IDS[1] - }, new Long[] { 123L, 345L, 234L }, new int[] { 0, 0, 0 }); + USER_IDS[0], USER_IDS[1] + }, new Long[] { 123L, 234L }, new int[] { 0, 0 }, new int[] { 2, 1 }); PendingIntent pendingIntent = mock(PendingIntent.class); - setupPendingIntentFactoryResult(pendingIntent); + setupDeduplicatePendingIntentFactoryResult(pendingIntent); KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, @@ -190,10 +185,10 @@ public class OpenPgpServiceKeyIdExtractorTest { Intent intent = new Intent(); intent.putExtra(OpenPgpApi.EXTRA_USER_IDS, USER_IDS); - setupContentResolverResult(USER_IDS, new Long[] { null, 234L }, new int[] { 0, 0 }); + setupContentResolverResult(USER_IDS, new Long[] { null, 234L }, new int[] { 0, 0 }, new int[] { 0, 1 }); PendingIntent pendingIntent = mock(PendingIntent.class); - setupPendingIntentFactoryResult(pendingIntent); + setupSelectPubkeyPendingIntentFactoryResult(pendingIntent); KeyIdResult keyIdResult = openPgpServiceKeyIdExtractor.returnKeyIdsFromIntent(intent, false, @@ -211,10 +206,10 @@ public class OpenPgpServiceKeyIdExtractorTest { .thenReturn(resultCursor); } - private void setupContentResolverResult(String[] userIds, Long[] resultKeyIds, int[] verified) { + private void setupContentResolverResult(String[] userIds, Long[] resultKeyIds, int[] verified, int[] candidates) { MatrixCursor resultCursor = new MatrixCursor(OpenPgpServiceKeyIdExtractor.PROJECTION_MAIL_STATUS); for (int i = 0; i < userIds.length; i++) { - resultCursor.addRow(new Object[] { userIds[i], resultKeyIds[i], verified[i] }); + resultCursor.addRow(new Object[] { userIds[i], resultKeyIds[i], verified[i], candidates[i], null, null, null }); } when(contentResolver.query( @@ -222,12 +217,16 @@ public class OpenPgpServiceKeyIdExtractorTest { .thenReturn(resultCursor); } - private void setupPendingIntentFactoryResult(PendingIntent pendingIntent) { + private void setupSelectPubkeyPendingIntentFactoryResult(PendingIntent pendingIntent) { when(apiPendingIntentFactory.createSelectPublicKeyPendingIntent( any(Intent.class), any(long[].class), any(ArrayList.class), any(ArrayList.class), any(Boolean.class))) .thenReturn(pendingIntent); } + private void setupDeduplicatePendingIntentFactoryResult(PendingIntent pendingIntent) { + when(apiPendingIntentFactory.createDeduplicatePendingIntent( + any(String.class), any(Intent.class), any(ArrayList.class))).thenReturn(pendingIntent); + } private static void assertArrayEqualsSorted(long[] a, long[] b) { long[] tmpA = Arrays.copyOf(a, a.length);