From 115d34ba0e22dca586e7205614c48f3fbf4cad7d Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 14 Mar 2013 03:23:50 +0000 Subject: [PATCH 01/14] private keys without master keys and some fixes for things which broke along the way --- .../keychain/helper/PgpHelper.java | 32 ++++++++- .../keychain/helper/PgpMain.java | 28 ++++---- .../ui/dialog/PassphraseDialogFragment.java | 70 +++++++++++++------ 3 files changed, 95 insertions(+), 35 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java index f59bd58de..b77fc71d1 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -24,12 +24,16 @@ import java.util.Locale; import java.util.Vector; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPPrivateKey; 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.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.PGPException; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.IterableIterator; @@ -76,6 +80,22 @@ public class PgpHelper { return null; } + @SuppressWarnings("unchecked") + public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) { + long cnt = 0; + if (keyRing == null) { + return null; + } + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (cnt == num) { + return key; + } + cnt++; + } + + return null; + } + @SuppressWarnings("unchecked") public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) { Vector encryptKeys = new Vector(); @@ -144,7 +164,17 @@ public class PgpHelper { for (int i = 0; i < signingKeys.size(); ++i) { PGPSecretKey key = signingKeys.get(i); if (key.isMasterKey()) { - masterKey = key; + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); + PGPPrivateKey testKey = key.extractPrivateKey( + keyDecryptor); + if (testKey != null) { + masterKey = key; + } + } catch (PGPException e) { + // all good if this fails, we likely didn't use the right password + } } else { usableKeys.add(key); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java index 26842e4dc..26739f564 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java @@ -84,6 +84,7 @@ import org.sufficientlysecure.keychain.util.PositionAwareInputStream; import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException; +import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.R; import android.content.Context; @@ -483,25 +484,28 @@ public class PgpMain { * @param keyring * @return */ + @SuppressWarnings("unchecked") public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { int status = Integer.MIN_VALUE; // out of bounds value (Id.return_value.*) try { if (keyring instanceof PGPSecretKeyRing) { PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; boolean save = true; - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); - PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey( - keyDecryptor); - if (testKey == null) { - // this is bad, something is very wrong... likely a --export-secret-subkeys - // export - save = false; - status = Id.return_value.bad; + + for (PGPSecretKey testSecretKey : new IterableIterator(secretKeyRing.getSecretKeys())) { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); + PGPPrivateKey testKey = testSecretKey.extractPrivateKey( + keyDecryptor); + if (testKey == null && !testSecretKey.isMasterKey()) { + // this is bad, something is very wrong... + save = false; + status = Id.return_value.bad; + } + } catch (PGPException e) { + // all good if this fails, we likely didn't use the right password } - } catch (PGPException e) { - // all good if this fails, we likely didn't use the right password } if (save) { diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java index 504de9d69..6f81b9319 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.dialog; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.sufficientlysecure.keychain.Constants; @@ -63,6 +64,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor private Messenger mMessenger; private EditText mPassphraseEditText; + private boolean canKB; /** * Creates new instance of this dialog fragment @@ -137,8 +139,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); - - long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); + final long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); mMessenger = getArguments().getParcelable(ARG_MESSENGER); AlertDialog.Builder alert = new AlertDialog.Builder(activity); @@ -152,8 +153,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor alert.setMessage(R.string.passPhraseForSymmetricEncryption); } else { // TODO: by master key id??? - secretKey = PgpHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - activity, secretKeyId)); + secretKey = PgpHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByKeyId(activity, secretKeyId)); // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); if (secretKey == null) { @@ -165,6 +165,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor } }); alert.setCancelable(false); + canKB = false; return alert.create(); } String userId = PgpHelper.getMainUserIdSafe(activity, secretKey); @@ -184,24 +185,43 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor @Override public void onClick(DialogInterface dialog, int id) { dismiss(); - + long curKeyIndex = 0; + boolean keyOK = true; String passPhrase = mPassphraseEditText.getText().toString(); long keyId; - if (secretKey != null) { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME).build( - passPhrase.toCharArray()); - PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); - if (testKey == null) { + PGPSecretKey clickSecretKey = secretKey; + + if (clickSecretKey != null) { + while (keyOK == true) { + if (clickSecretKey != null) { //check again for loop + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME).build( + passPhrase.toCharArray()); + PGPPrivateKey testKey = clickSecretKey.extractPrivateKey(keyDecryptor); + if (testKey == null) { + if (!clickSecretKey.isMasterKey()) { + Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey, + Toast.LENGTH_SHORT).show(); + return; + } else { + clickSecretKey = PgpHelper.getKeyNum(ProviderHelper.getPGPSecretKeyRingByKeyId(activity, secretKeyId), curKeyIndex); + curKeyIndex++; //does post-increment work like C? + continue; + } + } else { + keyOK = false; + } + } catch (PGPException e) { + Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT) + .show(); + return; + } + } else { Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey, Toast.LENGTH_SHORT).show(); - return; + return; //ran out of keys to try } - } catch (PGPException e) { - Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT) - .show(); - return; } keyId = secretKey.getKeyID(); } else { @@ -211,6 +231,9 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor // cache the new passphrase Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase); + if (keyOK == false) { + PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(), passPhrase); + } sendMessageToHandler(MESSAGE_OKAY); } @@ -224,18 +247,20 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor } }); + canKB = true; return alert.create(); } @Override public void onActivityCreated(Bundle arg0) { super.onActivityCreated(arg0); - + if (canKB) { // request focus and open soft keyboard - mPassphraseEditText.requestFocus(); - getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); + mPassphraseEditText.requestFocus(); + getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - mPassphraseEditText.setOnEditorActionListener(this); + mPassphraseEditText.setOnEditorActionListener(this); + } } /** @@ -272,4 +297,5 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor } } -} \ No newline at end of file +} + From cf42bcd039d6d2b8a109ed8a3d91fed6ced92588 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 14 Mar 2013 12:28:22 +0000 Subject: [PATCH 02/14] skip key 0 (master) on retry for passphrase --- .../keychain/ui/dialog/PassphraseDialogFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java index 6f81b9319..10624b18b 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -185,7 +185,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor @Override public void onClick(DialogInterface dialog, int id) { dismiss(); - long curKeyIndex = 0; + long curKeyIndex = 1; boolean keyOK = true; String passPhrase = mPassphraseEditText.getText().toString(); long keyId; From 911719bdb67a4dd7725706de9fb8a28b994b089d Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 14 Mar 2013 12:58:09 +0000 Subject: [PATCH 03/14] don't cache passphrase twice --- .../keychain/ui/dialog/PassphraseDialogFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java index 10624b18b..10fc74551 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -231,7 +231,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor // cache the new passphrase Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase); - if (keyOK == false) { + if (keyOK == false && clickSecretKey.getKeyID() != keyId) { PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(), passPhrase); } From 00afc2e8acd73ff031417d70208d9a955a7086de Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 14 Mar 2013 13:23:04 +0000 Subject: [PATCH 04/14] fix issue 42 --- OpenPGP-Keychain/res/layout/key_server_editor.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/res/layout/key_server_editor.xml b/OpenPGP-Keychain/res/layout/key_server_editor.xml index e4c25b316..51ff8a663 100644 --- a/OpenPGP-Keychain/res/layout/key_server_editor.xml +++ b/OpenPGP-Keychain/res/layout/key_server_editor.xml @@ -14,7 +14,7 @@ limitations under the License. --> - - + From 935274960d6a81c64fe829dd3cada4436e044e90 Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 14 Mar 2013 17:24:22 +0000 Subject: [PATCH 05/14] remove signing icon for master keys which can't sign --- .../keychain/helper/PgpHelper.java | 24 +++++++++++++++++++ .../keychain/ui/widget/KeyListAdapter.java | 12 +++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java index b77fc71d1..697eb1aaa 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -415,6 +415,30 @@ public class PgpHelper { return convertFingerprintToHex(key.getFingerprint()); } + public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); + PGPPrivateKey testKey = secretKey.extractPrivateKey( + keyDecryptor); + if (testKey != null) { + return false; + } + } catch (PGPException e) { + // all good if this fails, we likely didn't use the right password + } + return true; + } + + public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) { + PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); + if (secretKey == null) { + Log.e(Constants.TAG, "Key could not be found!"); + return false; //could be a public key, assume it is not empty + } + return isSecretKeyPrivateEmpty(secretKey); + } + public static String getSmallFingerPrint(long keyId) { String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US); while (fingerPrint.length() < 8) { diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java index 8e8d33886..61ca0e9d0 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java @@ -159,8 +159,14 @@ public class KeyListAdapter extends CursorTreeAdapter { } ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { - signIcon.setVisibility(View.GONE); + boolean privateEmpty = false; //Don't show signing icon for master keys without private keys + //TODO: does this need to be done for encrypting icon? Does anyone use master key for encrypt? + if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) == 1) { + privateEmpty = PgpHelper.isSecretKeyPrivateEmpty(context, + cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID))); + } + if (privateEmpty || cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { + signIcon.setVisibility(View.GONE); } else { signIcon.setVisibility(View.VISIBLE); } @@ -261,4 +267,4 @@ public class KeyListAdapter extends CursorTreeAdapter { return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder); } -} \ No newline at end of file +} From dd27d132beb47fb8878a36df3a2ccc8455c81ec2 Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 14 Mar 2013 22:04:24 +0000 Subject: [PATCH 06/14] tidy up code --- .../keychain/helper/PgpHelper.java | 16 ++++------------ .../keychain/helper/PgpMain.java | 10 ++-------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java index 697eb1aaa..9b8e4cb23 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -164,16 +164,8 @@ public class PgpHelper { for (int i = 0; i < signingKeys.size(); ++i) { PGPSecretKey key = signingKeys.get(i); if (key.isMasterKey()) { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); - PGPPrivateKey testKey = key.extractPrivateKey( - keyDecryptor); - if (testKey != null) { - masterKey = key; - } - } catch (PGPException e) { - // all good if this fails, we likely didn't use the right password + if (!isSecretKeyPrivateEmpty(key)) { + masterKey = key; } } else { usableKeys.add(key); @@ -424,8 +416,8 @@ public class PgpHelper { if (testKey != null) { return false; } - } catch (PGPException e) { - // all good if this fails, we likely didn't use the right password + } catch (PGPException e) { //exception if wrong key => not empty + return false; //all good if this fails, we likely didn't use the right password } return true; } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java index 26739f564..f4b7c7bbd 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java @@ -493,18 +493,12 @@ public class PgpMain { boolean save = true; for (PGPSecretKey testSecretKey : new IterableIterator(secretKeyRing.getSecretKeys())) { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); - PGPPrivateKey testKey = testSecretKey.extractPrivateKey( - keyDecryptor); - if (testKey == null && !testSecretKey.isMasterKey()) { + if (!testSecretKey.isMasterKey()) { + if (PgpHelper.isSecretKeyPrivateEmpty(testSecretKey)) { // this is bad, something is very wrong... save = false; status = Id.return_value.bad; } - } catch (PGPException e) { - // all good if this fails, we likely didn't use the right password } } From 9985722c6e04cbff027caa84fa01e05624e298a3 Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Fri, 15 Mar 2013 21:34:31 +0000 Subject: [PATCH 07/14] correctly encrypt using symmetric key if sign box checked --- .../org/sufficientlysecure/keychain/ui/EncryptActivity.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 9d73fc6e5..c6b2d196a 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -678,6 +678,7 @@ public class EncryptActivity extends SherlockFragmentActivity { long encryptionKeyIds[] = null; int compressionId = 0; boolean signOnly = false; + long mSecretKeyIdToPass = 0; if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); @@ -685,9 +686,9 @@ public class EncryptActivity extends SherlockFragmentActivity { if (passPhrase.length() == 0) { passPhrase = null; } - data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase); } else { + mSecretKeyIdToPass = mSecretKeyId; encryptionKeyIds = mEncryptionKeyIds; signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); } @@ -733,7 +734,7 @@ public class EncryptActivity extends SherlockFragmentActivity { useAsciiArmor = mAsciiArmorDemand; } - data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId); + data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyIdToPass); data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_AMOR, useAsciiArmor); data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds); data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId); From 4fc4f09e84718efdfda41cbd7d8822a393e65a60 Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Fri, 15 Mar 2013 23:24:06 +0000 Subject: [PATCH 08/14] Fix decrypt file selection and symmetric decryption failure --- .../keychain/ui/DecryptActivity.java | 12 ++++++++++-- .../keychain/ui/dialog/FileDialogFragment.java | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java index 87b1204a4..ed81c2aa8 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -62,6 +62,7 @@ import android.widget.ViewFlipper; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.BufferedInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.regex.Matcher; @@ -642,7 +643,7 @@ public class DecryptActivity extends SherlockFragmentActivity { } try { - inStream = new FileInputStream(mInputFilename); + inStream = new BufferedInputStream(new FileInputStream(mInputFilename)); } catch (FileNotFoundException e) { Log.e(Constants.TAG, "File not found!", e); Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), @@ -659,6 +660,9 @@ public class DecryptActivity extends SherlockFragmentActivity { // get decryption key for this inStream try { try { + if (inStream.markSupported()) { + inStream.mark(200); //should probably set this to the max size of two pgpF objects, if it even needs to be anything other than 0. + } mSecretKeyId = PgpMain.getDecryptionKeyId(this, inStream); if (mSecretKeyId == Id.key.none) { throw new PgpMain.PgpGeneralException( @@ -666,6 +670,9 @@ public class DecryptActivity extends SherlockFragmentActivity { } mAssumeSymmetricEncryption = false; } catch (PgpMain.NoAsymmetricEncryptionException e) { + if (inStream.markSupported()) { + inStream.reset(); + } mSecretKeyId = Id.key.symmetric; if (!PgpMain.hasSymmetricEncryption(this, inStream)) { throw new PgpMain.PgpGeneralException( @@ -914,7 +921,7 @@ public class DecryptActivity extends SherlockFragmentActivity { case Id.request.output_filename: { if (resultCode == RESULT_OK && data != null) { try { - String path = data.getData().getPath(); + String path = FileHelper.getPath(this, data.getData()); Log.d(Constants.TAG, "path=" + path); mFileDialog.setFilename(path); @@ -944,3 +951,4 @@ public class DecryptActivity extends SherlockFragmentActivity { } } + diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java index 2f9cdb3b1..7c4d9cea3 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -191,4 +191,5 @@ public class FileDialogFragment extends DialogFragment { Log.w(Constants.TAG, "Messenger is null!", e); } } -} \ No newline at end of file +} + From 2ccd3796c4c6bae5fb587f031dcf848a2ad72ff0 Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Sat, 16 Mar 2013 00:15:06 +0000 Subject: [PATCH 09/14] select correct master key for signing public key with --- .../org/sufficientlysecure/keychain/ui/SignKeyActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java index 21d55aca0..6ded66468 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java @@ -295,7 +295,8 @@ public class SignKeyActivity extends SherlockFragmentActivity { switch (requestCode) { case Id.request.secret_keys: { if (resultCode == RESULT_OK) { - mMasterKeyId = data.getLongExtra(EXTRA_KEY_ID, 0); + Bundle bundle = data.getExtras(); + mMasterKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); // re-enable the sign button so the user can initiate the sign process Button sign = (Button) findViewById(R.id.sign); From 12d6cfefd7f36d90780d0eea6863311dd5c2ae2d Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Mon, 18 Mar 2013 16:07:34 +0000 Subject: [PATCH 10/14] change how empty private cheks happen, speeds things up --- .../sufficientlysecure/keychain/helper/PgpHelper.java | 4 +--- .../keychain/provider/ProviderHelper.java | 10 +++++++++- .../keychain/ui/widget/KeyListAdapter.java | 9 ++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java index 9b8e4cb23..bb5f8eccd 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -164,9 +164,7 @@ public class PgpHelper { for (int i = 0; i < signingKeys.size(); ++i) { PGPSecretKey key = signingKeys.get(i); if (key.isMasterKey()) { - if (!isSecretKeyPrivateEmpty(key)) { - masterKey = key; - } + masterKey = key; } else { usableKeys.add(key); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 36049a64c..c23dcc559 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -370,11 +370,19 @@ public class ProviderHelper { private static ContentProviderOperation buildSecretKeyOperations(Context context, long keyRingRowId, PGPSecretKey key, int rank) throws IOException { ContentValues values = new ContentValues(); + + boolean has_private = true; + if (key.isMasterKey()) { + if (PgpHelper.isSecretKeyPrivateEmpty(key)) { + has_private = false; + } + } + values.put(Keys.KEY_ID, key.getKeyID()); values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm()); values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength()); - values.put(Keys.CAN_SIGN, PgpHelper.isSigningKey(key)); + values.put(Keys.CAN_SIGN, (PgpHelper.isSigningKey(key) && has_private)); values.put(Keys.CAN_ENCRYPT, PgpHelper.isEncryptionKey(key)); values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); values.put(Keys.CREATION, PgpHelper.getCreationDate(key).getTime() / 1000); diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java index 61ca0e9d0..9b5941706 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java @@ -159,13 +159,8 @@ public class KeyListAdapter extends CursorTreeAdapter { } ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); - boolean privateEmpty = false; //Don't show signing icon for master keys without private keys - //TODO: does this need to be done for encrypting icon? Does anyone use master key for encrypt? - if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) == 1) { - privateEmpty = PgpHelper.isSecretKeyPrivateEmpty(context, - cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID))); - } - if (privateEmpty || cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { + + if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { signIcon.setVisibility(View.GONE); } else { signIcon.setVisibility(View.VISIBLE); From 2d856c5f0e4f0ba9e1973ad85fde14ad06a1133c Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Mon, 18 Mar 2013 18:51:24 +0000 Subject: [PATCH 11/14] use db to mark keys which can certify --- .../res/drawable-hdpi/certify_small.png | Bin 0 -> 2091 bytes .../res/drawable-mdpi/certify_small.png | Bin 0 -> 1401 bytes .../res/drawable/certify_small.png | Bin 0 -> 1401 bytes .../res/layout/key_list_child_item.xml | 8 ++++- .../keychain/helper/PgpHelper.java | 30 ++++++++++++++++++ .../keychain/provider/KeychainContract.java | 1 + .../keychain/provider/KeychainDatabase.java | 7 +++- .../keychain/provider/KeychainProvider.java | 1 + .../keychain/provider/ProviderHelper.java | 1 + .../keychain/ui/SelectSecretKeyFragment.java | 2 +- .../keychain/ui/widget/KeyListAdapter.java | 12 +++++-- .../ui/widget/SelectKeyCursorAdapter.java | 2 +- 12 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/certify_small.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/certify_small.png create mode 100644 OpenPGP-Keychain/res/drawable/certify_small.png diff --git a/OpenPGP-Keychain/res/drawable-hdpi/certify_small.png b/OpenPGP-Keychain/res/drawable-hdpi/certify_small.png new file mode 100644 index 0000000000000000000000000000000000000000..9e54464ed6b61eff446eda483649026523284225 GIT binary patch literal 2091 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 Hu0mjfgBF^A literal 0 HcmV?d00001 diff --git a/OpenPGP-Keychain/res/drawable/certify_small.png b/OpenPGP-Keychain/res/drawable/certify_small.png new file mode 100644 index 0000000000000000000000000000000000000000..575b2d8662124cc3e75fb0c2a612c44e412ff21e GIT binary patch literal 1401 zcmV-<1%~>GP)3$g6vxk7=FQUCx=yDHb}C(fK&31~jG-n_i_k)8iY(C(5{;2WBZ}e&KulaBNQ`mA z4}3z97?heA1tAb?(^fW%E$x&7wqxl`J2Rc_&AfT5=MHV9#fIulZtmPOH~-%|%RQHZ zX~ufH-OkC818EFhEGU{UrfQuUP2Hq4pcze@>&nWgVO6Eb9SpU!$=cpw5&l3aZK@nK z9bl7<`|73o+AWMMr;doK3cM%*;A)~N5Mxo$6a_eg0rs71`Sn6v-i*ionh~JZq^oSG zT7FUQ9i)Apf$6>>WzWkmfJ+J4|AE_6h1;$f0iMk?HNCu{uCDpiNuU@8%nm1*($j%u zb)ZQih|ws-10#@#$H8K?!mv40+|$v!0{v@yAYh$|-m!gk^(T?u>ktWtA**~Y81#Ce z6$}*#mn30mWKbj-_+S8{kuYRFTnewXb-X_y!+rz^_d+J+pw`T6Y*@DRShgxLH@myQ zSy2gkmIW4LO)C=61b|b+smKyYxbmS8*fTSrJrIo__V`}(N^leb$~a^Kur7_8|5%Bu zb#Z|s?OJ;)*b5(mjJ$jlmqCYCQ1R!G46NxxPV=>4ta>1AG{@W))!WtCs zz7Md;%$(f0Zq0_)pML^P(;$C-1(-N4C9Xr^6a*)M5P%pBgMc{@u&M+sPR?wBZkd98 z9{<{;0zcdbc*SY+)-77#=xA>P8viYP1jD1LphrOtg*j4^xH6bzWi$YiD5iu3{Fnr} z09XJavmIV{556(1!XY%J>U$zYJF<;8o_=)k>?>n{0!*$E>s^n+dK5OKrUYy_1X@x6 z5ryLjAd^6dnk732-s|&TIun;0ar@^aK)s3H|NM&Od;2@DK~fOF;miZ8#gY;x03%uB zJQ(d)$Kr0tk>Y8X>^y;ZVQ-LbIT}il!@*0Ay2_mgXol)2G+ZEve}4 z>V!SyBJDk4t(lY{ZB zZ!nph6}i@~awD7W8}>m4LxUANoBI;}MZGyWj5>pBAt-jjdkq z$~!Fv{UBn^TR0s!Z8jKHFqv5QnFz8{Dt1^6dibPv&>#D-UxbfQ&@&@IV`C#@J==7- zZkDq`z@F2e6y-DiM%*+7fE9x!! z(y%1$9#lZ^YU-)53|G<2Te4h*xsD}ot$MP4lhtYl3PYx7}GwPQLvn@rvD+Q&72d<*YvUZ@yFCyz_W6 zne3Yy&KY^&Dr;DvYRWEIiu%HV?n|;5Y!w6{kdw2>QB_@ky5*OqbAtmHcakEQQJGFX zR93d#;&va2qMfuIa)~EKX=y|8NU(d^sQ>y2G)}x^;|Kl*+XZSjHg?{M00000NkvXX Hu0mjfgBF^A literal 0 HcmV?d00001 diff --git a/OpenPGP-Keychain/res/layout/key_list_child_item.xml b/OpenPGP-Keychain/res/layout/key_list_child_item.xml index 868989cf6..81d843529 100644 --- a/OpenPGP-Keychain/res/layout/key_list_child_item.xml +++ b/OpenPGP-Keychain/res/layout/key_list_child_item.xml @@ -63,6 +63,12 @@ android:paddingBottom="2dip" android:paddingTop="2dip" > + + - \ No newline at end of file + diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java index bb5f8eccd..0ff957b66 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -333,6 +333,36 @@ public class PgpHelper { return isSigningKey(key.getPublicKey()); } + @SuppressWarnings("unchecked") + public static boolean isCertificationKey(PGPPublicKey key) { + if (key.getVersion() <= 3) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null && (hashed.getKeyFlags() & KeyFlags.CERTIFY_OTHER) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.CERTIFY_OTHER) != 0) { + return true; + } + } + + return false; + } + + public static boolean isCertificationKey(PGPSecretKey key) { + return isCertificationKey(key.getPublicKey()); + } + public static String getAlgorithmInfo(PGPPublicKey key) { return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength()); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java index 3d4dddea5..b284eccaa 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -36,6 +36,7 @@ public class KeychainContract { String IS_MASTER_KEY = "is_master_key"; String ALGORITHM = "algorithm"; String KEY_SIZE = "key_size"; + String CAN_CERTIFY = "can_certify"; String CAN_SIGN = "can_sign"; String CAN_ENCRYPT = "can_encrypt"; String IS_REVOKED = "is_revoked"; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 4a2aeeb80..ef95ce7f7 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -31,7 +31,7 @@ import android.provider.BaseColumns; public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 3; + private static final int DATABASE_VERSION = 4; public interface Tables { String KEY_RINGS = "key_rings"; @@ -48,6 +48,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID + " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE + + " INTEGER, " + KeysColumns.CAN_CERTIFY + " INTEGER, " + KeysColumns.CAN_SIGN + " INTEGER, " + KeysColumns.CAN_ENCRYPT + " INTEGER, " + KeysColumns.IS_REVOKED + " INTEGER, " + KeysColumns.CREATION + " INTEGER, " + KeysColumns.EXPIRY + " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," @@ -93,6 +94,10 @@ public class KeychainDatabase extends SQLiteOpenHelper { Log.w(Constants.TAG, "Upgrading database to version " + version); switch (version) { + case 3: + db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY + " INTEGER DEFAULT 0;"); + db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;"); + break; default: break; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 23f9616e0..c63d9e772 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -367,6 +367,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY); projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM); projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE); + projectionMap.put(KeysColumns.CAN_CERTIFY, KeysColumns.CAN_CERTIFY); projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN); projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT); projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED); diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java index c23dcc559..7190d72c4 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -382,6 +382,7 @@ public class ProviderHelper { values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm()); values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength()); + values.put(Keys.CAN_CERTIFY, (PgpHelper.isCertificationKey(key) && has_private)); values.put(Keys.CAN_SIGN, (PgpHelper.isSigningKey(key) && has_private)); values.put(Keys.CAN_ENCRYPT, PgpHelper.isEncryptionKey(key)); values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java index d7d9fb0af..a3d9167a3 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -89,7 +89,7 @@ public class SelectSecretKeyFragment extends SherlockListFragment implements public Loader onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + Uri baseUri = KeyRings.buildSecretKeyRingsUri(); // These are the rows that we will retrieve. long now = new Date().getTime() / 1000; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java index 9b5941706..75c764540 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java @@ -151,6 +151,13 @@ public class KeyListAdapter extends CursorTreeAdapter { masterKeyIcon.setVisibility(View.VISIBLE); } + ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) { + certifyIcon.setVisibility(View.GONE); + } else { + certifyIcon.setVisibility(View.VISIBLE); + } + ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) { encryptIcon.setVisibility(View.GONE); @@ -159,7 +166,6 @@ public class KeyListAdapter extends CursorTreeAdapter { } ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { signIcon.setVisibility(View.GONE); } else { @@ -213,7 +219,7 @@ public class KeyListAdapter extends CursorTreeAdapter { switch (type) { case CHILD_FINGERPRINT: projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, - Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; + Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; sortOrder = Keys.RANK + " ASC"; // use only master key for fingerprint @@ -228,7 +234,7 @@ public class KeyListAdapter extends CursorTreeAdapter { case CHILD_KEY: projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, - Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; + Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; sortOrder = Keys.RANK + " ASC"; if (mKeyType == Id.type.public_key) { diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java index da8ac94ee..17423136f 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java @@ -139,4 +139,4 @@ public class SelectKeyCursorAdapter extends CursorAdapter { return mInflater.inflate(R.layout.select_key_item, null); } -} \ No newline at end of file +} From 650b22d5e9512ede43b0c742799d49b061dda587 Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Tue, 19 Mar 2013 02:04:56 +0000 Subject: [PATCH 12/14] only sign keys if private key has capability --- .../keychain/ui/SelectSecretKeyActivity.java | 5 +++++ .../keychain/ui/SelectSecretKeyFragment.java | 12 +++++++++++- .../keychain/ui/SignKeyActivity.java | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java index 14ccb17c7..10acd38d9 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java @@ -34,8 +34,11 @@ public class SelectSecretKeyActivity extends SherlockFragmentActivity { public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX + "SELECT_SECRET_KEYRING"; + public static final String EXTRA_FILTER_CERTIFY = "filter_certify"; + public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; public static final String RESULT_EXTRA_USER_ID = "userId"; + public static boolean filterCertify = false; @Override protected void onCreate(Bundle savedInstanceState) { @@ -61,6 +64,8 @@ public class SelectSecretKeyActivity extends SherlockFragmentActivity { // } // }); + filterCertify = getIntent().getBooleanExtra(EXTRA_FILTER_CERTIFY, false); + handleIntent(getIntent()); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java index a3d9167a3..ae3adeed7 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -91,12 +91,22 @@ public class SelectSecretKeyFragment extends SherlockListFragment implements // sample only has one Loader, so we don't care about the ID. Uri baseUri = KeyRings.buildSecretKeyRingsUri(); + String CapFilter = null; + if (((SelectSecretKeyActivity)getActivity()).filterCertify == true) { + CapFilter = "(cert>0)"; + } + + // These are the rows that we will retrieve. long now = new Date().getTime() / 1000; String[] projection = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, UserIds.USER_ID, + "(SELECT COUNT(cert_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS cert_keys WHERE cert_keys." + Keys.KEY_RING_ROW_ID + " = " + + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND cert_keys." + + Keys.CAN_CERTIFY + " = '1') AS cert", "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID @@ -127,7 +137,7 @@ public class SelectSecretKeyFragment extends SherlockListFragment implements // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); + return new CursorLoader(getActivity(), baseUri, projection, CapFilter, null, orderBy); } @Override diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java index 6ded66468..1e7fe0120 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java @@ -121,6 +121,7 @@ public class SignKeyActivity extends SherlockFragmentActivity { // kick off the SecretKey selection activity so the user chooses which key to sign with // first Intent intent = new Intent(this, SelectSecretKeyActivity.class); + intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_CERTIFY, true); startActivityForResult(intent, Id.request.secret_keys); } } From cf34a1720e02ed7bf853e66bd4b451f791dac23b Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 21 Mar 2013 14:18:38 +0000 Subject: [PATCH 13/14] change edit key for empty private master keys --- .../res/layout/edit_key_section.xml | 3 +- .../keychain/helper/PgpMain.java | 25 +++++++++++ .../keychain/provider/ProviderHelper.java | 44 +++++++++++++++++++ .../service/KeychainIntentService.java | 14 +++++- .../keychain/ui/EditKeyActivity.java | 15 +++++++ .../keychain/ui/KeyListSecretActivity.java | 14 +++--- .../keychain/ui/KeyListSecretFragment.java | 4 +- .../keychain/ui/widget/KeyEditor.java | 8 ++++ .../keychain/ui/widget/SectionView.java | 15 +++++++ .../keychain/ui/widget/UserIdEditor.java | 10 +++++ 10 files changed, 143 insertions(+), 9 deletions(-) diff --git a/OpenPGP-Keychain/res/layout/edit_key_section.xml b/OpenPGP-Keychain/res/layout/edit_key_section.xml index 870e1b063..dd0a5d4bd 100644 --- a/OpenPGP-Keychain/res/layout/edit_key_section.xml +++ b/OpenPGP-Keychain/res/layout/edit_key_section.xml @@ -41,6 +41,7 @@ android:text="Section Name" /> - \ No newline at end of file + diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java index f4b7c7bbd..27047cd0f 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java @@ -309,6 +309,31 @@ public class PgpMain { return secKeyRing; } + public static void changeSecretKeyPassphrase(Context context, + PGPSecretKeyRing keyRing, String oldPassPhrase, String newPassPhrase, + ProgressDialogUpdater progress) throws IOException, PGPException, PGPException, + NoSuchProviderException { + + updateProgress(progress, R.string.progress_buildingKey, 0, 100); + if (oldPassPhrase == null) { + oldPassPhrase = ""; + } + if (newPassPhrase == null) { + newPassPhrase = ""; + } + + PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(keyRing, + oldPassPhrase.toCharArray(), newPassPhrase.toCharArray(), keyRing.getSecretKey().getKeyEncryptionAlgorithm(), + new SecureRandom(), BOUNCY_CASTLE_PROVIDER_NAME); + + updateProgress(progress, R.string.progress_savingKeyRing, 50, 100); + + ProviderHelper.saveKeyRing(context, newKeyRing); + + updateProgress(progress, R.string.progress_done, 100, 100); + + } + public static void buildSecretKey(Context context, ArrayList userIds, ArrayList keys, ArrayList keysUsages, long masterKeyId, String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 7190d72c4..2665456ea 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -34,6 +34,7 @@ import org.sufficientlysecure.keychain.helper.PgpMain; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -494,6 +495,49 @@ public class ProviderHelper { return getMasterKeyId(context, queryUri, keyRingRowId); } + /** + * Get empty status of master key of keyring by its row id + * + * @param context + * @param keyRingRowId + * @return + */ + public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); + return getMasterKeyCanSign(context, queryUri, keyRingRowId); + } + + /** + * Private helper method to get master key private empty status of keyring by its row id + * + * @param context + * @param queryUri + * @param keyRingRowId + * @return + */ + private static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) { + String[] projection = new String[] { KeyRings.MASTER_KEY_ID, "(SELECT COUNT(sign_keys." + + Keys._ID + ") FROM " + Tables.KEYS + " AS sign_keys WHERE sign_keys." + Keys.KEY_RING_ROW_ID + " = " + + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND sign_keys." + + Keys.CAN_SIGN + " = '1' AND " + Keys.IS_MASTER_KEY + " = 1) AS sign", }; + + ContentResolver cr = context.getContentResolver(); + Cursor cursor = cr.query(queryUri, projection, null, null, null); + + long masterKeyId = -1; + if (cursor != null && cursor.moveToFirst()) { + int masterKeyIdCol = cursor.getColumnIndex("sign"); + + masterKeyId = cursor.getLong(masterKeyIdCol); + } + + if (cursor != null) { + cursor.close(); + } + + return (masterKeyId > 0); + } + /** * Get master key id of keyring by its row id * diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java index e525fe96e..48afdff5c 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -123,6 +123,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial public static final String SAVE_KEYRING_KEYS = "keys"; public static final String SAVE_KEYRING_KEYS_USAGES = "keysUsages"; public static final String SAVE_KEYRING_MASTER_KEY_ID = "masterKeyId"; + public static final String SAVE_KEYRING_CAN_SIGN = "can_sign"; // generate key public static final String GENERATE_KEY_ALGORITHM = "algorithm"; @@ -523,6 +524,12 @@ public class KeychainIntentService extends IntentService implements ProgressDial /* Input */ String oldPassPhrase = data.getString(SAVE_KEYRING_CURRENT_PASSPHRASE); String newPassPhrase = data.getString(SAVE_KEYRING_NEW_PASSPHRASE); + boolean canSign = true; + + if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) { + canSign = data.getBoolean(SAVE_KEYRING_CAN_SIGN); + } + if (newPassPhrase == null) { newPassPhrase = oldPassPhrase; } @@ -533,8 +540,13 @@ public class KeychainIntentService extends IntentService implements ProgressDial long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); /* Operation */ - PgpMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, + if (!canSign) { //library fails, fix later + //PgpMain.changeSecretKeyPassphrase(this, ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), + //oldPassPhrase, newPassPhrase, this); + } else { + PgpMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, newPassPhrase, this); + } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); /* Output */ diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 90ee9838e..3d819a824 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -75,6 +75,7 @@ public class EditKeyActivity extends SherlockFragmentActivity { public static final String EXTRA_NO_PASSPHRASE = "noPassphrase"; public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generateDefaultKeys"; public static final String EXTRA_MASTER_KEY_ID = "masterKeyId"; + public static final String EXTRA_MASTER_CAN_SIGN = "masterCanSign"; // results when saving key public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; @@ -97,6 +98,7 @@ public class EditKeyActivity extends SherlockFragmentActivity { Vector mUserIds; Vector mKeys; Vector mKeysUsages; + boolean masterCanSign = true; // will be set to false to build layout later in handler private boolean mBuildLayout = true; @@ -192,6 +194,13 @@ public class EditKeyActivity extends SherlockFragmentActivity { } }); + //disable key passhphrase changing with empty private keys for no + //library fails, fix later + if (!masterCanSign) { + mChangePassPhrase.setEnabled(false); + mNoPassphrase.setEnabled(false); + } + if (mBuildLayout) { buildLayout(); } @@ -317,6 +326,9 @@ public class EditKeyActivity extends SherlockFragmentActivity { } if (extras != null) { + if (extras.containsKey(EXTRA_MASTER_CAN_SIGN)) { + masterCanSign = extras.getBoolean(EXTRA_MASTER_CAN_SIGN); + } if (extras.containsKey(EXTRA_MASTER_KEY_ID)) { long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID); @@ -394,10 +406,12 @@ public class EditKeyActivity extends SherlockFragmentActivity { LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mUserIdsView.setType(Id.type.user_id); + mUserIdsView.setCanEdit(masterCanSign); mUserIdsView.setUserIds(mUserIds); container.addView(mUserIdsView); mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mKeysView.setType(Id.type.key); + mKeysView.setCanEdit(masterCanSign); mKeysView.setKeys(mKeys, mKeysUsages); container.addView(mKeysView); @@ -447,6 +461,7 @@ public class EditKeyActivity extends SherlockFragmentActivity { data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES, getKeysUsages(mKeysView)); data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); + data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java index 0e6cdd6a1..52ce19f97 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java @@ -70,18 +70,19 @@ public class KeyListSecretActivity extends KeyListActivity { } } - public void checkPassPhraseAndEdit(long masterKeyId) { + public void checkPassPhraseAndEdit(long masterKeyId, boolean masterCanSign) { String passPhrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); if (passPhrase == null) { - showPassphraseDialog(masterKeyId); + showPassphraseDialog(masterKeyId, masterCanSign); } else { PgpMain.setEditPassPhrase(passPhrase); - editKey(masterKeyId); + editKey(masterKeyId, masterCanSign); } } - private void showPassphraseDialog(final long masterKeyId) { + private void showPassphraseDialog(final long masterKeyId, boolean masterCanSign) { // Message is received after passphrase is cached + final boolean mCanSign = masterCanSign; Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { @@ -89,7 +90,7 @@ public class KeyListSecretActivity extends KeyListActivity { String passPhrase = PassphraseCacheService.getCachedPassphrase( KeyListSecretActivity.this, masterKeyId); PgpMain.setEditPassPhrase(passPhrase); - editKey(masterKeyId); + editKey(masterKeyId, mCanSign); } } }; @@ -115,9 +116,10 @@ public class KeyListSecretActivity extends KeyListActivity { startActivityForResult(intent, 0); } - private void editKey(long masterKeyId) { + private void editKey(long masterKeyId, boolean masterCanSign) { Intent intent = new Intent(EditKeyActivity.ACTION_EDIT_KEY); intent.putExtra(EditKeyActivity.EXTRA_MASTER_KEY_ID, masterKeyId); + intent.putExtra(EditKeyActivity.EXTRA_MASTER_CAN_SIGN, masterCanSign); startActivityForResult(intent, 0); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java index f4ba2b4d9..e41755840 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java @@ -91,9 +91,11 @@ public class KeyListSecretFragment extends KeyListFragment implements long masterKeyId = ProviderHelper .getSecretMasterKeyId(mKeyListSecretActivity, keyRingRowId); + boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(mKeyListSecretActivity, keyRingRowId); + switch (item.getItemId()) { case Id.menu.edit: - mKeyListSecretActivity.checkPassPhraseAndEdit(masterKeyId); + mKeyListSecretActivity.checkPassPhraseAndEdit(masterKeyId, masterCanSign); return true; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 1122fc522..e9a5674cd 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -125,6 +125,14 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { super.onFinishInflate(); } + public void setCanEdit(boolean bCanEdit) { + if (!bCanEdit) { + mDeleteButton.setVisibility(View.INVISIBLE); + mUsage.setEnabled(false); + mExpiryDateButton.setEnabled(false); + } + } + public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) { mKey = key; diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 7142e4426..898b05372 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -45,6 +45,7 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.EditText; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; @@ -55,12 +56,14 @@ import java.util.Vector; public class SectionView extends LinearLayout implements OnClickListener, EditorListener { private LayoutInflater mInflater; private View mAdd; + private ImageView mPlusButton; private ViewGroup mEditors; private TextView mTitle; private int mType = 0; private Choice mNewKeyAlgorithmChoice; private int mNewKeySize; + private boolean canEdit = true; private SherlockFragmentActivity mActivity; @@ -99,6 +102,14 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } } + public void setCanEdit(boolean bCanEdit) { + canEdit = bCanEdit; + mPlusButton = (ImageView)findViewById(R.id.plusbutton); + if (!canEdit) { + mPlusButton.setVisibility(View.INVISIBLE); + } + } + /** {@inheritDoc} */ @Override protected void onFinishInflate() { @@ -129,6 +140,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor /** {@inheritDoc} */ public void onClick(View v) { + if (canEdit) { switch (mType) { case Id.type.user_id: { UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, @@ -205,6 +217,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } } this.updateEditorsVisible(); + } } public void setUserIds(Vector list) { @@ -221,6 +234,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor if (mEditors.getChildCount() == 0) { view.setIsMainUserId(true); } + view.setCanEdit(canEdit); mEditors.addView(view); } @@ -241,6 +255,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor view.setEditorListener(this); boolean isMasterKey = (mEditors.getChildCount() == 0); view.setValue(list.get(i), isMasterKey, usages.get(i)); + view.setCanEdit(canEdit); mEditors.addView(view); } diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 2215d29e7..5b43ebbd9 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -56,6 +56,16 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene } } + public void setCanEdit(boolean bCanEdit) { + if (!bCanEdit) { + mDeleteButton.setVisibility(View.INVISIBLE); + mName.setEnabled(false); + mIsMainUserId.setEnabled(false); + mEmail.setEnabled(false); + mComment.setEnabled(false); + } + } + public static class NoEmailException extends Exception { static final long serialVersionUID = 0xf812773344L; From 0075c522a6773c14e34875cc7118055cde2e13be Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Thu, 21 Mar 2013 14:48:30 +0000 Subject: [PATCH 14/14] use certification keys to sign keyrings --- .../keychain/helper/PgpHelper.java | 44 +++++++++++++++++++ .../keychain/helper/PgpMain.java | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java index 0ff957b66..810f3a6f2 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -122,6 +122,19 @@ public class PgpHelper { return signingKeys; } + @SuppressWarnings("unchecked") + public static Vector getCertificationKeys(PGPSecretKeyRing keyRing) { + Vector signingKeys = new Vector(); + + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + if (isCertificationKey(key)) { + signingKeys.add(key); + } + } + + return signingKeys; + } + public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) { Vector usableKeys = new Vector(); Vector encryptKeys = getEncryptKeys(keyRing); @@ -157,6 +170,24 @@ public class PgpHelper { return isExpired(key.getPublicKey()); } + public static Vector getUsableCertificationKeys(PGPSecretKeyRing keyRing) { + Vector usableKeys = new Vector(); + Vector signingKeys = getCertificationKeys(keyRing); + PGPSecretKey masterKey = null; + for (int i = 0; i < signingKeys.size(); ++i) { + PGPSecretKey key = signingKeys.get(i); + if (key.isMasterKey()) { + masterKey = key; + } else { + usableKeys.add(key); + } + } + if (masterKey != null) { + usableKeys.add(masterKey); + } + return usableKeys; + } + public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) { Vector usableKeys = new Vector(); Vector signingKeys = getSigningKeys(keyRing); @@ -208,6 +239,19 @@ public class PgpHelper { return encryptKeys.get(0); } + public static PGPSecretKey getCertificationKey(Context context, long masterKeyId) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, + masterKeyId); + if (keyRing == null) { + return null; + } + Vector signingKeys = getUsableCertificationKeys(keyRing); + if (signingKeys.size() == 0) { + return null; + } + return signingKeys.get(0); + } + public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, masterKeyId); diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java index 27047cd0f..142dfec01 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java @@ -1132,7 +1132,7 @@ public class PgpMain { } else { PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); - PGPSecretKey signingKey = PgpHelper.getSigningKey(context, masterKeyId); + PGPSecretKey signingKey = PgpHelper.getCertificationKey(context, masterKeyId); if (signingKey == null) { throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); }