diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
5 files changed, 246 insertions, 20 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index c967a5abc..af09cf235 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -250,8 +250,21 @@ public class UncachedKeyRing { * - Remove all non-verifying self-certificates * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" - * - Remove all certificates which are superseded by a newer one on the same target, - * including revocations with later re-certifications. + * - For UID certificates, remove all certificates which are + * superseded by a newer one on the same target, including + * revocations with later re-certifications. + * - For subkey certifications, remove all certificates which + * are superseded by a newer one on the same target, unless + * it encounters a revocation certificate. The revocation + * certificate is considered to permanently revoke the key, + * even if contains later re-certifications. + * This is the "behavior in practice" used by (e.g.) GnuPG, and + * the rationale for both can be found as comments in the GnuPG + * source. + * UID signatures: + * https://github.com/mtigas/gnupg/blob/50c98c7ed6b542857ee2f902eca36cda37407737/g10/getkey.c#L1668-L1674 + * Subkey signatures: + * https://github.com/mtigas/gnupg/blob/50c98c7ed6b542857ee2f902eca36cda37407737/g10/getkey.c#L1990-L1997 * - Remove all certificates in other positions if not of known type: * - key revocation signatures on the master key * - subkey binding signatures for subkeys @@ -278,8 +291,21 @@ public class UncachedKeyRing { * - Remove all non-verifying self-certificates * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" - * - Remove all certificates which are superseded by a newer one on the same target, - * including revocations with later re-certifications. + * - For UID certificates, remove all certificates which are + * superseded by a newer one on the same target, including + * revocations with later re-certifications. + * - For subkey certifications, remove all certificates which + * are superseded by a newer one on the same target, unless + * it encounters a revocation certificate. The revocation + * certificate is considered to permanently revoke the key, + * even if contains later re-certifications. + * This is the "behavior in practice" used by (e.g.) GnuPG, and + * the rationale for both can be found as comments in the GnuPG + * source. + * UID signatures: + * https://github.com/mtigas/gnupg/blob/50c98c7ed6b542857ee2f902eca36cda37407737/g10/getkey.c#L1668-L1674 + * Subkey signatures: + * https://github.com/mtigas/gnupg/blob/50c98c7ed6b542857ee2f902eca36cda37407737/g10/getkey.c#L1990-L1997 * - Remove all certificates in other positions if not of known type: * - key revocation signatures on the master key * - subkey binding signatures for subkeys @@ -950,12 +976,6 @@ public class UncachedKeyRing { } selfCert = zert; - // if this is newer than a possibly existing revocation, drop that one - if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { - log.add(LogType.MSG_KC_SUB_REVOKE_DUP, indent); - redundantCerts += 1; - revocation = null; - } // it must be a revocation, then (we made sure above) } else { @@ -974,8 +994,9 @@ public class UncachedKeyRing { continue; } - // if there is a certification that is newer than this revocation, don't bother - if (selfCert != null && selfCert.getCreationTime().after(cert.getCreationTime())) { + // If we already have a newer revocation cert, skip this one. + if (revocation != null && + revocation.getCreationTime().after(cert.getCreationTime())) { log.add(LogType.MSG_KC_SUB_REVOKE_DUP, indent); redundantCerts += 1; continue; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 15c83d4dc..c3a122388 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -539,13 +539,26 @@ public class OpenPgpService extends Service { result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); - if (signatureResult.getResult() == OpenPgpSignatureResult.RESULT_KEY_MISSING) { - // If signature is unknown we return an _additional_ PendingIntent - // to retrieve the missing key - result.putExtra(OpenPgpApi.RESULT_INTENT, getKeyserverPendingIntent(data, signatureResult.getKeyId())); - } else { - // If signature key is known, return PendingIntent to show key - result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId())); + switch (signatureResult.getResult()) { + case OpenPgpSignatureResult.RESULT_KEY_MISSING: { + // If signature key is missing we return a PendingIntent to retrieve the key + result.putExtra(OpenPgpApi.RESULT_INTENT, getKeyserverPendingIntent(data, signatureResult.getKeyId())); + break; + } + case OpenPgpSignatureResult.RESULT_VALID_CONFIRMED: + case OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED: + case OpenPgpSignatureResult.RESULT_INVALID_KEY_REVOKED: + case OpenPgpSignatureResult.RESULT_INVALID_KEY_EXPIRED: + case OpenPgpSignatureResult.RESULT_INVALID_INSECURE: { + // If signature key is known, return PendingIntent to show key + result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId())); + break; + } + default: + case OpenPgpSignatureResult.RESULT_NO_SIGNATURE: + case OpenPgpSignatureResult.RESULT_INVALID_SIGNATURE: { + // no key id -> no PendingIntent + } } if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 5) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java index 1a04bcf43..52b439a0d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseSecurityTokenNfcActivity.java @@ -29,6 +29,7 @@ import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.TagLostException; @@ -36,6 +37,7 @@ import android.nfc.tech.IsoDep; import android.os.AsyncTask; import android.os.Bundle; +import nordpol.Apdu; import nordpol.android.TagDispatcher; import nordpol.android.AndroidCard; import nordpol.android.OnDiscoveredTagListener; @@ -59,6 +61,8 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.CreateKeyActivity; import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.ViewKeyActivity; +import org.sufficientlysecure.keychain.ui.dialog.FidesmoInstallDialog; +import org.sufficientlysecure.keychain.ui.dialog.FidesmoPgpInstallDialog; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; @@ -71,6 +75,10 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen public static final String EXTRA_TAG_HANDLING_ENABLED = "tag_handling_enabled"; + // Fidesmo constants + private static final String FIDESMO_APPS_AID_PREFIX = "A000000617"; + private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android"; + protected Passphrase mPin; protected Passphrase mAdminPin; protected boolean mPw1ValidForMultipleSignatures; @@ -309,6 +317,20 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen onNfcError(getString(R.string.security_token_error_unknown)); break; } + // 6A82 app not installed on security token! + case 0x6A82: { + if (isFidesmoDevice()) { + // Check if the Fidesmo app is installed + if (isAndroidAppInstalled(FIDESMO_APP_PACKAGE)) { + promptFidesmoPgpInstall(); + } else { + promptFidesmoAppInstall(); + } + } else { // Other (possibly) compatible hardware + onNfcError(getString(R.string.security_token_error_pgp_app_not_installed)); + } + break; + } default: { onNfcError(getString(R.string.security_token_error, e.getMessage())); break; @@ -372,7 +394,6 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen mPin = input.getPassphrase(); break; } - default: super.onActivityResult(requestCode, resultCode, data); } @@ -984,4 +1005,53 @@ public abstract class BaseSecurityTokenNfcActivity extends BaseActivity implemen } + private boolean isFidesmoDevice() { + if (isNfcConnected()) { // Check if we can still talk to the card + try { + // By trying to select any apps that have the Fidesmo AID prefix we can + // see if it is a Fidesmo device or not + byte[] mSelectResponse = mIsoCard.transceive(Apdu.select(FIDESMO_APPS_AID_PREFIX)); + // Compare the status returned by our select with the OK status code + return Apdu.hasStatus(mSelectResponse, Apdu.OK_APDU); + } catch (IOException e) { + Log.e(Constants.TAG, "Card communication failed!", e); + } + } + return false; + } + + /** + * Ask user if she wants to install PGP onto her Fidesmo device + */ + private void promptFidesmoPgpInstall() { + FidesmoPgpInstallDialog mFidesmoPgpInstallDialog = new FidesmoPgpInstallDialog(); + mFidesmoPgpInstallDialog.show(getSupportFragmentManager(), "mFidesmoPgpInstallDialog"); + } + + /** + * Show a Dialog to the user informing that Fidesmo App must be installed and with option + * to launch the Google Play store. + */ + private void promptFidesmoAppInstall() { + FidesmoInstallDialog mFidesmoInstallDialog = new FidesmoInstallDialog(); + mFidesmoInstallDialog.show(getSupportFragmentManager(), "mFidesmoInstallDialog"); + } + + /** + * Use the package manager to detect if an application is installed on the phone + * @param uri an URI identifying the application's package + * @return 'true' if the app is installed + */ + private boolean isAndroidAppInstalled(String uri) { + PackageManager mPackageManager = getPackageManager(); + boolean mAppInstalled = false; + try { + mPackageManager.getPackageInfo(uri, PackageManager.GET_ACTIVITIES); + mAppInstalled = true; + } catch (PackageManager.NameNotFoundException e) { + Log.e(Constants.TAG, "App not installed on Android device"); + mAppInstalled = false; + } + return mAppInstalled; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FidesmoInstallDialog.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FidesmoInstallDialog.java new file mode 100644 index 000000000..76934c5d4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FidesmoInstallDialog.java @@ -0,0 +1,59 @@ +package org.sufficientlysecure.keychain.ui.dialog; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; + +import org.sufficientlysecure.keychain.R; + +public class FidesmoInstallDialog extends DialogFragment { + + // URLs for Google Play app and to install apps via browser + private final static String PLAY_STORE_URI = "market://details?id="; + private final static String PLAY_STORE_VIA_BROWSER_URI = "http://play.google.com/store/apps/details?id="; + + // Fidesmo constants + private static final String FIDESMO_APP_PACKAGE = "com.fidesmo.sec.android"; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + CustomAlertDialogBuilder mCustomAlertDialogBuilder = new CustomAlertDialogBuilder(getActivity()); + mCustomAlertDialogBuilder.setTitle(getString(R.string.prompt_fidesmo_app_install_title)); + mCustomAlertDialogBuilder.setMessage(getString(R.string.prompt_fidesmo_app_install_message)); + mCustomAlertDialogBuilder.setPositiveButton( + getString(R.string.prompt_fidesmo_app_install_button_positive), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + startPlayStoreFidesmoAppActivity(); + } + }); + mCustomAlertDialogBuilder.setNegativeButton( + getString(R.string.prompt_fidesmo_app_install_button_negative), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }); + + return mCustomAlertDialogBuilder.show(); + } + + private void startPlayStoreFidesmoAppActivity() { + try { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(PLAY_STORE_URI + + FIDESMO_APP_PACKAGE))); + } catch (android.content.ActivityNotFoundException exception) { + // if the Google Play app is not installed, call the browser + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(PLAY_STORE_VIA_BROWSER_URI + + FIDESMO_APP_PACKAGE))); + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FidesmoPgpInstallDialog.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FidesmoPgpInstallDialog.java new file mode 100644 index 000000000..cdf6e5c7c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FidesmoPgpInstallDialog.java @@ -0,0 +1,63 @@ +package org.sufficientlysecure.keychain.ui.dialog; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; + +public class FidesmoPgpInstallDialog extends DialogFragment { + + // Fidesmo constants + private static final String FIDESMO_SERVICE_DELIVERY_CARD_ACTION = "com.fidesmo.sec.DELIVER_SERVICE"; + private static final String FIDESMO_SERVICE_URI = "https://api.fidesmo.com/service/"; + private static final String FIDESMO_PGP_APPLICATION_ID = "0cdc651e"; + private static final String FIDESMO_PGP_SERVICE_ID = "OKC-install"; + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + CustomAlertDialogBuilder mCustomAlertDialogBuilder = new CustomAlertDialogBuilder(getActivity()); + mCustomAlertDialogBuilder.setTitle(getString(R.string.prompt_fidesmo_pgp_install_title)); + mCustomAlertDialogBuilder.setMessage(getString(R.string.prompt_fidesmo_pgp_install_message)); + mCustomAlertDialogBuilder.setPositiveButton( + getString(R.string.prompt_fidesmo_pgp_install_button_positive), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + startFidesmoPgpAppletActivity(); + } + }); + mCustomAlertDialogBuilder.setNegativeButton( + getString(R.string.prompt_fidesmo_pgp_install_button_negative), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }); + + return mCustomAlertDialogBuilder.show(); + } + + private void startFidesmoPgpAppletActivity() { + try { + // Call the Fidesmo app with the PGP applet as parameter to + // send the user straight to it + final String mPgpInstallServiceUrl = FIDESMO_SERVICE_URI + FIDESMO_PGP_APPLICATION_ID + + "/" + FIDESMO_PGP_SERVICE_ID; + Intent mPgpServiceIntent = new Intent(FIDESMO_SERVICE_DELIVERY_CARD_ACTION, + Uri.parse(mPgpInstallServiceUrl)); + startActivity(mPgpServiceIntent); + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "Error when parsing URI"); + } + } +} |