From c7eb40b8145fa70b05b1c4cfe01852a95a669e11 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 16 Apr 2014 17:33:33 +0200 Subject: stripped support: implement has_secret support, in ui and impot of secret keys Closes #570 --- .../keychain/provider/KeychainProvider.java | 16 ++++++++- .../keychain/provider/ProviderHelper.java | 40 ++++++++++++++++++---- .../keychain/ui/SelectSecretKeyFragment.java | 8 +++-- .../keychain/ui/ViewKeyMainFragment.java | 4 +-- .../keychain/ui/adapter/ViewKeyKeysAdapter.java | 23 ++++++++++++- OpenKeychain/src/main/res/values/strings.xml | 2 ++ .../org/spongycastle/openpgp/PGPSecretKey.java | 11 +++++- .../org/spongycastle/openpgp/PGPSecretKey.java | 11 +++++- 8 files changed, 101 insertions(+), 14 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 35932b5c9..83986c254 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -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; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 1da4ffe55..01e95343d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -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(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); + } + } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java index 5b46df8ae..118449b9b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -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 { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index f7bea33a0..39eceda81 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -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 onCreateLoader(int id, Bundle args) { switch (id) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 534fbfb02..71be6e5f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -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); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 1cb4da986..0a855ae70 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -535,5 +535,7 @@ cannot certify Key not found! Error processing key! + subkey unavailable + stripped diff --git a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java index 2554be8f1..1fa5261b1 100644 --- a/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java +++ b/libraries/spongycastle/pg/src/main/j2me/org/spongycastle/openpgp/PGPSecretKey.java @@ -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. * diff --git a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java index c0f7dfa3b..f7e3a50c7 100644 --- a/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java +++ b/libraries/spongycastle/pg/src/main/java/org/spongycastle/openpgp/PGPSecretKey.java @@ -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. * -- cgit v1.2.3