From e0905a3afbba7f96822becb378b7e8fd9c51e85a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 2 Sep 2014 01:24:16 +0200 Subject: cache key s2k type in database, for later use --- .../keychain/pgp/CanonicalizedSecretKey.java | 53 ++++++++++++++++++++++ .../keychain/pgp/CanonicalizedSecretKeyRing.java | 13 ------ .../keychain/provider/ProviderHelper.java | 45 ++++++++++++------ .../keychain/service/OperationResultParcel.java | 2 + .../keychain/ui/SelectSecretKeyFragment.java | 3 +- .../keychain/ui/ViewKeyActivity.java | 2 +- .../keychain/ui/adapter/SubkeysAdapter.java | 23 +++++++--- OpenKeychain/src/main/res/values/strings.xml | 11 +++-- 8 files changed, 113 insertions(+), 39 deletions(-) (limited to 'OpenKeychain/src') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index 51770f930..833e1ad3d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -80,6 +80,59 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { return (CanonicalizedSecretKeyRing) mRing; } + public enum SecretKeyType { + UNAVAILABLE(0), GNU_DUMMY (1), PASSPHRASE (2), PASSPHRASE_EMPTY (3), DIVERT_TO_CARD (4); + + final int mNum; + SecretKeyType(int num) { + mNum = num; + } + + public static SecretKeyType fromNum(int num) { + switch (num) { + case 1: return GNU_DUMMY; + case 2: return PASSPHRASE; + case 3: return PASSPHRASE_EMPTY; + case 4: return DIVERT_TO_CARD; + // if this case happens, it's probably a check from a database value + default: return UNAVAILABLE; + } + } + + public int getNum() { + return mNum; + } + + public boolean isUsable() { + return this != UNAVAILABLE && this != GNU_DUMMY; + } + + } + + public SecretKeyType getSecretKeyType() { + if (mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K) { + // divert to card is special + if (mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) { + return SecretKeyType.DIVERT_TO_CARD; + } + // no matter the exact protection mode, it's some kind of dummy key + return SecretKeyType.GNU_DUMMY; + } + + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); + // If this doesn't throw + mSecretKey.extractPrivateKey(keyDecryptor); + // It means the passphrase is empty + return SecretKeyType.PASSPHRASE_EMPTY; + } catch (PGPException e) { + // Otherwise, it's just a regular ol' passphrase + return SecretKeyType.PASSPHRASE; + } + + } + /** * Returns true on right passphrase */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java index d8b873d31..48a2aaeb6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java @@ -74,19 +74,6 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing { return new CanonicalizedSecretKey(this, mRing.getSecretKey(id)); } - public HashSet getAvailableSubkeys() { - HashSet result = new HashSet(); - // then, mark exactly the keys we have available - for (PGPSecretKey sub : new IterableIterator(getRing().getSecretKeys())) { - S2K s2k = sub.getS2K(); - // add key, except if the private key has been stripped (GNU extension) - if(s2k == null || (s2k.getProtectionMode() != S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY)) { - result.add(sub.getKeyID()); - } - } - return result; - } - /** Getter that returns the subkey that should be used for signing. */ CanonicalizedSecretKey getSigningSubKey() throws PgpGeneralException { PGPSecretKey key = mRing.getSecretKey(getSignId()); 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 4fde8e581..e1dae55ad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -34,6 +34,8 @@ import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.PgpHelper; @@ -592,27 +594,40 @@ public class ProviderHelper { 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 log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); mIndent += 1; - Set available = keyRing.getAvailableSubkeys(); - for (UncachedPublicKey sub : keyRing.publicKeyIterator()) { + for (CanonicalizedSecretKey sub : keyRing.secretKeyIterator()) { long id = sub.getKeyId(); - if (available.contains(id)) { - int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", - new String[]{Long.toString(id)}); - if (upd == 1) { - log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, - PgpKeyHelper.convertKeyIdToHex(id) - ); - } else { - log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, - PgpKeyHelper.convertKeyIdToHex(id) - ); + SecretKeyType mode = sub.getSecretKeyType(); + values.put(Keys.HAS_SECRET, mode.getNum()); + int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", + new String[]{ Long.toString(id) }); + if (upd == 1) { + switch (mode) { + case PASSPHRASE: + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, + PgpKeyHelper.convertKeyIdToHex(id) + ); + break; + case PASSPHRASE_EMPTY: + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_EMPTY, + PgpKeyHelper.convertKeyIdToHex(id) + ); + break; + case GNU_DUMMY: + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_STRIPPED, + PgpKeyHelper.convertKeyIdToHex(id) + ); + break; + case DIVERT_TO_CARD: + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_DIVERT, + PgpKeyHelper.convertKeyIdToHex(id) + ); + break; } } else { - log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, + log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, PgpKeyHelper.convertKeyIdToHex(id) ); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 4e89d0243..438443644 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -287,6 +287,8 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), + MSG_IS_SUBKEY_DIVERT (R.string.msg_is_subkey_divert), + MSG_IS_SUBKEY_EMPTY (R.string.msg_is_subkey_empty), MSG_IS_SUCCESS_IDENTICAL (R.string.msg_is_success_identical), MSG_IS_SUCCESS (R.string.msg_is_success), 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 9ddc8e3e1..3f1053f69 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -32,6 +32,7 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.ui.adapter.SelectKeyCursorAdapter; @@ -190,7 +191,7 @@ public class SelectSecretKeyFragment extends ListFragment implements if(mFilterCertify) { // Only enable if can certify if (cursor.getInt(mIndexCanCertify) == 0 - || cursor.getInt(mIndexHasSecret) == 0) { + || !SecretKeyType.fromNum(cursor.getInt(mIndexHasSecret)).isUsable()) { h.status.setText(R.string.can_certify_not); } else { h.status.setText(R.string.can_certify); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 08300ff04..5d8e13349 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -300,7 +300,7 @@ public class ViewKeyActivity extends ActionBarActivity implements exportHelper.showExportKeysDialog( new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, - Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) == 1) + Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) ); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java index 489cbcefb..7dd61117c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java @@ -31,6 +31,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; @@ -113,7 +114,8 @@ public class SubkeysAdapter extends CursorAdapter { hasAnySecret = false; if (newCursor != null && newCursor.moveToFirst()) { do { - if (newCursor.getInt(INDEX_HAS_SECRET) != 0) { + SecretKeyType hasSecret = SecretKeyType.fromNum(newCursor.getInt(INDEX_HAS_SECRET)); + if (hasSecret.isUsable()) { hasAnySecret = true; break; } @@ -149,12 +151,21 @@ public class SubkeysAdapter extends CursorAdapter { vKeyId.setText(keyIdStr); // may be set with additional "stripped" later on - if (hasAnySecret && cursor.getInt(INDEX_HAS_SECRET) == 0) { - vKeyDetails.setText(algorithmStr + ", " + - context.getString(R.string.key_stripped)); - } else { - vKeyDetails.setText(algorithmStr); + switch (SecretKeyType.fromNum(cursor.getInt(INDEX_HAS_SECRET))) { + case GNU_DUMMY: + algorithmStr += ", " + context.getString(R.string.key_stripped); + break; + case DIVERT_TO_CARD: + algorithmStr += ", " + context.getString(R.string.key_divert); + break; + case PASSPHRASE_EMPTY: + algorithmStr += ", " + context.getString(R.string.key_no_passphrase); + break; + case UNAVAILABLE: + algorithmStr += ", " + context.getString(R.string.key_unavailable); + break; } + vKeyDetails.setText(algorithmStr); boolean isMasterKey = cursor.getInt(INDEX_RANK) == 0; if (isMasterKey) { diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 5b1a44fb6..7775bc259 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -589,9 +589,11 @@ Processing secret subkeys Error encoding keyring Generating public keyring from secret keyring - Subkey %s unavailable in public key - Marked %s as available - Marked %s as stripped + Subkey %s unavailable in secret key + Marked secret subkey %s as available + Marked secret subkey %s as available, with empty passphrase + Marked secret subkey %s as stripped + Marked secret subkey %s as "divert to smartcard/nfc" Keyring contains no new data, nothing to do Successfully imported secret keyring @@ -765,6 +767,9 @@ Key not found! Error processing key! stripped + divert to card + no passphrase + unavailable Your own keys can only be deleted individually! View Certificate Details unknown -- cgit v1.2.3