Add experimental support for yubikey PINs, remove caching of empty passphrases

This commit is contained in:
Dominik Schürmann 2014-09-05 20:04:07 +02:00
parent fa9bbdd60c
commit 6e3973e26a
3 changed files with 34 additions and 15 deletions

View File

@ -213,15 +213,13 @@ public class PassphraseCacheService extends Service {
SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId); SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId);
switch (keyType) { switch (keyType) {
// TODO: HACK for yubikeys
case DIVERT_TO_CARD: case DIVERT_TO_CARD:
return "123456"; if (Preferences.getPreferences(this).useDefaultYubikeyPin()) {
case PASSPHRASE_EMPTY: return "123456"; // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/
try { } else {
addCachedPassphrase(this, subKeyId, "", keyRing.getPrimaryUserIdWithFallback()); break;
} catch (PgpGeneralException e) {
Log.d(Constants.TAG, "PgpGeneralException occured");
} }
case PASSPHRASE_EMPTY:
return ""; return "";
case UNAVAILABLE: case UNAVAILABLE:
throw new NotFoundException("secret key for this subkey is not available"); throw new NotFoundException("secret key for this subkey is not available");

View File

@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@ -116,7 +117,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity(); final Activity activity = getActivity();
final long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); final long subKeyId = getArguments().getLong(ARG_SECRET_KEY_ID);
mMessenger = getArguments().getParcelable(ARG_MESSENGER); mMessenger = getArguments().getParcelable(ARG_MESSENGER);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
@ -126,14 +127,15 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
final CanonicalizedSecretKeyRing secretRing; final CanonicalizedSecretKeyRing secretRing;
String userId; String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) { if (subKeyId == Constants.key.symmetric || subKeyId == Constants.key.none) {
alert.setMessage(R.string.passphrase_for_symmetric_encryption); alert.setMessage(R.string.passphrase_for_symmetric_encryption);
secretRing = null; secretRing = null;
} else { } else {
String message;
try { try {
ProviderHelper helper = new ProviderHelper(activity); ProviderHelper helper = new ProviderHelper(activity);
secretRing = helper.getCanonicalizedSecretKeyRing( secretRing = helper.getCanonicalizedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(secretKeyId)); KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
// yes the inner try/catch block is necessary, otherwise the final variable // yes the inner try/catch block is necessary, otherwise the final variable
// above can't be statically verified to have been set in all cases because // above can't be statically verified to have been set in all cases because
// the catch clause doesn't return. // the catch clause doesn't return.
@ -142,9 +144,28 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
userId = null; userId = null;
} }
/* Get key type for message */
// find a master key id for our key
long masterKeyId = new ProviderHelper(getActivity()).getMasterKeyId(subKeyId);
CachedPublicKeyRing keyRing = new ProviderHelper(getActivity()).getCachedPublicKeyRing(masterKeyId);
// get the type of key (from the database)
CanonicalizedSecretKey.SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId);
switch (keyType) {
case PASSPHRASE:
message = getString(R.string.passphrase_for, userId);
break;
case DIVERT_TO_CARD:
message = getString(R.string.yubikey_pin);
break;
default:
message = "This should not happen!";
break;
}
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
alert.setTitle(R.string.title_key_not_found); alert.setTitle(R.string.title_key_not_found);
alert.setMessage(getString(R.string.key_not_found, secretKeyId)); alert.setMessage(getString(R.string.key_not_found, subKeyId));
alert.setPositiveButton(android.R.string.ok, new OnClickListener() { alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
dismiss(); dismiss();
@ -154,8 +175,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return alert.create(); return alert.create();
} }
Log.d(Constants.TAG, "User id: '" + userId + "'"); alert.setMessage(message);
alert.setMessage(getString(R.string.passphrase_for, userId));
} }
LayoutInflater inflater = activity.getLayoutInflater(); LayoutInflater inflater = activity.getLayoutInflater();
@ -186,7 +206,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
try { try {
// make sure this unlocks // make sure this unlocks
// TODO this is a very costly operation, we should not be doing this here! // TODO this is a very costly operation, we should not be doing this here!
secretRing.getSecretKey(secretKeyId).unlock(passphrase); secretRing.getSecretKey(subKeyId).unlock(passphrase);
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
Toast.makeText(activity, R.string.error_could_not_extract_private_key, Toast.makeText(activity, R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
@ -199,7 +219,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
try { try {
PassphraseCacheService.addCachedPassphrase(activity, secretKeyId, passphrase, PassphraseCacheService.addCachedPassphrase(activity, subKeyId, passphrase,
secretRing.getPrimaryUserIdWithFallback()); secretRing.getPrimaryUserIdWithFallback());
} catch (PgpGeneralException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "adding of a passphrase failed", e); Log.e(Constants.TAG, "adding of a passphrase failed", e);

View File

@ -180,6 +180,7 @@
<string name="passphrase_must_not_be_empty">Please enter a passphrase.</string> <string name="passphrase_must_not_be_empty">Please enter a passphrase.</string>
<string name="passphrase_for_symmetric_encryption">Symmetric encryption.</string> <string name="passphrase_for_symmetric_encryption">Symmetric encryption.</string>
<string name="passphrase_for">Enter passphrase for \'%s\'</string> <string name="passphrase_for">Enter passphrase for \'%s\'</string>
<string name="yubikey_pin">Enter PIN to access Yubikey for \'%s\'</string>
<string name="file_delete_confirmation">"Are you sure you want to delete\n%s?"</string> <string name="file_delete_confirmation">"Are you sure you want to delete\n%s?"</string>
<string name="file_delete_successful">Successfully deleted.</string> <string name="file_delete_successful">Successfully deleted.</string>
<string name="no_file_selected">Select a file first.</string> <string name="no_file_selected">Select a file first.</string>