/* * Copyright (C) 2012 Dominik Schürmann * Copyright (C) 2010-2011 K-9 Mail Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sufficientlysecure.keychain.integration; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.widget.Toast; public class KeychainIntentHelper { public static final String APG_INTENT_PREFIX = Constants.KEYCHAIN_PACKAGE_NAME + ".action."; // Intents /** * Encrypt */ // without permission public static final String ACTION_ENCRYPT = APG_INTENT_PREFIX + "ENCRYPT"; public static final String ACTION_ENCRYPT_FILE = APG_INTENT_PREFIX + "ENCRYPT_FILE"; // with permission public static final String ACTION_ENCRYPT_AND_RETURN = APG_INTENT_PREFIX + "ENCRYPT_AND_RETURN"; public static final String ACTION_GENERATE_SIGNATURE_AND_RETURN = APG_INTENT_PREFIX + "GENERATE_SIGNATURE_AND_RETURN"; public static final String ACTION_ENCRYPT_STREAM_AND_RETURN = APG_INTENT_PREFIX + "ENCRYPT_STREAM_AND_RETURN"; /** * Decrypt */ // without permission public static final String ACTION_DECRYPT = APG_INTENT_PREFIX + "DECRYPT"; public static final String ACTION_DECRYPT_FILE = APG_INTENT_PREFIX + "DECRYPT_FILE"; // with permission public static final String ACTION_DECRYPT_AND_RETURN = APG_INTENT_PREFIX + "DECRYPT_AND_RETURN"; public static final String ACTION_DECRYPT_STREAM_AND_RETURN = APG_INTENT_PREFIX + "DECRYPT_STREAM_AND_RETURN"; /** * Select keys, without permission */ public static final String ACTION_SELECT_PUBLIC_KEYS = APG_INTENT_PREFIX + "SELECT_PUBLIC_KEYRINGS"; public static final String ACTION_SELECT_SECRET_KEY = APG_INTENT_PREFIX + "SELECT_SECRET_KEYRING"; /** * Create key/edit key, without permission */ public static final String ACTION_CREATE_KEY = APG_INTENT_PREFIX + "CREATE_KEYRING"; public static final String ACTION_EDIT_KEY = APG_INTENT_PREFIX + "EDIT_KEYRING"; /** * Import actions */ public static final String ACTION_IMPORT = APG_INTENT_PREFIX + "IMPORT"; // only used by IMPORT public static final String EXTRA_IMPORT_TEXT = "text"; public static final String EXTRA_IMPORT_KEYRING_BYTES = "keyringBytes"; public static final String ACTION_IMPORT_FROM_FILE = APG_INTENT_PREFIX + "IMPORT_FROM_FILE"; public static final String ACTION_IMPORT_FROM_QR_CODE = APG_INTENT_PREFIX + "IMPORT_FROM_QR_CODE"; /** * Share actions */ public static final String ACTION_SHARE_KEYRING = APG_INTENT_PREFIX + "SHARE_KEYRING"; public static final String ACTION_SHARE_KEYRING_WITH_QR_CODE = APG_INTENT_PREFIX + "SHARE_KEYRING_WITH_QR_CODE"; public static final String ACTION_SHARE_KEYRING_WITH_NFC = APG_INTENT_PREFIX + "SHARE_KEYRING_WITH_NFC"; // used by SHARE_WITH_QR_CODE and SHARE_WITH_NFC public static final String EXTRA_MASTER_KEY_ID = "masterKeyId"; public static final String EXTRA_TEXT = "text"; public static final String EXTRA_DATA = "data"; public static final String EXTRA_ERROR = "error"; public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage"; public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage"; public static final String EXTRA_SIGNATURE = "signature"; public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId"; public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess"; public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown"; public static final String EXTRA_USER_ID = "userId"; public static final String EXTRA_USER_IDS = "userIds"; public static final String EXTRA_KEY_ID = "masterKeyId"; public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds"; public static final String EXTRA_SELECTION = "selection"; public static final String EXTRA_MESSAGE = "message"; public static final String EXTRA_NO_PASSPHRASE = "noPassphrase"; public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generateDefaultKeys"; public static final String EXTRA_INTENT_VERSION = "intentVersion"; public static final String RESULT_EXTRA_MASTER_KEY_IDS = "masterKeyIds"; public static final String RESULT_EXTRA_USER_IDS = "userIds"; // result from EditKey public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; public static final String RESULT_EXTRA_USER_ID = "userId"; public static final String INTENT_VERSION = "1"; public static final int DECRYPT_MESSAGE = 0x00007121; public static final int ENCRYPT_MESSAGE = 0x00007122; public static final int SELECT_PUBLIC_KEYS = 0x00007123; public static final int SELECT_SECRET_KEY = 0x00007124; public static final int CREATE_KEY = 0x00007125; public static final int EDIT_KEY = 0x00007126; private Activity activity; public KeychainIntentHelper(Activity activity) { this.activity = activity; } /** * Start an activity.
* This method is defined to allow different methods of activity starting for newer versions of * Android and for compatibility library. * * @param intent * Intent to start. * @param code * Request code for the activity * @see android.app.Activity#startActivityForResult(Intent, int) * @see android.app.Fragment#startActivityForResult(Intent, int) */ protected void startActivityForResult(Intent intent, int code) { activity.startActivityForResult(intent, code); } /** * Open activity to scan qr code and import key in it * * @return true when activity was found and executed successfully */ public boolean importFromQrCode() { Intent intent = new Intent(ACTION_IMPORT_FROM_QR_CODE); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); try { startActivityForResult(intent, -1); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Opens activity with qr code for masterKeyId * * @return true when activity was found and executed successfully */ public boolean shareWithQrCode(long masterKeyId) { Intent intent = new Intent(ACTION_SHARE_KEYRING_WITH_QR_CODE); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); intent.putExtra(EXTRA_MASTER_KEY_ID, masterKeyId); try { startActivityForResult(intent, -1); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Opens activity to share keyring withnc based on given masterKeyId * * @return true when activity was found and executed successfully */ public boolean shareWithNfc(long masterKeyId) { Intent intent = new Intent(ACTION_SHARE_KEYRING_WITH_NFC); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); intent.putExtra(EXTRA_MASTER_KEY_ID, masterKeyId); try { startActivityForResult(intent, -1); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Opens APG activity to create new key * * @param userIds * value to specify prefilled values for user that should be created * @return true when activity was found and executed successfully */ public boolean createNewKey(String userIds, boolean noPassphrase, boolean generateDefaultKeys) { Intent intent = new Intent(ACTION_CREATE_KEY); if (userIds != null) { intent.putExtra(EXTRA_USER_IDS, userIds); } intent.putExtra(EXTRA_NO_PASSPHRASE, noPassphrase); intent.putExtra(EXTRA_GENERATE_DEFAULT_KEYS, generateDefaultKeys); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); try { startActivityForResult(intent, CREATE_KEY); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Opens APG activity to create new key * * @return true when activity was found and executed successfully */ public boolean createNewKey() { return createNewKey(null, false, false); } /** * Opens APG activity to edit already existing key based on keyId * * @param keyId * @return true when activity was found and executed successfully */ public boolean editKey(long keyId) { Intent intent = new Intent(ACTION_EDIT_KEY); intent.putExtra(EXTRA_KEY_ID, keyId); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); try { startActivityForResult(intent, EDIT_KEY); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Opens APG activity to select the signature key. * * @return true when activity was found and executed successfully */ public boolean selectSecretKey() { Intent intent = new Intent(ACTION_SELECT_SECRET_KEY); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); try { startActivityForResult(intent, SELECT_SECRET_KEY); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Encrypts the given data by opening APGs encrypt activity. If encryptionKeys are given it * encrypts immediately and goes back to your program after that * * @param data * String that contains the message to be encrypted * @param encryptionKeyIds * long[] that holds the ids of the encryption keys * @param signatureKeyId * id of the signature key * @return true when activity was found and executed successfully */ public boolean encrypt(String data, long[] encryptionKeyIds, long signatureKeyId, boolean returnResult) { Intent intent = new Intent(); if (returnResult) { intent.setAction(ACTION_ENCRYPT_AND_RETURN); } else { intent.setAction(ACTION_ENCRYPT); } intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); intent.setType("text/plain"); intent.putExtra(EXTRA_TEXT, data); intent.putExtra(EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds); intent.putExtra(EXTRA_SIGNATURE_KEY_ID, signatureKeyId); try { startActivityForResult(intent, ENCRYPT_MESSAGE); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Start the decrypt activity. * * @param activity * @param data * @param pgpData * @return true when activity was found and executed successfully */ public boolean decrypt(String data, boolean returnResult) { Intent intent = new Intent(); if (returnResult) { intent.setAction(ACTION_DECRYPT_AND_RETURN); } else { intent.setAction(ACTION_DECRYPT); } intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); intent.setType("text/plain"); if (data == null) { return false; } try { intent.putExtra(EXTRA_TEXT, data); startActivityForResult(intent, DECRYPT_MESSAGE); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } /** * Handle the activity results that concern us. * * @param requestCode * @param resultCode * @param data * @return handled or not */ public boolean onActivityResult(int requestCode, int resultCode, Intent data, KeychainData apgData) { switch (requestCode) { case CREATE_KEY: if (resultCode != Activity.RESULT_OK || data == null) { // user canceled! break; } apgData.setSecretKeyId(data.getLongExtra(RESULT_EXTRA_MASTER_KEY_ID, 0)); apgData.setSecretKeyUserId(data.getStringExtra(RESULT_EXTRA_USER_ID)); break; case SELECT_SECRET_KEY: if (resultCode != Activity.RESULT_OK || data == null) { // user canceled! break; } apgData.setSecretKeyId(data.getLongExtra(EXTRA_KEY_ID, 0)); apgData.setSecretKeyUserId(data.getStringExtra(EXTRA_USER_ID)); break; case SELECT_PUBLIC_KEYS: if (resultCode != Activity.RESULT_OK || data == null) { apgData.setPublicKeyIds(null); break; } apgData.setPublicKeyIds(data.getLongArrayExtra(RESULT_EXTRA_MASTER_KEY_IDS)); apgData.setPublicUserIds(data.getStringArrayExtra(RESULT_EXTRA_USER_IDS)); break; case ENCRYPT_MESSAGE: if (resultCode != Activity.RESULT_OK || data == null) { apgData.setPublicKeyIds(null); break; } apgData.setEncryptedData(data.getStringExtra(EXTRA_ENCRYPTED_MESSAGE)); break; case DECRYPT_MESSAGE: if (resultCode != Activity.RESULT_OK || data == null) { break; } apgData.setSecretKeyUserId(data.getStringExtra(EXTRA_SIGNATURE_USER_ID)); apgData.setSecretKeyId(data.getLongExtra(EXTRA_SIGNATURE_KEY_ID, 0)); apgData.setSignatureSuccess(data.getBooleanExtra(EXTRA_SIGNATURE_SUCCESS, false)); apgData.setSignatureUnknown(data.getBooleanExtra(EXTRA_SIGNATURE_UNKNOWN, false)); apgData.setDecryptedData(data.getStringExtra(EXTRA_DECRYPTED_MESSAGE)); break; default: return false; } return true; } /** * Select encryption keys. * * @param emails * The emails that should be used for preselection. * @return true when activity was found and executed successfully */ public boolean selectPublicKeys(String emails) { return selectPublicKeys(emails, null); } /** * Select encryption keys. * * @param emails * The emails that should be used for preselection. * @param apgData * ApgData with encryption keys and signature keys preselected * @return true when activity was found and executed successfully */ public boolean selectPublicKeys(String emails, KeychainData apgData) { Intent intent = new Intent(ACTION_SELECT_PUBLIC_KEYS); intent.putExtra(EXTRA_INTENT_VERSION, INTENT_VERSION); long[] initialKeyIds = null; if (apgData == null || !apgData.hasPublicKeys()) { KeychainContentProviderHelper cPHelper = new KeychainContentProviderHelper(activity); initialKeyIds = cPHelper.getPublicKeyringIdsByEmail(emails); } else { initialKeyIds = apgData.getPublicKeys(); } intent.putExtra(EXTRA_SELECTION, initialKeyIds); try { startActivityForResult(intent, SELECT_PUBLIC_KEYS); return true; } catch (ActivityNotFoundException e) { activityNotFound(); return false; } } private void activityNotFound() { Toast.makeText(activity, "APG Activity not found! Is APG installed correctly?", Toast.LENGTH_LONG).show(); } }