cache key s2k type in database, for later use

This commit is contained in:
Vincent Breitmoser 2014-09-02 01:24:16 +02:00
parent a97ebc1ec9
commit e0905a3afb
8 changed files with 113 additions and 39 deletions

View File

@ -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
*/

View File

@ -74,19 +74,6 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
return new CanonicalizedSecretKey(this, mRing.getSecretKey(id));
}
public HashSet<Long> getAvailableSubkeys() {
HashSet<Long> result = new HashSet<Long>();
// then, mark exactly the keys we have available
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -589,9 +589,11 @@
<string name="msg_is_importing_subkeys">Processing secret subkeys</string>
<string name="msg_is_io_exc">Error encoding keyring</string>
<string name="msg_is_pubring_generate">Generating public keyring from secret keyring</string>
<string name="msg_is_subkey_nonexistent">Subkey %s unavailable in public key</string>
<string name="msg_is_subkey_ok">Marked %s as available</string>
<string name="msg_is_subkey_stripped">Marked %s as stripped</string>
<string name="msg_is_subkey_nonexistent">Subkey %s unavailable in secret key</string>
<string name="msg_is_subkey_ok">Marked secret subkey %s as available</string>
<string name="msg_is_subkey_empty">Marked secret subkey %s as available, with empty passphrase</string>
<string name="msg_is_subkey_stripped">Marked secret subkey %s as stripped</string>
<string name="msg_is_subkey_divert">Marked secret subkey %s as "divert to smartcard/nfc"</string>
<string name="msg_is_success_identical">Keyring contains no new data, nothing to do</string>
<string name="msg_is_success">Successfully imported secret keyring</string>
@ -765,6 +767,9 @@
<string name="error_key_not_found">Key not found!</string>
<string name="error_key_processing">Error processing key!</string>
<string name="key_stripped">stripped</string>
<string name="key_divert">divert to card</string>
<string name="key_no_passphrase">no passphrase</string>
<string name="key_unavailable">unavailable</string>
<string name="secret_cannot_multiple">Your own keys can only be deleted individually!</string>
<string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string>