stripped support: implement has_secret support, in ui and impot of secret keys

Closes #570
This commit is contained in:
Vincent Breitmoser 2014-04-16 17:33:33 +02:00
parent 66b8b86695
commit c7eb40b814
8 changed files with 101 additions and 14 deletions

View File

@ -703,6 +703,20 @@ public class KeychainProvider extends ContentProvider {
try {
final int match = mUriMatcher.match(uri);
switch (match) {
case KEY_RING_KEYS: {
if(values.size() != 1 || !values.containsKey(Keys.HAS_SECRET)) {
throw new UnsupportedOperationException(
"Only has_secret column may be updated!");
}
// make sure we get a long value here
Long mkid = Long.parseLong(uri.getPathSegments().get(1));
String actualSelection = Keys.MASTER_KEY_ID + " = " + Long.toString(mkid);
if(!TextUtils.isEmpty(selection)) {
actualSelection += " AND (" + selection + ")";
}
count = db.update(Tables.KEYS, values, actualSelection, selectionArgs);
break;
}
case API_APPS_BY_PACKAGE_NAME:
count = db.update(Tables.API_APPS, values,
buildDefaultApiAppsSelection(uri, selection), selectionArgs);
@ -719,7 +733,7 @@ public class KeychainProvider extends ContentProvider {
getContext().getContentResolver().notifyChange(uri, null);
} catch (SQLiteConstraintException e) {
Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?");
Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?", e);
}
return count;

View File

@ -28,12 +28,15 @@ import android.net.Uri;
import android.os.RemoteException;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
@ -387,13 +390,38 @@ public class ProviderHelper {
public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException {
long masterKeyId = keyRing.getPublicKey().getKeyID();
{
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
// first, mark all keys as not available
ContentValues values = new ContentValues();
values.put(Keys.HAS_SECRET, 0);
mContentResolver.update(uri, values, null, null);
values.put(Keys.HAS_SECRET, 1);
// then, mark exactly the keys we have available
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
// Set to 1, except if the encryption type is GNU_DUMMY_S2K
if(sub.getS2K().getType() != S2K.GNU_DUMMY_S2K) {
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
Long.toString(sub.getKeyID())
});
}
}
// this implicitly leaves all keys which were not in the secret key ring
// with has_secret = 0
}
// save secret keyring
ContentValues values = new ContentValues();
values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
mContentResolver.insert(uri, values);
{
ContentValues values = new ContentValues();
values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
mContentResolver.insert(uri, values);
}
}
/**

View File

@ -117,6 +117,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
KeyRings.IS_REVOKED,
KeyRings.CAN_CERTIFY,
KeyRings.HAS_SIGN,
KeyRings.HAS_SECRET,
KeyRings.HAS_ANY_SECRET
};
@ -151,7 +152,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
private class SelectSecretKeyCursorAdapter extends SelectKeyCursorAdapter {
private int mIndexHasSign, mIndexCanCertify;
private int mIndexHasSecret, mIndexHasSign, mIndexCanCertify;
public SelectSecretKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) {
super(context, c, flags, listView);
@ -161,6 +162,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
protected void initIndex(Cursor cursor) {
super.initIndex(cursor);
if (cursor != null) {
mIndexHasSecret = cursor.getColumnIndexOrThrow(KeyRings.HAS_SECRET);
mIndexCanCertify = cursor.getColumnIndexOrThrow(KeyRings.CAN_CERTIFY);
mIndexHasSign = cursor.getColumnIndexOrThrow(KeyRings.HAS_SIGN);
}
@ -177,8 +179,10 @@ public class SelectSecretKeyFragment extends ListFragment implements
// Special from superclass: Te
boolean enabled = false;
if((Boolean) h.status.getTag()) {
if (cursor.getInt(mIndexHasSecret) == 0) {
h.status.setText(R.string.no_subkey);
// Check if key is viable for our purposes (certify or sign)
if(mFilterCertify) {
} else if(mFilterCertify) {
if (cursor.getInt(mIndexCanCertify) == 0) {
h.status.setText(R.string.can_certify_not);
} else {

View File

@ -174,11 +174,11 @@ public class ViewKeyMainFragment extends Fragment implements
static final String[] KEYS_PROJECTION = new String[] {
Keys._ID,
Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE,
Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.HAS_SECRET,
Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED,
Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT
};
static final int KEYS_INDEX_CAN_ENCRYPT = 6;
static final int KEYS_INDEX_CAN_ENCRYPT = 7;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {

View File

@ -45,9 +45,12 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
private int mIndexCanCertify;
private int mIndexCanEncrypt;
private int mIndexCanSign;
private int mIndexHasSecret;
private int mIndexRevokedKey;
private int mIndexExpiry;
private boolean hasAnySecret;
private ColorStateList mDefaultTextColor;
public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {
@ -62,6 +65,17 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
public Cursor swapCursor(Cursor newCursor) {
initIndex(newCursor);
hasAnySecret = false;
if (newCursor != null) {
newCursor.moveToFirst();
do {
if(newCursor.getInt(mIndexHasSecret) != 0) {
hasAnySecret = true;
break;
}
} while(newCursor.moveToNext());
}
return super.swapCursor(newCursor);
}
@ -80,6 +94,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY);
mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT);
mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN);
mIndexHasSecret = cursor.getColumnIndexOrThrow(Keys.HAS_SECRET);
mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED);
mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY);
}
@ -101,7 +116,13 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
cursor.getInt(mIndexKeySize));
keyId.setText(keyIdStr);
keyDetails.setText("(" + algorithmStr + ")");
// may be set with additional "stripped" later on
if(hasAnySecret && cursor.getInt(mIndexHasSecret) == 0) {
keyDetails.setText("(" + algorithmStr + ", " +
context.getString(R.string.key_stripped) + ")");
} else {
keyDetails.setText("(" + algorithmStr + ")");
}
if (cursor.getInt(mIndexRank) == 0) {
masterKeyIcon.setVisibility(View.INVISIBLE);

View File

@ -535,5 +535,7 @@
<string name="can_certify_not">cannot certify</string>
<string name="error_key_not_found">Key not found!</string>
<string name="error_key_processing">Error processing key!</string>
<string name="no_subkey">subkey unavailable</string>
<string name="key_stripped">stripped</string>
</resources>

View File

@ -278,7 +278,16 @@ public class PGPSecretKey
{
return pub.getUserIDs();
}
/**
* Return the S2K object used to encrypt this secret key.
*
* @return an iterator of Strings.
*/
public S2K getS2K() {
return secret.getS2K();
}
/**
* Return any user attribute vectors associated with the key.
*

View File

@ -431,7 +431,16 @@ public class PGPSecretKey
{
return pub.getUserIDs();
}
/**
* Return the S2K this secret key is encrypted with.
*
* @return the S2K for this key.
*/
public S2K getS2K() {
return secret.getS2K();
}
/**
* Return any user attribute vectors associated with the key.
*