extract subkey loading from KeychainProvider
This commit is contained in:
@@ -9,8 +9,8 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.squareup.sqldelight.SqlDelightQuery;
|
||||
import org.sufficientlysecure.keychain.model.Key;
|
||||
import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ public class KeyRingDao {
|
||||
}
|
||||
|
||||
public List<UnifiedKeyInfo> getUnifiedKeyInfo() {
|
||||
SqlDelightQuery query = Key.FACTORY.selectAllUnifiedKeyInfo();
|
||||
SqlDelightQuery query = SubKey.FACTORY.selectAllUnifiedKeyInfo();
|
||||
List<UnifiedKeyInfo> result = new ArrayList<>();
|
||||
try (Cursor cursor = db.query(query)) {
|
||||
while (cursor.moveToNext()) {
|
||||
UnifiedKeyInfo unifiedKeyInfo = Key.UNIFIED_KEY_INFO_MAPPER.map(cursor);
|
||||
UnifiedKeyInfo unifiedKeyInfo = SubKey.UNIFIED_KEY_INFO_MAPPER.map(cursor);
|
||||
result.add(unifiedKeyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import com.squareup.sqldelight.ColumnAdapter;
|
||||
import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
|
||||
|
||||
|
||||
@@ -50,4 +51,17 @@ public final class CustomColumnAdapters {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static final ColumnAdapter<SecretKeyType,Long> SECRET_KEY_TYPE_ADAPTER = new ColumnAdapter<SecretKeyType, Long>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public SecretKeyType decode(Long databaseValue) {
|
||||
return databaseValue == null ? SecretKeyType.UNAVAILABLE : SecretKeyType.fromNum(databaseValue.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long encode(@NonNull SecretKeyType value) {
|
||||
return (long) value.getNum();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,14 +6,23 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.squareup.sqldelight.RowMapper;
|
||||
import org.sufficientlysecure.keychain.KeysModel;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
|
||||
|
||||
@AutoValue
|
||||
public abstract class Key implements KeysModel {
|
||||
public static final Factory<Key> FACTORY = new Factory<>(AutoValue_Key::new);
|
||||
public abstract class SubKey implements KeysModel {
|
||||
public static final Factory<SubKey> FACTORY =
|
||||
new Factory<>(AutoValue_SubKey::new, CustomColumnAdapters.SECRET_KEY_TYPE_ADAPTER);
|
||||
public static final SelectAllUnifiedKeyInfoMapper<UnifiedKeyInfo> UNIFIED_KEY_INFO_MAPPER =
|
||||
FACTORY.selectAllUnifiedKeyInfoMapper(AutoValue_Key_UnifiedKeyInfo::new);
|
||||
FACTORY.selectAllUnifiedKeyInfoMapper(AutoValue_SubKey_UnifiedKeyInfo::new);
|
||||
public static Mapper<SubKey> SUBKEY_MAPPER = new Mapper<>(FACTORY);
|
||||
public static RowMapper<SecretKeyType> SKT_MAPPER = FACTORY.selectSecretKeyTypeMapper();
|
||||
|
||||
public boolean expires() {
|
||||
return expiry() != null;
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public static abstract class UnifiedKeyInfo implements SelectAllUnifiedKeyInfoModel {
|
||||
@@ -45,6 +54,5 @@ public abstract class Key implements KeysModel {
|
||||
}
|
||||
return autocryptPackageNames;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult.SenderStatusResult;
|
||||
@@ -27,7 +28,6 @@ import org.openintents.openpgp.util.OpenPgpUtils;
|
||||
import org.openintents.openpgp.util.OpenPgpUtils.UserId;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ public class OpenPgpSignatureResultBuilder {
|
||||
|
||||
// OpenPgpSignatureResult
|
||||
private String mPrimaryUserId;
|
||||
private ArrayList<String> mUserIds = new ArrayList<>();
|
||||
private ArrayList<String> mConfirmedUserIds;
|
||||
private List<String> mUserIds = new ArrayList<>();
|
||||
private List<String> mConfirmedUserIds;
|
||||
private long mKeyId;
|
||||
private SenderStatusResult mSenderStatusResult;
|
||||
|
||||
@@ -101,7 +101,7 @@ public class OpenPgpSignatureResultBuilder {
|
||||
this.mIsKeyExpired = keyExpired;
|
||||
}
|
||||
|
||||
public void setUserIds(ArrayList<String> userIds, ArrayList<String> confirmedUserIds) {
|
||||
public void setUserIds(List<String> userIds, List<String> confirmedUserIds) {
|
||||
this.mUserIds = userIds;
|
||||
this.mConfirmedUserIds = confirmedUserIds;
|
||||
}
|
||||
@@ -125,9 +125,8 @@ public class OpenPgpSignatureResultBuilder {
|
||||
}
|
||||
setSignatureKeyCertified(signingRing.getVerified() > 0);
|
||||
|
||||
ArrayList<String> allUserIds = signingRing.getUnorderedUserIds();
|
||||
ArrayList<String> confirmedUserIds;
|
||||
confirmedUserIds = mKeyRepository.getConfirmedUserIds(signingRing.getMasterKeyId());
|
||||
List<String> allUserIds = signingRing.getUnorderedUserIds();
|
||||
List<String> confirmedUserIds = mKeyRepository.getConfirmedUserIds(signingRing.getMasterKeyId());
|
||||
setUserIds(allUserIds, confirmedUserIds);
|
||||
|
||||
mSenderStatusResult = processSenderStatusResult(allUserIds, confirmedUserIds);
|
||||
@@ -138,7 +137,7 @@ public class OpenPgpSignatureResultBuilder {
|
||||
}
|
||||
|
||||
private SenderStatusResult processSenderStatusResult(
|
||||
ArrayList<String> allUserIds, ArrayList<String> confirmedUserIds) {
|
||||
List<String> allUserIds, List<String> confirmedUserIds) {
|
||||
if (mSenderAddress == null) {
|
||||
return SenderStatusResult.UNKNOWN;
|
||||
}
|
||||
@@ -152,7 +151,7 @@ public class OpenPgpSignatureResultBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean userIdListContainsAddress(String senderAddress, ArrayList<String> confirmedUserIds) {
|
||||
private static boolean userIdListContainsAddress(String senderAddress, List<String> confirmedUserIds) {
|
||||
for (String rawUserId : confirmedUserIds) {
|
||||
UserId userId = OpenPgpUtils.splitUserId(rawUserId);
|
||||
if (senderAddress.equalsIgnoreCase(userId.email)) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
@@ -227,10 +226,6 @@ public class CachedPublicKeyRing extends KeyRing {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSecretAuthentication() throws PgpKeyNotFoundException {
|
||||
return getSecretAuthenticationId() != 0;
|
||||
}
|
||||
|
||||
public long getAuthenticationId() throws PgpKeyNotFoundException {
|
||||
try {
|
||||
Object data = mKeyRepository.getGenericData(mUri,
|
||||
@@ -242,10 +237,6 @@ public class CachedPublicKeyRing extends KeyRing {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasAuthentication() throws PgpKeyNotFoundException {
|
||||
return getAuthenticationId() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVerified() throws PgpKeyNotFoundException {
|
||||
try {
|
||||
@@ -270,11 +261,11 @@ public class CachedPublicKeyRing extends KeyRing {
|
||||
}
|
||||
|
||||
public SecretKeyType getSecretKeyType(long keyId) throws NotFoundException {
|
||||
Object data = mKeyRepository.getGenericData(Keys.buildKeysUri(mUri),
|
||||
KeyRings.HAS_SECRET,
|
||||
KeyRepository.FIELD_TYPE_INTEGER,
|
||||
KeyRings.KEY_ID + " = " + Long.toString(keyId));
|
||||
return SecretKeyType.fromNum(((Long) data).intValue());
|
||||
SecretKeyType secretKeyType = mKeyRepository.getSecretKeyType(keyId);
|
||||
if (secretKeyType == null) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return secretKeyType;
|
||||
}
|
||||
|
||||
public byte[] getEncoded() throws PgpKeyNotFoundException {
|
||||
|
||||
@@ -32,11 +32,13 @@ import android.net.Uri;
|
||||
import com.squareup.sqldelight.SqlDelightQuery;
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||
import org.sufficientlysecure.keychain.model.KeyRingPublic;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.model.UserPacket;
|
||||
import org.sufficientlysecure.keychain.model.UserPacket.UserId;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
@@ -119,11 +121,6 @@ public class KeyRepository extends AbstractDao {
|
||||
return result;
|
||||
}
|
||||
|
||||
Object getGenericData(Uri uri, String column, int type, String selection)
|
||||
throws NotFoundException {
|
||||
return getGenericData(uri, new String[]{column}, new int[]{type}, selection).get(column);
|
||||
}
|
||||
|
||||
private HashMap<String, Object> getGenericData(Uri uri, String[] proj, int[] types)
|
||||
throws NotFoundException {
|
||||
return getGenericData(uri, proj, types, null);
|
||||
@@ -246,7 +243,7 @@ public class KeyRepository extends AbstractDao {
|
||||
return mapAllRows(query, UserPacket.USER_ID_MAPPER::map);
|
||||
}
|
||||
|
||||
public ArrayList<String> getConfirmedUserIds(long masterKeyId) {
|
||||
public List<String> getConfirmedUserIds(long masterKeyId) {
|
||||
ArrayList<String> userIds = new ArrayList<>();
|
||||
SqlDelightQuery query =
|
||||
UserPacket.FACTORY.selectUserIdsByMasterKeyIdAndVerification(masterKeyId, Certs.VERIFIED_SECRET);
|
||||
@@ -256,6 +253,21 @@ public class KeyRepository extends AbstractDao {
|
||||
return userIds;
|
||||
}
|
||||
|
||||
public List<SubKey> getSubKeysByMasterKeyId(long masterKeyId) {
|
||||
SqlDelightQuery query = SubKey.FACTORY.selectSubkeysByMasterKeyId(masterKeyId);
|
||||
return mapAllRows(query, SubKey.SUBKEY_MAPPER::map);
|
||||
}
|
||||
|
||||
public SecretKeyType getSecretKeyType(long keyId) {
|
||||
SqlDelightQuery query = SubKey.FACTORY.selectSecretKeyType(keyId);
|
||||
try (Cursor cursor = getReadableDb().query(query)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return SubKey.SKT_MAPPER.map(cursor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getKeyRingAsArmoredData(byte[] data) throws IOException, PgpGeneralException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
|
||||
|
||||
@@ -130,11 +130,6 @@ public class KeychainContract {
|
||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||
.appendPath(BASE_KEY_RINGS).build();
|
||||
|
||||
public static final String CONTENT_TYPE
|
||||
= "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.key_rings";
|
||||
public static final String CONTENT_ITEM_TYPE
|
||||
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.key_rings";
|
||||
|
||||
public static Uri buildUnifiedKeyRingsUri() {
|
||||
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
|
||||
}
|
||||
@@ -194,34 +189,15 @@ public class KeychainContract {
|
||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||
.appendPath(BASE_KEY_RINGS).build();
|
||||
|
||||
/**
|
||||
* Use if multiple items get returned
|
||||
*/
|
||||
public static final String CONTENT_TYPE
|
||||
= "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.keychain.keys";
|
||||
|
||||
/**
|
||||
* Use if a single item is returned
|
||||
*/
|
||||
public static final String CONTENT_ITEM_TYPE
|
||||
= "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.keychain.keys";
|
||||
|
||||
public static Uri buildKeysUri(long masterKeyId) {
|
||||
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_KEYS).build();
|
||||
}
|
||||
|
||||
public static Uri buildKeysUri(Uri uri) {
|
||||
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_KEYS).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static 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 {
|
||||
@@ -235,11 +211,6 @@ public class KeychainContract {
|
||||
}
|
||||
|
||||
public static class Certs implements CertsColumns, BaseColumns {
|
||||
public static final String USER_ID = UserPacketsColumns.USER_ID;
|
||||
public static final String NAME = UserPacketsColumns.NAME;
|
||||
public static final String EMAIL = UserPacketsColumns.EMAIL;
|
||||
public static final String COMMENT = UserPacketsColumns.COMMENT;
|
||||
|
||||
public static final int UNVERIFIED = 0;
|
||||
public static final int VERIFIED_SECRET = 1;
|
||||
public static final int VERIFIED_SELF = 2;
|
||||
|
||||
@@ -168,17 +168,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
*/
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
final int match = mUriMatcher.match(uri);
|
||||
switch (match) {
|
||||
case KEY_RING_KEYS:
|
||||
return Keys.CONTENT_TYPE;
|
||||
|
||||
case KEY_SIGNATURES:
|
||||
return KeySignatures.CONTENT_TYPE;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +184,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
int match = mUriMatcher.match(uri);
|
||||
|
||||
// all query() parameters, for good measure
|
||||
String groupBy = null, having = null;
|
||||
String groupBy;
|
||||
|
||||
switch (match) {
|
||||
case KEY_RING_UNIFIED:
|
||||
@@ -431,34 +421,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_RING_KEYS: {
|
||||
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);
|
||||
projectionMap.put(Keys.KEY_ID, Keys.KEY_ID);
|
||||
projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
|
||||
projectionMap.put(Keys.KEY_CURVE_OID, Keys.KEY_CURVE_OID);
|
||||
projectionMap.put(Keys.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
|
||||
projectionMap.put(Keys.IS_SECURE, Tables.KEYS + "." + Keys.IS_SECURE);
|
||||
projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY);
|
||||
projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
|
||||
projectionMap.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
|
||||
projectionMap.put(Keys.CAN_AUTHENTICATE, Keys.CAN_AUTHENTICATE);
|
||||
projectionMap.put(Keys.HAS_SECRET, Keys.HAS_SECRET);
|
||||
projectionMap.put(Keys.CREATION, Keys.CREATION);
|
||||
projectionMap.put(Keys.EXPIRY, Keys.EXPIRY);
|
||||
projectionMap.put(Keys.ALGORITHM, Keys.ALGORITHM);
|
||||
projectionMap.put(Keys.FINGERPRINT, Keys.FINGERPRINT);
|
||||
qb.setProjectionMap(projectionMap);
|
||||
|
||||
qb.setTables(Tables.KEYS);
|
||||
qb.appendWhere(Keys.MASTER_KEY_ID + " = ");
|
||||
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
|
||||
}
|
||||
@@ -489,7 +451,7 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe
|
||||
}
|
||||
|
||||
if (Constants.DEBUG && Constants.DEBUG_EXPLAIN_QUERIES) {
|
||||
String rawQuery = qb.buildQuery(projection, selection, groupBy, having, orderBy, null);
|
||||
String rawQuery = qb.buildQuery(projection, selection, groupBy, null, orderBy, null);
|
||||
DatabaseUtil.explainQuery(db, rawQuery);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
@@ -36,10 +37,10 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
@@ -138,32 +139,19 @@ public class BackupRestoreFragment extends Fragment {
|
||||
}
|
||||
|
||||
private Long getFirstSubKeyWithPassphrase(long masterKeyId, ContentResolver resolver) {
|
||||
Cursor cursor = resolver.query(
|
||||
KeychainContract.Keys.buildKeysUri(masterKeyId), new String[]{
|
||||
Keys.KEY_ID,
|
||||
Keys.HAS_SECRET,
|
||||
}, Keys.HAS_SECRET + " != 0", null, null);
|
||||
try {
|
||||
if (cursor != null) {
|
||||
while(cursor.moveToNext()) {
|
||||
SecretKeyType secretKeyType = SecretKeyType.fromNum(cursor.getInt(1));
|
||||
switch (secretKeyType) {
|
||||
case PASSPHRASE_EMPTY:
|
||||
case DIVERT_TO_CARD:
|
||||
case UNAVAILABLE:
|
||||
return null;
|
||||
case GNU_DUMMY:
|
||||
continue;
|
||||
default: {
|
||||
return cursor.getLong(0);
|
||||
}
|
||||
}
|
||||
KeyRepository keyRepository = KeyRepository.create(requireContext());
|
||||
for (SubKey subKey : keyRepository.getSubKeysByMasterKeyId(masterKeyId)) {
|
||||
switch (subKey.has_secret()) {
|
||||
case PASSPHRASE_EMPTY:
|
||||
case DIVERT_TO_CARD:
|
||||
case UNAVAILABLE:
|
||||
return null;
|
||||
case GNU_DUMMY:
|
||||
continue;
|
||||
default: {
|
||||
return subKey.key_id();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
@@ -43,6 +43,7 @@ import org.openintents.openpgp.util.OpenPgpUtils;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.UploadResult;
|
||||
@@ -50,7 +51,6 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.service.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
|
||||
@@ -410,31 +410,20 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
|
||||
private void moveToCard(final EditKeyResult saveKeyResult) {
|
||||
CreateKeyActivity activity = (CreateKeyActivity) getActivity();
|
||||
KeyRepository keyRepository = KeyRepository.create(getContext());
|
||||
|
||||
SaveKeyringParcel.Builder builder;
|
||||
CachedPublicKeyRing key = (KeyRepository.create(getContext()))
|
||||
.getCachedPublicKeyRing(saveKeyResult.mMasterKeyId);
|
||||
CachedPublicKeyRing key = keyRepository.getCachedPublicKeyRing(saveKeyResult.mMasterKeyId);
|
||||
try {
|
||||
builder = SaveKeyringParcel.buildChangeKeyringParcel(key.getMasterKeyId(), key.getFingerprint());
|
||||
builder = SaveKeyringParcel.buildChangeKeyringParcel(saveKeyResult.mMasterKeyId, key.getFingerprint());
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
Timber.e("Key that should be moved to Security Token not found in database!");
|
||||
return;
|
||||
}
|
||||
|
||||
// define subkeys that should be moved to the card
|
||||
Cursor cursor = activity.getContentResolver().query(
|
||||
KeychainContract.Keys.buildKeysUri(builder.getMasterKeyId()),
|
||||
new String[]{KeychainContract.Keys.KEY_ID,}, null, null, null
|
||||
);
|
||||
try {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
long subkeyId = cursor.getLong(0);
|
||||
builder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(subkeyId));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
List<SubKey> subKeys = keyRepository.getSubKeysByMasterKeyId(saveKeyResult.mMasterKeyId);
|
||||
for (SubKey subKey : subKeys) {
|
||||
builder.addOrReplaceSubkeyChange(SubkeyChange.createMoveToSecurityTokenChange(subKey.key_id()));
|
||||
}
|
||||
|
||||
// define new PIN and Admin PIN for the card
|
||||
|
||||
@@ -59,7 +59,7 @@ import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.keysync.KeyserverSyncManager;
|
||||
import org.sufficientlysecure.keychain.livedata.KeyRingDao;
|
||||
import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.operations.results.BenchmarkResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@@ -41,8 +44,11 @@ import android.widget.ViewAnimator;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||
import org.sufficientlysecure.keychain.livedata.GenericLiveData;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
|
||||
@@ -62,7 +68,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
public static final String ARG_DATA_URI = "data_uri";
|
||||
|
||||
private static final int LOADER_ID_UNIFIED = 0;
|
||||
private static final int LOADER_ID_SUBKEYS = 1;
|
||||
|
||||
private ListView mSubkeysList;
|
||||
private ListView mSubkeysAddedList;
|
||||
@@ -78,7 +83,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
|
||||
private long mMasterKeyId;
|
||||
private byte[] mFingerprint;
|
||||
private boolean mHasSecret;
|
||||
private SaveKeyringParcel.Builder mEditModeSkpBuilder;
|
||||
|
||||
@Override
|
||||
@@ -148,13 +152,14 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
mDataUri = dataUri;
|
||||
|
||||
// Create an empty adapter we will use to display the loaded data.
|
||||
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
|
||||
mSubkeysAdapter = new SubkeysAdapter(requireContext());
|
||||
mSubkeysList.setAdapter(mSubkeysAdapter);
|
||||
|
||||
// Prepare the loaders. Either re-connect with an existing ones,
|
||||
// or start new ones.
|
||||
getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
|
||||
getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, this);
|
||||
|
||||
setContentShown(false);
|
||||
}
|
||||
|
||||
// These are the rows that we will retrieve.
|
||||
@@ -178,14 +183,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
PROJECTION, null, null, null);
|
||||
}
|
||||
|
||||
case LOADER_ID_SUBKEYS: {
|
||||
setContentShown(false);
|
||||
|
||||
Uri subkeysUri = KeychainContract.Keys.buildKeysUri(mDataUri);
|
||||
return new CursorLoader(getActivity(), subkeysUri,
|
||||
SubkeysAdapter.SUBKEYS_PROJECTION, null, null, null);
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -202,29 +199,29 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
data.moveToFirst();
|
||||
|
||||
mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
|
||||
mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0;
|
||||
mFingerprint = data.getBlob(INDEX_FINGERPRINT);
|
||||
break;
|
||||
}
|
||||
case LOADER_ID_SUBKEYS: {
|
||||
// Swap the new cursor in. (The framework will take care of closing the
|
||||
// old cursor once we return.)
|
||||
mSubkeysAdapter.swapCursor(data);
|
||||
|
||||
// TODO: maybe show not before both are loaded!
|
||||
setContentShown(true);
|
||||
KeyRepository keyRepository = KeyRepository.create(requireContext());
|
||||
LiveData<List<SubKey>> subKeyLiveData = new GenericLiveData<>(requireContext(), null,
|
||||
() -> keyRepository.getSubKeysByMasterKeyId(mMasterKeyId));
|
||||
subKeyLiveData.observe(this, this::onLoadSubKeys);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onLoadSubKeys(List<SubKey> subKeys) {
|
||||
mSubkeysAdapter.setData(subKeys);
|
||||
setContentShown(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
|
||||
* We need to make sure we are no longer using it.
|
||||
*/
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
mSubkeysAdapter.swapCursor(null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -256,7 +253,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
mSubkeyAddFabLayout.setDisplayedChild(1);
|
||||
|
||||
mSubkeysAdapter.setEditMode(mEditModeSkpBuilder);
|
||||
getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this);
|
||||
|
||||
mode.setTitle(R.string.title_edit_subkeys);
|
||||
mode.getMenuInflater().inflate(R.menu.action_edit_uids, menu);
|
||||
@@ -281,7 +277,6 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
mSubkeysAdapter.setEditMode(null);
|
||||
mSubkeysAddedLayout.setVisibility(View.GONE);
|
||||
mSubkeyAddFabLayout.setDisplayedChild(0);
|
||||
getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -309,7 +304,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
}
|
||||
|
||||
private void editSubkey(final int position) {
|
||||
final long keyId = mSubkeysAdapter.getKeyId(position);
|
||||
final SubKey subKey = mSubkeysAdapter.getItem(position);
|
||||
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
@@ -320,29 +315,28 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
break;
|
||||
case EditSubkeyDialogFragment.MESSAGE_REVOKE:
|
||||
// toggle
|
||||
if (mEditModeSkpBuilder.getMutableRevokeSubKeys().contains(keyId)) {
|
||||
mEditModeSkpBuilder.removeRevokeSubkey(keyId);
|
||||
if (mEditModeSkpBuilder.getMutableRevokeSubKeys().contains(subKey.key_id())) {
|
||||
mEditModeSkpBuilder.removeRevokeSubkey(subKey.key_id());
|
||||
} else {
|
||||
mEditModeSkpBuilder.addRevokeSubkey(keyId);
|
||||
mEditModeSkpBuilder.addRevokeSubkey(subKey.key_id());
|
||||
}
|
||||
break;
|
||||
case EditSubkeyDialogFragment.MESSAGE_STRIP: {
|
||||
SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
|
||||
if (secretKeyType == SecretKeyType.GNU_DUMMY) {
|
||||
if (subKey.has_secret() == SecretKeyType.GNU_DUMMY) {
|
||||
// Key is already stripped; this is a no-op.
|
||||
break;
|
||||
}
|
||||
|
||||
SubkeyChange change = mEditModeSkpBuilder.getSubkeyChange(keyId);
|
||||
SubkeyChange change = mEditModeSkpBuilder.getSubkeyChange(subKey.key_id());
|
||||
if (change == null || !change.getDummyStrip()) {
|
||||
mEditModeSkpBuilder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(keyId));
|
||||
mEditModeSkpBuilder.addOrReplaceSubkeyChange(SubkeyChange.createStripChange(subKey.key_id()));
|
||||
} else {
|
||||
mEditModeSkpBuilder.removeSubkeyChange(change);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
|
||||
mSubkeysAdapter.notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -360,9 +354,10 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
}
|
||||
|
||||
private void editSubkeyExpiry(final int position) {
|
||||
final long keyId = mSubkeysAdapter.getKeyId(position);
|
||||
final Long creationDate = mSubkeysAdapter.getCreationDate(position);
|
||||
final Long expiryDate = mSubkeysAdapter.getExpiryDate(position);
|
||||
SubKey subKey = mSubkeysAdapter.getItem(position);
|
||||
final long keyId = subKey.key_id();
|
||||
final Long creationDate = subKey.creation();
|
||||
final Long expiryDate = subKey.expiry();
|
||||
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
@@ -375,7 +370,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
|
||||
SubkeyChange.createFlagsOrExpiryChange(keyId, null, expiry));
|
||||
break;
|
||||
}
|
||||
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
|
||||
mSubkeysAdapter.notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import eu.davidea.flexibleadapter.items.IFilterable;
|
||||
import eu.davidea.flexibleadapter.items.IFlexible;
|
||||
import eu.davidea.viewholders.FlexibleViewHolder;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDetailsItem.FlexibleKeyItemViewHolder;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyItem.FlexibleSectionableKeyItem;
|
||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||
|
||||
@@ -10,7 +10,7 @@ import android.content.res.Resources;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.model.Key.UnifiedKeyInfo;
|
||||
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
|
||||
|
||||
|
||||
public class FlexibleKeyItemFactory {
|
||||
|
||||
@@ -20,15 +20,14 @@ package org.sufficientlysecure.keychain.ui.adapter;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
@@ -37,121 +36,64 @@ import android.text.style.StyleSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
||||
public class SubkeysAdapter extends CursorAdapter {
|
||||
private LayoutInflater mInflater;
|
||||
public class SubkeysAdapter extends BaseAdapter {
|
||||
private final Context context;
|
||||
private final LayoutInflater layoutInflater;
|
||||
|
||||
private List<SubKey> data;
|
||||
private SaveKeyringParcel.Builder mSkpBuilder;
|
||||
|
||||
private boolean mHasAnySecret;
|
||||
private ColorStateList mDefaultTextColor;
|
||||
|
||||
public static final String[] SUBKEYS_PROJECTION = new String[]{
|
||||
Keys._ID,
|
||||
Keys.KEY_ID,
|
||||
Keys.RANK,
|
||||
Keys.ALGORITHM,
|
||||
Keys.KEY_SIZE,
|
||||
Keys.KEY_CURVE_OID,
|
||||
Keys.HAS_SECRET,
|
||||
Keys.CAN_CERTIFY,
|
||||
Keys.CAN_ENCRYPT,
|
||||
Keys.CAN_SIGN,
|
||||
Keys.CAN_AUTHENTICATE,
|
||||
Keys.IS_REVOKED,
|
||||
Keys.IS_SECURE,
|
||||
Keys.CREATION,
|
||||
Keys.EXPIRY,
|
||||
Keys.FINGERPRINT
|
||||
};
|
||||
private static final int INDEX_ID = 0;
|
||||
private static final int INDEX_KEY_ID = 1;
|
||||
private static final int INDEX_RANK = 2;
|
||||
private static final int INDEX_ALGORITHM = 3;
|
||||
private static final int INDEX_KEY_SIZE = 4;
|
||||
private static final int INDEX_KEY_CURVE_OID = 5;
|
||||
private static final int INDEX_HAS_SECRET = 6;
|
||||
private static final int INDEX_CAN_CERTIFY = 7;
|
||||
private static final int INDEX_CAN_ENCRYPT = 8;
|
||||
private static final int INDEX_CAN_SIGN = 9;
|
||||
private static final int INDEX_CAN_AUTHENTICATE = 10;
|
||||
private static final int INDEX_IS_REVOKED = 11;
|
||||
private static final int INDEX_IS_SECURE = 12;
|
||||
private static final int INDEX_CREATION = 13;
|
||||
private static final int INDEX_EXPIRY = 14;
|
||||
private static final int INDEX_FINGERPRINT = 15;
|
||||
|
||||
public SubkeysAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
|
||||
mInflater = LayoutInflater.from(context);
|
||||
public SubkeysAdapter(Context context) {
|
||||
this.context = context;
|
||||
layoutInflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
public long getKeyId(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return mCursor.getLong(INDEX_KEY_ID);
|
||||
public void setData(List<SubKey> data) {
|
||||
this.data = data;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public long getCreationDate(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return mCursor.getLong(INDEX_CREATION);
|
||||
@Override
|
||||
public int getCount() {
|
||||
return data != null ? data.size() : 0;
|
||||
}
|
||||
|
||||
public Long getExpiryDate(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
if (mCursor.isNull(INDEX_EXPIRY)) {
|
||||
return null;
|
||||
@Override
|
||||
public SubKey getItem(int position) {
|
||||
return data.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return data.get(position).key_id();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View view;
|
||||
if (convertView != null) {
|
||||
view = convertView;
|
||||
} else {
|
||||
return mCursor.getLong(INDEX_EXPIRY);
|
||||
}
|
||||
}
|
||||
|
||||
public int getAlgorithm(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return mCursor.getInt(INDEX_ALGORITHM);
|
||||
}
|
||||
|
||||
public int getKeySize(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return mCursor.getInt(INDEX_KEY_SIZE);
|
||||
}
|
||||
|
||||
public String getCurveOid(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return mCursor.getString(INDEX_KEY_CURVE_OID);
|
||||
}
|
||||
|
||||
public SecretKeyType getSecretKeyType(int position) {
|
||||
mCursor.moveToPosition(position);
|
||||
return SecretKeyType.fromNum(mCursor.getInt(INDEX_HAS_SECRET));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor swapCursor(Cursor newCursor) {
|
||||
mHasAnySecret = false;
|
||||
if (newCursor != null && newCursor.moveToFirst()) {
|
||||
do {
|
||||
SecretKeyType hasSecret = SecretKeyType.fromNum(newCursor.getInt(INDEX_HAS_SECRET));
|
||||
if (hasSecret.isUsable()) {
|
||||
mHasAnySecret = true;
|
||||
break;
|
||||
}
|
||||
} while (newCursor.moveToNext());
|
||||
view = layoutInflater.inflate(R.layout.view_key_adv_subkey_item, parent, false);
|
||||
}
|
||||
|
||||
return super.swapCursor(newCursor);
|
||||
}
|
||||
if (mDefaultTextColor == null) {
|
||||
TextView keyId = view.findViewById(R.id.subkey_item_key_id);
|
||||
mDefaultTextColor = keyId.getTextColors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
TextView vKeyId = view.findViewById(R.id.subkey_item_key_id);
|
||||
TextView vKeyDetails = view.findViewById(R.id.subkey_item_details);
|
||||
TextView vKeyExpiry = view.findViewById(R.id.subkey_item_expiry);
|
||||
@@ -166,19 +108,20 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
ImageView deleteImage = view.findViewById(R.id.subkey_item_delete_button);
|
||||
deleteImage.setVisibility(View.GONE);
|
||||
|
||||
long keyId = cursor.getLong(INDEX_KEY_ID);
|
||||
vKeyId.setText(KeyFormattingUtils.beautifyKeyId(keyId));
|
||||
SubKey subKey = getItem(position);
|
||||
|
||||
vKeyId.setText(KeyFormattingUtils.beautifyKeyId(subKey.key_id()));
|
||||
|
||||
// may be set with additional "stripped" later on
|
||||
SpannableStringBuilder algorithmStr = new SpannableStringBuilder();
|
||||
algorithmStr.append(KeyFormattingUtils.getAlgorithmInfo(
|
||||
context,
|
||||
cursor.getInt(INDEX_ALGORITHM),
|
||||
cursor.getInt(INDEX_KEY_SIZE),
|
||||
cursor.getString(INDEX_KEY_CURVE_OID)
|
||||
subKey.algorithm(),
|
||||
subKey.key_size(),
|
||||
subKey.key_curve_oid()
|
||||
));
|
||||
|
||||
SubkeyChange change = mSkpBuilder != null ? mSkpBuilder.getSubkeyChange(keyId) : null;
|
||||
SubkeyChange change = mSkpBuilder != null ? mSkpBuilder.getSubkeyChange(subKey.key_id()) : null;
|
||||
if (change != null && (change.getDummyStrip() || change.getMoveKeyToSecurityToken())) {
|
||||
if (change.getDummyStrip()) {
|
||||
algorithmStr.append(", ");
|
||||
@@ -197,7 +140,7 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
algorithmStr.append(boldDivert);
|
||||
}
|
||||
} else {
|
||||
switch (SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET))) {
|
||||
switch (subKey.has_secret()) {
|
||||
case GNU_DUMMY:
|
||||
algorithmStr.append(", ");
|
||||
algorithmStr.append(context.getString(R.string.key_stripped));
|
||||
@@ -218,7 +161,7 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
}
|
||||
vKeyDetails.setText(algorithmStr, TextView.BufferType.SPANNABLE);
|
||||
|
||||
boolean isMasterKey = cursor.getInt(INDEX_RANK) == 0;
|
||||
boolean isMasterKey = subKey.rank() == 0;
|
||||
if (isMasterKey) {
|
||||
vKeyId.setTypeface(null, Typeface.BOLD);
|
||||
} else {
|
||||
@@ -226,22 +169,21 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
}
|
||||
|
||||
// Set icons according to properties
|
||||
vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
|
||||
vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
|
||||
vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
|
||||
vAuthenticateIcon.setVisibility(cursor.getInt(INDEX_CAN_AUTHENTICATE) != 0 ? View.VISIBLE : View.GONE);
|
||||
vCertifyIcon.setVisibility(subKey.can_certify() ? View.VISIBLE : View.GONE);
|
||||
vEncryptIcon.setVisibility(subKey.can_encrypt() ? View.VISIBLE : View.GONE);
|
||||
vSignIcon.setVisibility(subKey.can_sign() ? View.VISIBLE : View.GONE);
|
||||
vAuthenticateIcon.setVisibility(subKey.can_authenticate() ? View.VISIBLE : View.GONE);
|
||||
|
||||
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
|
||||
boolean isSecure = cursor.getInt(INDEX_IS_SECURE) > 0;
|
||||
boolean isRevoked = subKey.is_revoked();
|
||||
|
||||
Date expiryDate = null;
|
||||
if (!cursor.isNull(INDEX_EXPIRY)) {
|
||||
expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
|
||||
if (subKey.expires()) {
|
||||
expiryDate = new Date(subKey.expiry() * 1000);
|
||||
}
|
||||
|
||||
// for edit key
|
||||
if (mSkpBuilder != null) {
|
||||
boolean revokeThisSubkey = (mSkpBuilder.getMutableRevokeSubKeys().contains(keyId));
|
||||
boolean revokeThisSubkey = (mSkpBuilder.getMutableRevokeSubKeys().contains(subKey.key_id()));
|
||||
|
||||
if (revokeThisSubkey) {
|
||||
if (!isRevoked) {
|
||||
@@ -249,7 +191,7 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
SaveKeyringParcel.SubkeyChange subkeyChange = mSkpBuilder.getSubkeyChange(keyId);
|
||||
SaveKeyringParcel.SubkeyChange subkeyChange = mSkpBuilder.getSubkeyChange(subKey.key_id());
|
||||
if (subkeyChange != null) {
|
||||
if (subkeyChange.getExpiry() == null || subkeyChange.getExpiry() == 0L) {
|
||||
expiryDate = null;
|
||||
@@ -280,37 +222,37 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
}
|
||||
|
||||
// if key is expired or revoked...
|
||||
boolean isInvalid = isRevoked || isExpired || !isSecure;
|
||||
boolean isInvalid = isRevoked || isExpired || !subKey.is_secure();
|
||||
if (isInvalid) {
|
||||
vStatus.setVisibility(View.VISIBLE);
|
||||
|
||||
vCertifyIcon.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
vSignIcon.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
vEncryptIcon.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
vAuthenticateIcon.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
|
||||
if (isRevoked) {
|
||||
vStatus.setImageResource(R.drawable.status_signature_revoked_cutout_24dp);
|
||||
vStatus.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
} else if (isExpired) {
|
||||
vStatus.setImageResource(R.drawable.status_signature_expired_cutout_24dp);
|
||||
vStatus.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
} else if (!isSecure) {
|
||||
} else if (!subKey.is_secure()) {
|
||||
vStatus.setImageResource(R.drawable.status_signature_invalid_cutout_24dp);
|
||||
vStatus.setColorFilter(
|
||||
mContext.getResources().getColor(R.color.key_flag_gray),
|
||||
context.getResources().getColor(R.color.key_flag_gray),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
} else {
|
||||
@@ -328,36 +270,20 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
vKeyId.setEnabled(!isInvalid);
|
||||
vKeyDetails.setEnabled(!isInvalid);
|
||||
vKeyExpiry.setEnabled(!isInvalid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
View view = mInflater.inflate(R.layout.view_key_adv_subkey_item, null);
|
||||
if (mDefaultTextColor == null) {
|
||||
TextView keyId = view.findViewById(R.id.subkey_item_key_id);
|
||||
mDefaultTextColor = keyId.getTextColors();
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
// Disable selection of items, http://stackoverflow.com/a/4075045
|
||||
@Override
|
||||
public boolean areAllItemsEnabled() {
|
||||
if (mSkpBuilder == null) {
|
||||
return false;
|
||||
} else {
|
||||
return super.areAllItemsEnabled();
|
||||
}
|
||||
return mSkpBuilder != null && super.areAllItemsEnabled();
|
||||
}
|
||||
|
||||
// Disable selection of items, http://stackoverflow.com/a/4075045
|
||||
@Override
|
||||
public boolean isEnabled(int position) {
|
||||
if (mSkpBuilder == null) {
|
||||
return false;
|
||||
} else {
|
||||
return super.isEnabled(position);
|
||||
}
|
||||
return mSkpBuilder != null && super.isEnabled(position);
|
||||
}
|
||||
|
||||
/** Set this adapter into edit mode. This mode displays additional info for
|
||||
@@ -372,6 +298,7 @@ public class SubkeysAdapter extends CursorAdapter {
|
||||
*/
|
||||
public void setEditMode(@Nullable SaveKeyringParcel.Builder builder) {
|
||||
mSkpBuilder = builder;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,97 +24,59 @@ import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.model.SubKey;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants;
|
||||
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||
import timber.log.Timber;
|
||||
import org.sufficientlysecure.keychain.provider.KeyRepository;
|
||||
|
||||
|
||||
public class SubkeyStatusDao {
|
||||
public static final String[] PROJECTION = new String[] {
|
||||
Keys.KEY_ID,
|
||||
Keys.CREATION,
|
||||
Keys.CAN_CERTIFY,
|
||||
Keys.CAN_SIGN,
|
||||
Keys.CAN_ENCRYPT,
|
||||
Keys.HAS_SECRET,
|
||||
Keys.EXPIRY,
|
||||
Keys.IS_REVOKED,
|
||||
Keys.ALGORITHM,
|
||||
Keys.KEY_SIZE,
|
||||
Keys.KEY_CURVE_OID
|
||||
};
|
||||
private static final int INDEX_KEY_ID = 0;
|
||||
private static final int INDEX_CREATION = 1;
|
||||
private static final int INDEX_CAN_CERTIFY = 2;
|
||||
private static final int INDEX_CAN_SIGN = 3;
|
||||
private static final int INDEX_CAN_ENCRYPT = 4;
|
||||
private static final int INDEX_HAS_SECRET = 5;
|
||||
private static final int INDEX_EXPIRY = 6;
|
||||
private static final int INDEX_IS_REVOKED = 7;
|
||||
private static final int INDEX_ALGORITHM = 8;
|
||||
private static final int INDEX_KEY_SIZE = 9;
|
||||
private static final int INDEX_KEY_CURVE_OID = 10;
|
||||
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
private final KeyRepository keyRepository;
|
||||
|
||||
|
||||
public static SubkeyStatusDao getInstance(Context context) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
return new SubkeyStatusDao(contentResolver);
|
||||
KeyRepository keyRepository = KeyRepository.create(context);
|
||||
return new SubkeyStatusDao(keyRepository);
|
||||
}
|
||||
|
||||
private SubkeyStatusDao(ContentResolver contentResolver) {
|
||||
this.contentResolver = contentResolver;
|
||||
private SubkeyStatusDao(KeyRepository keyRepository) {
|
||||
this.keyRepository = keyRepository;
|
||||
}
|
||||
|
||||
KeySubkeyStatus getSubkeyStatus(long masterKeyId, Comparator<SubKeyItem> comparator) {
|
||||
Cursor cursor = contentResolver.query(Keys.buildKeysUri(masterKeyId), PROJECTION, null, null, null);
|
||||
if (cursor == null) {
|
||||
Timber.e("Error loading key items!");
|
||||
SubKeyItem keyCertify = null;
|
||||
ArrayList<SubKeyItem> keysSign = new ArrayList<>();
|
||||
ArrayList<SubKeyItem> keysEncrypt = new ArrayList<>();
|
||||
for (SubKey subKey : keyRepository.getSubKeysByMasterKeyId(masterKeyId)) {
|
||||
SubKeyItem ski = new SubKeyItem(masterKeyId, subKey);
|
||||
|
||||
if (ski.mKeyId == masterKeyId) {
|
||||
keyCertify = ski;
|
||||
}
|
||||
|
||||
if (ski.mCanSign) {
|
||||
keysSign.add(ski);
|
||||
}
|
||||
if (ski.mCanEncrypt) {
|
||||
keysEncrypt.add(ski);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCertify == null) {
|
||||
if (!keysSign.isEmpty() || !keysEncrypt.isEmpty()) {
|
||||
throw new IllegalStateException("Certification key can't be missing for a key that hasn't been deleted!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
SubKeyItem keyCertify = null;
|
||||
ArrayList<SubKeyItem> keysSign = new ArrayList<>();
|
||||
ArrayList<SubKeyItem> keysEncrypt = new ArrayList<>();
|
||||
while (cursor.moveToNext()) {
|
||||
SubKeyItem ski = new SubKeyItem(masterKeyId, cursor);
|
||||
Collections.sort(keysSign, comparator);
|
||||
Collections.sort(keysEncrypt, comparator);
|
||||
|
||||
if (ski.mKeyId == masterKeyId) {
|
||||
keyCertify = ski;
|
||||
}
|
||||
|
||||
if (ski.mCanSign) {
|
||||
keysSign.add(ski);
|
||||
}
|
||||
if (ski.mCanEncrypt) {
|
||||
keysEncrypt.add(ski);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCertify == null) {
|
||||
if (!keysSign.isEmpty() || !keysEncrypt.isEmpty()) {
|
||||
throw new IllegalStateException("Certification key can't be missing for a key that hasn't been deleted!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Collections.sort(keysSign, comparator);
|
||||
Collections.sort(keysEncrypt, comparator);
|
||||
|
||||
return new KeySubkeyStatus(keyCertify, keysSign, keysEncrypt);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
return new KeySubkeyStatus(keyCertify, keysSign, keysEncrypt);
|
||||
}
|
||||
|
||||
public static class KeySubkeyStatus {
|
||||
@@ -131,7 +93,6 @@ public class SubkeyStatusDao {
|
||||
}
|
||||
|
||||
public static class SubKeyItem {
|
||||
final int mPosition;
|
||||
final long mKeyId;
|
||||
final Date mCreation;
|
||||
public final SecretKeyType mSecretKeyType;
|
||||
@@ -140,25 +101,23 @@ public class SubkeyStatusDao {
|
||||
final boolean mCanCertify, mCanSign, mCanEncrypt;
|
||||
public final KeySecurityProblem mSecurityProblem;
|
||||
|
||||
SubKeyItem(long masterKeyId, Cursor cursor) {
|
||||
mPosition = cursor.getPosition();
|
||||
SubKeyItem(long masterKeyId, SubKey subKey) {
|
||||
mKeyId = subKey.key_id();
|
||||
mCreation = new Date(subKey.creation() * 1000);
|
||||
|
||||
mKeyId = cursor.getLong(INDEX_KEY_ID);
|
||||
mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000);
|
||||
mSecretKeyType = subKey.has_secret();
|
||||
|
||||
mSecretKeyType = SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET));
|
||||
|
||||
mIsRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
|
||||
mExpiry = cursor.isNull(INDEX_EXPIRY) ? null : new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
|
||||
mIsRevoked = subKey.is_revoked();
|
||||
mExpiry = subKey.expiry() == null ? null : new Date(subKey.expiry() * 1000);
|
||||
mIsExpired = mExpiry != null && mExpiry.before(new Date());
|
||||
|
||||
mCanCertify = cursor.getInt(INDEX_CAN_CERTIFY) > 0;
|
||||
mCanSign = cursor.getInt(INDEX_CAN_SIGN) > 0;
|
||||
mCanEncrypt = cursor.getInt(INDEX_CAN_ENCRYPT) > 0;
|
||||
mCanCertify = subKey.can_certify();
|
||||
mCanSign = subKey.can_sign();
|
||||
mCanEncrypt = subKey.can_encrypt();
|
||||
|
||||
int algorithm = cursor.getInt(INDEX_ALGORITHM);
|
||||
Integer bitStrength = cursor.isNull(INDEX_KEY_SIZE) ? null : cursor.getInt(INDEX_KEY_SIZE);
|
||||
String curveOid = cursor.getString(INDEX_KEY_CURVE_OID);
|
||||
int algorithm = subKey.algorithm();
|
||||
Integer bitStrength = subKey.key_size();
|
||||
String curveOid = subKey.key_curve_oid();
|
||||
|
||||
mSecurityProblem = PgpSecurityConstants.getKeySecurityProblem(
|
||||
masterKeyId, mKeyId, algorithm, bitStrength, curveOid);
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import java.lang.Boolean;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS keys (
|
||||
master_key_id INTEGER NOT NULL,
|
||||
rank INTEGER NOT NULL,
|
||||
key_id INTEGER NOT NULL,
|
||||
key_size INTEGER,
|
||||
key_size INTEGER AS Integer,
|
||||
key_curve_oid TEXT,
|
||||
algorithm INTEGER NOT NULL,
|
||||
algorithm INTEGER AS Integer NOT NULL,
|
||||
fingerprint BLOB NOT NULL,
|
||||
can_certify INTEGER AS Boolean NOT NULL,
|
||||
can_sign INTEGER AS Boolean NOT NULL,
|
||||
can_encrypt INTEGER AS Boolean NOT NULL,
|
||||
can_authenticate INTEGER AS Boolean NOT NULL,
|
||||
is_revoked INTEGER AS Boolean NOT NULL,
|
||||
has_secret INTEGER AS Boolean NOT NULL,
|
||||
has_secret INTEGER AS SecretKeyType NOT NULL,
|
||||
is_secure INTEGER AS Boolean NOT NULL,
|
||||
creation INTEGER NOT NULL,
|
||||
expiry INTEGER,
|
||||
@@ -34,4 +35,15 @@ SELECT keys.master_key_id, MIN(user_packets.rank), user_packets.name, user_packe
|
||||
LEFT JOIN autocrypt_peers AS aTI ON ( aTI.master_key_id = keys.master_key_id )
|
||||
WHERE keys.rank = 0
|
||||
GROUP BY keys.master_key_id
|
||||
ORDER BY has_secret DESC, user_packets.name COLLATE NOCASE ASC;
|
||||
ORDER BY has_secret DESC, user_packets.name COLLATE NOCASE ASC;
|
||||
|
||||
selectSubkeysByMasterKeyId:
|
||||
SELECT *
|
||||
FROM keys
|
||||
WHERE master_key_id = ?
|
||||
ORDER BY rank ASC;
|
||||
|
||||
selectSecretKeyType:
|
||||
SELECT has_secret
|
||||
FROM keys
|
||||
WHERE key_id = ?;
|
||||
Reference in New Issue
Block a user