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

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