Fix and optimize Autocrypt 1.0 logic

This commit is contained in:
Vincent Breitmoser
2018-01-15 02:55:57 +01:00
parent 7b268b11ed
commit 7c1fe18b2c
5 changed files with 207 additions and 94 deletions

View File

@@ -19,12 +19,13 @@ package org.sufficientlysecure.keychain.provider;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.text.format.DateUtils;
@@ -35,6 +36,7 @@ public class AutocryptPeerDataAccessObject {
private static final long AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS = 35 * DateUtils.DAY_IN_MILLIS;
private static final String[] PROJECTION_AUTOCRYPT_QUERY = {
ApiAutocryptPeer.IDENTIFIER,
ApiAutocryptPeer.LAST_SEEN,
ApiAutocryptPeer.MASTER_KEY_ID,
ApiAutocryptPeer.LAST_SEEN_KEY,
@@ -48,18 +50,19 @@ public class AutocryptPeerDataAccessObject {
ApiAutocryptPeer.GOSSIP_KEY_IS_EXPIRED,
ApiAutocryptPeer.GOSSIP_KEY_IS_VERIFIED,
};
private static final int INDEX_LAST_SEEN = 0;
private static final int INDEX_MASTER_KEY_ID = 1;
private static final int INDEX_LAST_SEEN_KEY = 2;
private static final int INDEX_STATE = 3;
private static final int INDEX_KEY_IS_REVOKED = 4;
private static final int INDEX_KEY_IS_EXPIRED = 5;
private static final int INDEX_KEY_IS_VERIFIED = 6;
private static final int INDEX_GOSSIP_MASTER_KEY_ID = 7;
private static final int INDEX_GOSSIP_LAST_SEEN_KEY = 8;
private static final int INDEX_GOSSIP_KEY_IS_REVOKED = 9;
private static final int INDEX_GOSSIP_KEY_IS_EXPIRED = 10;
private static final int INDEX_GOSSIP_KEY_IS_VERIFIED = 11;
private static final int INDEX_IDENTIFIER = 0;
private static final int INDEX_LAST_SEEN = 1;
private static final int INDEX_MASTER_KEY_ID = 2;
private static final int INDEX_LAST_SEEN_KEY = 3;
private static final int INDEX_STATE = 4;
private static final int INDEX_KEY_IS_REVOKED = 5;
private static final int INDEX_KEY_IS_EXPIRED = 6;
private static final int INDEX_KEY_IS_VERIFIED = 7;
private static final int INDEX_GOSSIP_MASTER_KEY_ID = 8;
private static final int INDEX_GOSSIP_LAST_SEEN_KEY = 9;
private static final int INDEX_GOSSIP_KEY_IS_REVOKED = 10;
private static final int INDEX_GOSSIP_KEY_IS_EXPIRED = 11;
private static final int INDEX_GOSSIP_KEY_IS_VERIFIED = 12;
private final SimpleContentResolverInterface queryInterface;
private final String packageName;
@@ -178,7 +181,7 @@ public class AutocryptPeerDataAccessObject {
queryInterface.delete(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), null, null);
}
public AutocryptPeerStateResult getAutocryptState(String autocryptId) {
public Map<String,AutocryptPeerStateResult> getAutocryptState(String... autocryptIds) {
/*
Determine if encryption is possible
If there is no peers[to-addr], then set ui-recommendation to disable, and terminate.
@@ -199,47 +202,70 @@ If autocrypt_timestamp is more than 35 days older than last_seen, set preliminar
Otherwise, set preliminary-recommendation to available.
*/
Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId),
PROJECTION_AUTOCRYPT_QUERY, null, null, null);
Map<String,AutocryptPeerStateResult> result = new HashMap<>();
StringBuilder selection = new StringBuilder(ApiAutocryptPeer.IDENTIFIER + " IN (?");
for (int i = 1; i < autocryptIds.length; i++) {
selection.append(",?");
}
selection.append(")");
Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageName(packageName),
PROJECTION_AUTOCRYPT_QUERY, selection.toString(), autocryptIds, null);
try {
if (!cursor.moveToFirst()) {
return new AutocryptPeerStateResult(AutocryptState.DISABLE, null, false);
}
while (cursor.moveToNext()) {
String autocryptId = cursor.getString(INDEX_IDENTIFIER);
boolean hasKey = !cursor.isNull(INDEX_MASTER_KEY_ID);
boolean isRevoked = cursor.getInt(INDEX_KEY_IS_REVOKED) != 0;
boolean isExpired = cursor.getInt(INDEX_KEY_IS_EXPIRED) != 0;
if (hasKey && !isRevoked && !isExpired) {
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
long lastSeen = cursor.getLong(INDEX_LAST_SEEN);
long lastSeenKey = cursor.getLong(INDEX_LAST_SEEN_KEY);
boolean isVerified = cursor.getInt(INDEX_KEY_IS_VERIFIED) != 0;
if (lastSeenKey < (lastSeen - AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS)) {
return new AutocryptPeerStateResult(AutocryptState.DISCOURAGED_OLD, masterKeyId, isVerified);
boolean hasKey = !cursor.isNull(INDEX_MASTER_KEY_ID);
boolean isRevoked = cursor.getInt(INDEX_KEY_IS_REVOKED) != 0;
boolean isExpired = cursor.getInt(INDEX_KEY_IS_EXPIRED) != 0;
if (hasKey && !isRevoked && !isExpired) {
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
long lastSeen = cursor.getLong(INDEX_LAST_SEEN);
long lastSeenKey = cursor.getLong(INDEX_LAST_SEEN_KEY);
boolean isVerified = cursor.getInt(INDEX_KEY_IS_VERIFIED) != 0;
if (lastSeenKey < (lastSeen - AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS)) {
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
AutocryptState.DISCOURAGED_OLD, masterKeyId, isVerified);
result.put(autocryptId, peerResult);
continue;
}
boolean isMutual = cursor.getInt(INDEX_STATE) != 0;
if (isMutual) {
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
AutocryptState.MUTUAL, masterKeyId, isVerified);
result.put(autocryptId, peerResult);
continue;
} else {
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
AutocryptState.AVAILABLE, masterKeyId, isVerified);
result.put(autocryptId, peerResult);
continue;
}
}
boolean isMutual = cursor.getInt(INDEX_STATE) != 0;
if (isMutual) {
return new AutocryptPeerStateResult(AutocryptState.MUTUAL, masterKeyId, isVerified);
} else {
return new AutocryptPeerStateResult(AutocryptState.AVAILABLE, masterKeyId, isVerified);
boolean gossipHasKey = !cursor.isNull(INDEX_GOSSIP_MASTER_KEY_ID);
boolean gossipIsRevoked = cursor.getInt(INDEX_GOSSIP_KEY_IS_REVOKED) != 0;
boolean gossipIsExpired = cursor.getInt(INDEX_GOSSIP_KEY_IS_EXPIRED) != 0;
boolean isVerified = cursor.getInt(INDEX_GOSSIP_KEY_IS_VERIFIED) != 0;
if (gossipHasKey && !gossipIsRevoked && !gossipIsExpired) {
long masterKeyId = cursor.getLong(INDEX_GOSSIP_MASTER_KEY_ID);
AutocryptPeerStateResult peerResult =
new AutocryptPeerStateResult(
AutocryptState.DISCOURAGED_GOSSIP, masterKeyId, isVerified);
result.put(autocryptId, peerResult);
continue;
}
}
boolean gossipHasKey = !cursor.isNull(INDEX_GOSSIP_MASTER_KEY_ID);
boolean gossipIsRevoked = cursor.getInt(INDEX_GOSSIP_KEY_IS_REVOKED) != 0;
boolean gossipIsExpired = cursor.getInt(INDEX_GOSSIP_KEY_IS_EXPIRED) != 0;
boolean isVerified = cursor.getInt(INDEX_GOSSIP_KEY_IS_VERIFIED) != 0;
if (gossipHasKey && !gossipIsRevoked && !gossipIsExpired) {
System.err.println("xx");
long masterKeyId = cursor.getLong(INDEX_GOSSIP_MASTER_KEY_ID);
return new AutocryptPeerStateResult(AutocryptState.DISCOURAGED_GOSSIP, masterKeyId, isVerified);
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
AutocryptState.DISABLE, null, false);
result.put(autocryptId, peerResult);
}
return new AutocryptPeerStateResult(AutocryptState.DISABLE, null, false);
} finally {
cursor.close();
}
return result;
}
public static class AutocryptPeerStateResult {

View File

@@ -386,6 +386,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(uri.getPathSegments().get(1)).build();
}
public static Uri buildByPackageName(String packageName) {
return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName).build();
}
public static Uri buildByPackageNameAndAutocryptId(String packageName, String autocryptPeer) {
return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName).appendPath(autocryptPeer).build();
}

