diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index bad087468..11e61a6a5 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -871,7 +871,7 @@ android:exported="false" android:label="@string/app_name" /> CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index e3b3139a4..8a0600159 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.SignatureException; import java.util.Date; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -208,7 +209,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation skippedDisallowedKeys = new HashSet<>(); boolean insecureEncryptionKey = false; // convenience method to return with error @@ -607,7 +608,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation arrayList) { - long[] result = new long[arrayList.size()]; - int i = 0; - for (Long e : arrayList) { - result[i++] = e; - } - return result; - } - static class KeyIdResult { private final Intent mResultIntent; private final HashSet mKeyIds; @@ -206,7 +195,7 @@ class OpenPgpServiceKeyIdExtractor { if (mKeyIds == null) { throw new AssertionError("key ids must not be null when getKeyIds is called!"); } - return getUnboxedLongArray(mKeyIds); + return KeyFormattingUtils.getUnboxedLongArray(mKeyIds); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java new file mode 100644 index 000000000..33e58a482 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RequestKeyPermissionActivity.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 Vincent Breitmoser + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.remote.ui; + + +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.ViewAnimator; + +import org.openintents.openpgp.util.OpenPgpUtils.UserId; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.ApiDataAccessObject; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.ApiPermissionHelper; +import org.sufficientlysecure.keychain.remote.ApiPermissionHelper.WrongPackageCertificateException; +import org.sufficientlysecure.keychain.ui.ViewKeyActivity; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; +import org.sufficientlysecure.keychain.util.Log; + + +public class RequestKeyPermissionActivity extends BaseActivity { + public static final String EXTRA_PACKAGE_NAME = "package_name"; + public static final String EXTRA_REQUESTED_KEY_IDS = "requested_key_ids"; + + private ViewAnimator viewAnimator; + + private String packageName; + private View keyInfoLayout; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); + + checkPackageAllowed(packageName); + + // Inflate a "Done" custom action bar + setFullScreenDialogClose( + new View.OnClickListener() { + @Override + public void onClick(View v) { + setResult(RESULT_CANCELED); + finish(); + } + }); + + ImageView iconClientApp = (ImageView) findViewById(R.id.icon_client_app); + Drawable appIcon; + CharSequence appName; + try { + PackageManager packageManager = getPackageManager(); + ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0); + appIcon = packageManager.getApplicationIcon(applicationInfo); + appName = packageManager.getApplicationLabel(applicationInfo); + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Unable to find info of calling app!"); + finish(); + return; + } + iconClientApp.setImageDrawable(appIcon); + + TextView titleText = (TextView) findViewById(R.id.select_identity_key_title); + titleText.setText(getString(R.string.request_permission_title, appName)); + + viewAnimator = (ViewAnimator) findViewById(R.id.status_animator); + keyInfoLayout = findViewById(R.id.key_info_layout); + + long[] requestedMasterKeyIds = getIntent().getLongArrayExtra(EXTRA_REQUESTED_KEY_IDS); + long masterKeyId = requestedMasterKeyIds[0]; +// long masterKeyId = 4817915339785265755L; + try { + CachedPublicKeyRing cachedPublicKeyRing = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId); + + UserId userId = cachedPublicKeyRing.getSplitPrimaryUserIdWithFallback(); + displayKeyInfo(masterKeyId, userId); + + if (cachedPublicKeyRing.hasAnySecret()) { + displayRequestKeyChoice(masterKeyId); + } else { + displayNoSecretKeyError(); + } + } catch (PgpKeyNotFoundException e) { + keyInfoLayout.setVisibility(View.GONE); + displayUnknownSecretKeyError(); + } + } + + private void displayKeyInfo(final long masterKeyId, UserId userId) { + keyInfoLayout.setVisibility(View.VISIBLE); + TextView keyUserIdView = (TextView) findViewById(R.id.select_key_item_name); + keyUserIdView.setText(userId.name); + + findViewById(R.id.display_key).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(RequestKeyPermissionActivity.this, ViewKeyActivity.class); + intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); + startActivity(intent); + } + }); + } + + private void displayRequestKeyChoice(final long masterKeyId) { + viewAnimator.setDisplayedChild(0); + + findViewById(R.id.button_allow).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + allowAndFinish(masterKeyId); + } + }); + + findViewById(R.id.button_disallow).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + setResult(Activity.RESULT_CANCELED); + finish(); + } + }); + } + + private void allowAndFinish(long masterKeyId) { + new ApiDataAccessObject(this).addAllowedKeyIdForApp(packageName, masterKeyId); + + finish(); + } + + private void displayNoSecretKeyError() { + viewAnimator.setDisplayedChild(1); + + } + + private void displayUnknownSecretKeyError() { + viewAnimator.setDisplayedChild(2); + + } + + private void checkPackageAllowed(String packageName) { + ApiDataAccessObject apiDao = new ApiDataAccessObject(this); + ApiPermissionHelper apiPermissionHelper = new ApiPermissionHelper(this, apiDao); + boolean packageAllowed; + try { + packageAllowed = apiPermissionHelper.isPackageAllowed(packageName); + } catch (WrongPackageCertificateException e) { + packageAllowed = false; + } + if (!packageAllowed) { + throw new IllegalStateException("Pending intent launched by unknown app!"); + } + } + + @Override + protected void initLayout() { + setContentView(R.layout.api_remote_request_key_permission); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectAllowedKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectAllowedKeysActivity.java deleted file mode 100644 index 1dc53e61a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectAllowedKeysActivity.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2015 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.remote.ui; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.util.Log; - -public class SelectAllowedKeysActivity extends BaseActivity { - - public static final String EXTRA_SERVICE_INTENT = "data"; - - private AppSettingsAllowedKeysListFragment mAllowedKeysFragment; - - Intent mServiceData; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Inflate a "Done" custom action bar - setFullScreenDialogDoneClose(R.string.api_settings_save, - new View.OnClickListener() { - @Override - public void onClick(View v) { - save(); - } - }, - new View.OnClickListener() { - @Override - public void onClick(View v) { - cancel(); - } - }); - - Intent intent = getIntent(); - mServiceData = intent.getParcelableExtra(EXTRA_SERVICE_INTENT); - Uri appUri = intent.getData(); - if (appUri == null) { - Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); - finish(); - return; - } else { - Log.d(Constants.TAG, "uri: " + appUri); - loadData(savedInstanceState, appUri); - } - } - - @Override - protected void initLayout() { - setContentView(R.layout.api_remote_select_allowed_keys); - } - - private void save() { - mAllowedKeysFragment.saveAllowedKeys(); - setResult(Activity.RESULT_OK, mServiceData); - finish(); - } - - private void cancel() { - setResult(Activity.RESULT_CANCELED); - finish(); - } - - private void loadData(Bundle savedInstanceState, Uri appUri) { - Uri allowedKeysUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ALLOWED_KEYS).build(); - Log.d(Constants.TAG, "allowedKeysUri: " + allowedKeysUri); - startListFragments(savedInstanceState, allowedKeysUri); - } - - private void startListFragments(Bundle savedInstanceState, Uri allowedKeysUri) { - // However, if we're being restored from a previous state, - // then we don't need to do anything and should return or else - // we could end up with overlapping fragments. - if (savedInstanceState != null) { - return; - } - - // Create an instance of the fragments - mAllowedKeysFragment = AppSettingsAllowedKeysListFragment.newInstance(allowedKeysUri); - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.api_allowed_keys_list_fragment, mAllowedKeysFragment) - .commitAllowingStateLoss(); - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index 169a0c831..b5f41796c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.PorterDuff; +import android.support.annotation.NonNull; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; @@ -51,6 +52,7 @@ import java.nio.ByteBuffer; import java.security.DigestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Collection; import java.util.Locale; public class KeyFormattingUtils { @@ -410,6 +412,16 @@ public class KeyFormattingUtils { public static final int DEFAULT_COLOR = -1; + @NonNull + public static long[] getUnboxedLongArray(@NonNull Collection arrayList) { + long[] result = new long[arrayList.size()]; + int i = 0; + for (Long e : arrayList) { + result[i++] = e; + } + return result; + } + public enum State { REVOKED, EXPIRED, diff --git a/OpenKeychain/src/main/res/layout/api_remote_request_key_permission.xml b/OpenKeychain/src/main/res/layout/api_remote_request_key_permission.xml new file mode 100644 index 000000000..ac2ccf57e --- /dev/null +++ b/OpenKeychain/src/main/res/layout/api_remote_request_key_permission.xml @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +