extract subkey loading from KeychainProvider

This commit is contained in:
Vincent Breitmoser
2018-06-22 19:23:00 +02:00
parent 500c219fa0
commit 6cd065a3bd
17 changed files with 248 additions and 421 deletions

View File

@@ -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);
}
}

View File

@@ -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();
}
};
}

View File

@@ -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;
}
}
}

View File

@@ -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)) {

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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();
}
};

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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();
}
}

View File

@@ -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);