View File

@@ -236,6 +236,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ UserPacketsColumns.USER_ID + ", " + UserPacketsColumns.MASTER_KEY_ID + ");");
db.execSQL("CREATE INDEX verified_certs ON certs ("
+ CertsColumns.VERIFIED + ", " + CertsColumns.MASTER_KEY_ID + ");");
db.execSQL("CREATE INDEX uids_by_email ON user_packets ("
+ UserPacketsColumns.EMAIL + ");");
Preferences.getPreferences(mContext).setKeySignaturesTableInitialized();
}
@@ -412,6 +414,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
case 24:
db.execSQL("DROP TABLE api_autocrypt_peers");
db.execSQL(CREATE_API_AUTOCRYPT_PEERS);
db.execSQL("CREATE INDEX uids_by_email ON user_packets (email);");
}
}

View File

@@ -717,14 +717,10 @@ public class KeychainProvider extends ContentProvider {
case AUTOCRYPT_PEERS_BY_MASTER_KEY_ID:
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME:
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: {
if (selection != null || selectionArgs != null) {
throw new UnsupportedOperationException();
}
long unixSeconds = new Date().getTime() / 1000;
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(ApiAutocryptPeer._ID, "oid AS " + ApiAutocryptPeer._ID);
projectionMap.put(ApiAutocryptPeer._ID, Tables.API_AUTOCRYPT_PEERS + ".oid AS " + ApiAutocryptPeer._ID);
projectionMap.put(ApiAutocryptPeer.IDENTIFIER, ApiAutocryptPeer.IDENTIFIER);
projectionMap.put(ApiAutocryptPeer.PACKAGE_NAME, ApiAutocryptPeer.PACKAGE_NAME);
projectionMap.put(ApiAutocryptPeer.LAST_SEEN, ApiAutocryptPeer.LAST_SEEN);
@@ -771,8 +767,8 @@ public class KeychainProvider extends ContentProvider {
} else if (match == AUTOCRYPT_PEERS_BY_PACKAGE_NAME) {
String packageName = uri.getPathSegments().get(2);
selection = Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = ?";
selectionArgs = new String[] { packageName };
qb.appendWhere(Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(packageName);
} else { // AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID
String packageName = uri.getPathSegments().get(2);
String autocryptPeer = uri.getPathSegments().get(3);