From b63fe462a4e0b9eb3936beb875ec240fa58138a2 Mon Sep 17 00:00:00 2001 From: Dominik Date: Wed, 25 Apr 2012 18:59:51 +0200 Subject: restructering services --- org_apg/AndroidManifest.xml | 33 +- .../org/thialfihar/android/apg/ApgService2.java | 658 -------------------- .../org/thialfihar/android/apg/IApgService.aidl | 125 ---- .../src/org/thialfihar/android/apg/Service.java | 92 --- .../apg/provider/ApgServiceBlobDatabase.java | 2 +- .../apg/provider/ApgServiceBlobProvider.java | 2 +- .../android/apg/service/ApgRemoteService.java | 5 + .../android/apg/service/ApgService2.java | 672 +++++++++++++++++++++ .../android/apg/service/IApgService2.aidl | 125 ++++ .../apg/service/PassphraseCacheService.java | 95 +++ .../thialfihar/android/apg/ui/BaseActivity.java | 6 +- .../thialfihar/android/apg/ui/EditKeyActivity.java | 3 +- .../org/thialfihar/android/apg/util/ApgCon.java | 10 +- 13 files changed, 925 insertions(+), 903 deletions(-) delete mode 100644 org_apg/src/org/thialfihar/android/apg/ApgService2.java delete mode 100644 org_apg/src/org/thialfihar/android/apg/IApgService.aidl delete mode 100644 org_apg/src/org/thialfihar/android/apg/Service.java create mode 100644 org_apg/src/org/thialfihar/android/apg/service/ApgRemoteService.java create mode 100644 org_apg/src/org/thialfihar/android/apg/service/ApgService2.java create mode 100644 org_apg/src/org/thialfihar/android/apg/service/IApgService2.aidl create mode 100644 org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java (limited to 'org_apg') diff --git a/org_apg/AndroidManifest.xml b/org_apg/AndroidManifest.xml index e51f9eac4..797f87fab 100644 --- a/org_apg/AndroidManifest.xml +++ b/org_apg/AndroidManifest.xml @@ -223,23 +223,24 @@ android:name="org.thialfihar.android.apg.ui.HelpActivity" android:label="@string/title_help" /> - - + - - - - - - - - - - - - - - + + + + + + + + + > FUNCTIONS_REQUIRED_ARGS = new HashMap>(); - static { - HashSet args = new HashSet(); - args.add(arg.SYMMETRIC_PASSPHRASE); - FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_passphrase.name(), args); - - args = new HashSet(); - args.add(arg.PUBLIC_KEYS); - FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_public_key.name(), args); - - args = new HashSet(); - FUNCTIONS_REQUIRED_ARGS.put(call.decrypt.name(), args); - - args = new HashSet(); - args.add(arg.KEY_TYPE); - FUNCTIONS_REQUIRED_ARGS.put(call.get_keys.name(), args); - } - - /** optional arguments for each AIDL function */ - private static final HashMap> FUNCTIONS_OPTIONAL_ARGS = new HashMap>(); - static { - HashSet args = new HashSet(); - args.add(arg.ENCRYPTION_ALGORYTHM); - args.add(arg.HASH_ALGORYTHM); - args.add(arg.ARMORED_OUTPUT); - args.add(arg.FORCE_V3_SIGNATURE); - args.add(arg.COMPRESSION); - args.add(arg.PRIVATE_KEY_PASSPHRASE); - args.add(arg.SIGNATURE_KEY); - args.add(arg.BLOB); - args.add(arg.MESSAGE); - FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_passphrase.name(), args); - FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_public_key.name(), args); - - args = new HashSet(); - args.add(arg.SYMMETRIC_PASSPHRASE); - args.add(arg.PUBLIC_KEYS); - args.add(arg.PRIVATE_KEY_PASSPHRASE); - args.add(arg.MESSAGE); - args.add(arg.BLOB); - FUNCTIONS_OPTIONAL_ARGS.put(call.decrypt.name(), args); - } - - /** a map from ApgService parameters to function calls to get the default */ - private static final HashMap FUNCTIONS_DEFAULTS = new HashMap(); - static { - FUNCTIONS_DEFAULTS.put(arg.ENCRYPTION_ALGORYTHM, "getDefaultEncryptionAlgorithm"); - FUNCTIONS_DEFAULTS.put(arg.HASH_ALGORYTHM, "getDefaultHashAlgorithm"); - FUNCTIONS_DEFAULTS.put(arg.ARMORED_OUTPUT, "getDefaultAsciiArmour"); - FUNCTIONS_DEFAULTS.put(arg.FORCE_V3_SIGNATURE, "getForceV3Signatures"); - FUNCTIONS_DEFAULTS.put(arg.COMPRESSION, "getDefaultMessageCompression"); - } - - /** a map of the default function names to their method */ - private static final HashMap FUNCTIONS_DEFAULTS_METHODS = new HashMap(); - static { - try { - FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm", - Preferences.class.getMethod("getDefaultEncryptionAlgorithm")); - FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm", - Preferences.class.getMethod("getDefaultHashAlgorithm")); - FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour", - Preferences.class.getMethod("getDefaultAsciiArmour")); - FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures", - Preferences.class.getMethod("getForceV3Signatures")); - FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression", - Preferences.class.getMethod("getDefaultMessageCompression")); - } catch (Exception e) { - Log.e(TAG, "Function method exception: " + e.getMessage()); - } - } - - private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException { - byte[] buffer = new byte[8]; - int len = 0; - while ((len = is.read(buffer)) != -1) { - os.write(buffer, 0, len); - } - } - - private static Cursor getKeyEntries(HashMap pParams) { - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "(" - + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "." - + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY - + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "(" - + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "." - + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); - - String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by") - : UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC"; - - String typeVal[] = null; - String typeWhere = null; - if (pParams.containsKey("key_type")) { - typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?"; - typeVal = new String[] { "" + pParams.get("key_type") }; - } - return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere, - typeVal, null, null, orderBy); - } - - /** - * maps a fingerprint or user id of a key to a master key in database - * - * @param search_key - * fingerprint or user id to search for - * @return master key if found, or 0 - */ - private static long getMasterKey(String pSearchKey, Bundle pReturn) { - if (pSearchKey == null || pSearchKey.length() != 8) { - return 0; - } - ArrayList keyList = new ArrayList(); - keyList.add(pSearchKey); - long[] keys = getMasterKey(keyList, pReturn); - if (keys.length > 0) { - return keys[0]; - } else { - return 0; - } - } - - /** - * maps fingerprints or user ids of keys to master keys in database - * - * @param search_keys - * a list of keys (fingerprints or user ids) to look for in database - * @return an array of master keys - */ - private static long[] getMasterKey(ArrayList pSearchKeys, Bundle pReturn) { - - HashMap qParams = new HashMap(); - qParams.put("columns", new String[] { KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0 - UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1 - }); - qParams.put("key_type", Id.database.type_public); - - Cursor mCursor = getKeyEntries(qParams); - - if (LOCAL_LOGV) - Log.v(TAG, "going through installed user keys"); - ArrayList masterKeys = new ArrayList(); - while (mCursor.moveToNext()) { - long curMkey = mCursor.getLong(0); - String curUser = mCursor.getString(1); - - String curFprint = Apg.getSmallFingerPrint(curMkey); - if (LOCAL_LOGV) - Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")"); - if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) { - if (LOCAL_LOGV) - Log.v(TAG, "master key found for: " + curFprint); - masterKeys.add(curMkey); - pSearchKeys.remove(curFprint); - } else { - if (LOCAL_LOGV) - Log.v(TAG, "Installed key " + curFprint - + " is not in the list of public keys to encrypt with"); - } - } - mCursor.close(); - - long[] masterKeyLongs = new long[masterKeys.size()]; - int i = 0; - for (Long key : masterKeys) { - masterKeyLongs[i++] = key; - } - - if (i == 0) { - Log.w(TAG, "Found not one public key"); - pReturn.getStringArrayList(ret.WARNINGS.name()).add( - "Searched for public key(s) but found not one"); - } - - for (String key : pSearchKeys) { - Log.w(TAG, "Searched for key " + key + " but cannot find it in APG"); - pReturn.getStringArrayList(ret.WARNINGS.name()).add( - "Searched for key " + key + " but cannot find it in APG"); - } - - return masterKeyLongs; - } - - /** - * Add default arguments if missing - * - * @param args - * the bundle to add default parameters to if missing - */ - private void addDefaultArguments(String pCall, Bundle pArgs) { - // check whether there are optional elements defined for that call - if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pCall)) { - Preferences preferences = Preferences.getPreferences(getBaseContext(), true); - - Iterator iter = FUNCTIONS_DEFAULTS.keySet().iterator(); - while (iter.hasNext()) { - arg currentArg = iter.next(); - String currentKey = currentArg.name(); - if (!pArgs.containsKey(currentKey) - && FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) { - String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg); - try { - Class returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) - .getReturnType(); - if (returnType == String.class) { - pArgs.putString(currentKey, - (String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) - .invoke(preferences)); - } else if (returnType == boolean.class) { - pArgs.putBoolean(currentKey, - (Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) - .invoke(preferences)); - } else if (returnType == int.class) { - pArgs.putInt(currentKey, - (Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) - .invoke(preferences)); - } else { - Log.e(TAG, "Unknown return type " + returnType.toString() - + " for default option"); - } - } catch (Exception e) { - Log.e(TAG, "Exception in add_default_arguments " + e.getMessage()); - } - } - } - } - } - - /** - * updates a Bundle with default return values - * - * @param pReturn - * the Bundle to update - */ - private void addDefaultReturns(Bundle pReturn) { - ArrayList errors = new ArrayList(); - ArrayList warnings = new ArrayList(); - - pReturn.putStringArrayList(ret.ERRORS.name(), errors); - pReturn.putStringArrayList(ret.WARNINGS.name(), warnings); - } - - /** - * checks for required arguments and adds them to the error if missing - * - * @param function - * the functions required arguments to check for - * @param pArgs - * the Bundle of arguments to check - * @param pReturn - * the bundle to write errors to - */ - private void checkForRequiredArgs(String pFunction, Bundle pArgs, Bundle pReturn) { - if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) { - Iterator iter = FUNCTIONS_REQUIRED_ARGS.get(pFunction).iterator(); - while (iter.hasNext()) { - String curArg = iter.next().name(); - if (!pArgs.containsKey(curArg)) { - pReturn.getStringArrayList(ret.ERRORS.name()) - .add("Argument missing: " + curArg); - } - } - } - - if (pFunction.equals(call.encrypt_with_passphrase.name()) - || pFunction.equals(call.encrypt_with_public_key.name()) - || pFunction.equals(call.decrypt.name())) { - // check that either MESSAGE or BLOB are there - if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) { - pReturn.getStringArrayList(ret.ERRORS.name()).add( - "Arguments missing: Neither MESSAGE nor BLOG found"); - } - - } - } - - /** - * checks for unknown arguments and add them to warning if found - * - * @param function - * the functions name to check against - * @param pArgs - * the Bundle of arguments to check - * @param pReturn - * the bundle to write warnings to - */ - private void checkForUnknownArgs(String pFunction, Bundle pArgs, Bundle pReturn) { - - HashSet allArgs = new HashSet(); - if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) { - allArgs.addAll(FUNCTIONS_REQUIRED_ARGS.get(pFunction)); - } - if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pFunction)) { - allArgs.addAll(FUNCTIONS_OPTIONAL_ARGS.get(pFunction)); - } - - ArrayList unknownArgs = new ArrayList(); - Iterator iter = pArgs.keySet().iterator(); - while (iter.hasNext()) { - String curKey = iter.next(); - try { - arg curArg = arg.valueOf(curKey); - if (!allArgs.contains(curArg)) { - pReturn.getStringArrayList(ret.WARNINGS.name()).add( - "Unknown argument: " + curKey); - unknownArgs.add(curKey); - } - } catch (Exception e) { - pReturn.getStringArrayList(ret.WARNINGS.name()).add("Unknown argument: " + curKey); - unknownArgs.add(curKey); - } - } - - // remove unknown arguments so our bundle has just what we need - for (String arg : unknownArgs) { - pArgs.remove(arg); - } - } - - private boolean prepareArgs(String pCall, Bundle pArgs, Bundle pReturn) { - Apg.initialize(getBaseContext()); - - /* add default return values for all functions */ - addDefaultReturns(pReturn); - - /* add default arguments if missing */ - addDefaultArguments(pCall, pArgs); - if (LOCAL_LOGV) - Log.v(TAG, "add_default_arguments"); - - /* check for required arguments */ - checkForRequiredArgs(pCall, pArgs, pReturn); - if (LOCAL_LOGV) - Log.v(TAG, "check_required_args"); - - /* check for unknown arguments and add to warning if found */ - checkForUnknownArgs(pCall, pArgs, pReturn); - if (LOCAL_LOGV) - Log.v(TAG, "check_unknown_args"); - - /* return if errors happened */ - if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) { - if (LOCAL_LOGV) - Log.v(TAG, "Errors after preparing, not executing " + pCall); - pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal()); - return false; - } - if (LOCAL_LOGV) - Log.v(TAG, "error return"); - - return true; - } - - private boolean encrypt(Bundle pArgs, Bundle pReturn) { - boolean isBlob = pArgs.containsKey(arg.BLOB.name()); - - long pubMasterKeys[] = {}; - if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) { - ArrayList list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name()); - ArrayList pubKeys = new ArrayList(); - if (LOCAL_LOGV) - Log.v(TAG, "Long size: " + list.size()); - Iterator iter = list.iterator(); - while (iter.hasNext()) { - pubKeys.add(iter.next()); - } - pubMasterKeys = getMasterKey(pubKeys, pReturn); - } - - InputStream inStream = null; - if (isBlob) { - ContentResolver cr = getContentResolver(); - try { - inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); - } catch (Exception e) { - Log.e(TAG, "... exception on opening blob", e); - } - } else { - inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); - } - InputData in = new InputData(inStream, 0); // XXX Size second param? - - OutputStream out = new ByteArrayOutputStream(); - if (LOCAL_LOGV) - Log.v(TAG, "About to encrypt"); - try { - Apg.encrypt(getBaseContext(), // context - in, // input stream - out, // output stream - pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT - pubMasterKeys, // encryption keys - getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature - // key - pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase - null, // progress - pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption - pArgs.getInt(arg.HASH_ALGORYTHM.name()), // hash - pArgs.getInt(arg.COMPRESSION.name()), // compression - pArgs.getBoolean(arg.FORCE_V3_SIGNATURE.name()), // mPreferences.getForceV3Signatures(), - pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) // passPhrase - ); - } catch (Exception e) { - Log.e(TAG, "Exception in encrypt"); - String msg = e.getMessage(); - if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) { - pReturn.getStringArrayList(ret.ERRORS.name()).add( - "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): " - + msg); - pReturn.putInt(ret.ERROR.name(), - error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal()); - } else if (msg.equals(getBaseContext().getString( - R.string.error_couldNotExtractPrivateKey))) { - pReturn.getStringArrayList(ret.ERRORS.name()).add( - "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() - + " probably wrong): " + msg); - pReturn.putInt(ret.ERROR.name(), - error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); - } else { - pReturn.getStringArrayList(ret.ERRORS.name()).add( - "Internal failure (" + e.getClass() + ") in APG when encrypting: " - + e.getMessage()); - pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); - } - return false; - } - if (LOCAL_LOGV) - Log.v(TAG, "Encrypted"); - if (isBlob) { - ContentResolver cr = getContentResolver(); - try { - OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB - .name()))); - writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); - outStream.close(); - } catch (Exception e) { - Log.e(TAG, "... exception on writing blob", e); - } - } else { - pReturn.putString(ret.RESULT.name(), out.toString()); - } - return true; - } - - private final IApgService.Stub mBinder = new IApgService.Stub() { - - public boolean getKeys(Bundle pArgs, Bundle pReturn) { - - prepareArgs("get_keys", pArgs, pReturn); - - HashMap qParams = new HashMap(); - qParams.put("columns", new String[] { - KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0 - UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1 - }); - - qParams.put("key_type", pArgs.getInt(arg.KEY_TYPE.name())); - - Cursor cursor = getKeyEntries(qParams); - ArrayList fPrints = new ArrayList(); - ArrayList ids = new ArrayList(); - while (cursor.moveToNext()) { - if (LOCAL_LOGV) - Log.v(TAG, "adding key " + Apg.getSmallFingerPrint(cursor.getLong(0))); - fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0))); - ids.add(cursor.getString(1)); - } - cursor.close(); - - pReturn.putStringArrayList(ret.FINGERPRINTS.name(), fPrints); - pReturn.putStringArrayList(ret.USER_IDS.name(), ids); - return true; - } - - public boolean encryptWithPublicKey(Bundle pArgs, Bundle pReturn) { - if (!prepareArgs("encrypt_with_public_key", pArgs, pReturn)) { - return false; - } - - return encrypt(pArgs, pReturn); - } - - public boolean encryptWithPassphrase(Bundle pArgs, Bundle pReturn) { - if (!prepareArgs("encrypt_with_passphrase", pArgs, pReturn)) { - return false; - } - - return encrypt(pArgs, pReturn); - - } - - public boolean decrypt(Bundle pArgs, Bundle pReturn) { - if (!prepareArgs("decrypt", pArgs, pReturn)) { - return false; - } - - boolean isBlob = pArgs.containsKey(arg.BLOB.name()); - - String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs - .getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs - .getString(arg.PRIVATE_KEY_PASSPHRASE.name()); - - InputStream inStream = null; - if (isBlob) { - ContentResolver cr = getContentResolver(); - try { - inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); - } catch (Exception e) { - Log.e(TAG, "... exception on opening blob", e); - } - } else { - inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); - } - - InputData in = new InputData(inStream, 0); // XXX what size in second parameter? - OutputStream out = new ByteArrayOutputStream(); - if (LOCAL_LOGV) - Log.v(TAG, "About to decrypt"); - try { - Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress - pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric - ); - } catch (Exception e) { - Log.e(TAG, "Exception in decrypt"); - String msg = e.getMessage(); - if (msg.equals(getBaseContext().getString(R.string.error_noSecretKeyFound))) { - pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg); - pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal()); - } else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) { - pReturn.getStringArrayList(ret.ERRORS.name()).add( - "Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() - + " wrong/missing): " + msg); - pReturn.putInt(ret.ERROR.name(), - error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); - } else { - pReturn.getStringArrayList(ret.ERRORS.name()).add( - "Internal failure (" + e.getClass() + ") in APG when decrypting: " - + msg); - pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); - } - return false; - } - if (LOCAL_LOGV) - Log.v(TAG, "... decrypted"); - - if (isBlob) { - ContentResolver cr = getContentResolver(); - try { - OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB - .name()))); - writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), - outStream); - outStream.close(); - } catch (Exception e) { - Log.e(TAG, "... exception on writing blob", e); - } - } else { - pReturn.putString(ret.RESULT.name(), out.toString()); - } - return true; - } - - }; -} diff --git a/org_apg/src/org/thialfihar/android/apg/IApgService.aidl b/org_apg/src/org/thialfihar/android/apg/IApgService.aidl deleted file mode 100644 index 25780f366..000000000 --- a/org_apg/src/org/thialfihar/android/apg/IApgService.aidl +++ /dev/null @@ -1,125 +0,0 @@ -package org.thialfihar.android.apg; - -interface IApgService { - - /* All functions fill the returnVals Bundle with the following keys: - * - * ArrayList "WARNINGS" = Warnings, if any - * ArrayList "ERRORS" = Human readable error descriptions, if any - * int "ERROR" = Numeric representation of error, if any - * starting with 100: - * 100: Required argument missing - * 101: Generic failure of APG - * 102: No matching private key found - * 103: Private key's passphrase wrong - * 104: Private key's passphrase missing - */ - - /* ******************************************************** - * Encryption - * ********************************************************/ - - /* All encryption function's arguments - * - * Bundle params' keys: - * (optional/required) - * TYPE "STRING KEY" = EXPLANATION / VALUES - * - * (required) - * String "MESSAGE" = Message to encrypt - * OR - * String "BLOB" = ContentUri to a file handle - * with binary data to encrypt - * (Attention: file will be overwritten - * with encrypted content!) - * - * (optional) - * int "ENCRYPTION_ALGORYTHM" = Encryption Algorithm - * 7: AES-128, 8: AES-192, 9: AES-256, - * 4: Blowfish, 10: Twofish, 3: CAST5, - * 6: DES, 2: Triple DES, 1: IDEA - * (optional) - * int "HASH_ALGORYTHM" = Hash Algorithm - * 1: MD5, 3: RIPEMD-160, 2: SHA-1, - * 11: SHA-224, 8: SHA-256, 9: SHA-384, - * 10: SHA-512 - * (optional) - * Boolean "ARMORED_OUTPUT" = Armor output - * - * (optional) - * Boolean "FORCE_V3_SIGNATURE" = Force V3 Signatures - * - * (optional) - * int "COMPRESSION" = Compression to use - * 0x21070001: none, 1: Zip, 2: Zlib, - * 3: BZip2 - * (optional) - * String "SIGNATURE_KEY" = Key to sign with - * - * (optional) - * String "PRIVATE_KEY_PASSPHRASE" = Passphrase for signing key - * - * Bundle returnVals (in addition to the ERRORS/WARNINGS above): - * If "MESSAGE" was set: - * String "RESULT" = Encrypted message - */ - - /* Additional argument for function below: - * (required) - * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase to use - */ - boolean encryptWithPassphrase(in Bundle params, out Bundle returnVals); - - /* Additional argument: - * (required) - * ArrayList "PUBLIC_KEYS" = Public keys (8char fingerprint "123ABC12" OR - * complete id "Alice Meyer ") - */ - boolean encryptWithPublicKey(in Bundle params, out Bundle returnVals); - - /* ******************************************************** - * Decryption - * ********************************************************/ - - /* Bundle params: - * (required) - * String "MESSAGE" = Message to dencrypt - * OR - * String "BLOB" = ContentUri to a file handle - * with binary data to dencrypt - * (Attention: file will be overwritten - * with dencrypted content!) - * - * (optional) - * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase for decryption - * - * (optional) - * String "PRIVATE_KEY_PASSPHRASE" = Private keys's passphrase on asymmetric encryption - * - * Bundle return_vals: - * If "MESSAGE" was set: - * String "RESULT" = Decrypted message - */ - boolean decrypt(in Bundle params, out Bundle returnVals); - - /* ******************************************************** - * Get key information - * ********************************************************/ - - /* Get info about all available keys - * - * Bundle params: - * (required) - * int "KEY_TYPE" = info about what type of keys to return - * 0: public keys - * 1: private keys - * - * Returns: - * StringArrayList "FINGERPRINTS" = Short fingerprints of keys - * - * StringArrayList "USER_IDS" = User ids of corresponding fingerprints - * (order is the same as in FINGERPRINTS) - */ - boolean getKeys(in Bundle params, out Bundle returnVals); - -} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/Service.java b/org_apg/src/org/thialfihar/android/apg/Service.java deleted file mode 100644 index a1e298ef6..000000000 --- a/org_apg/src/org/thialfihar/android/apg/Service.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.thialfihar.android.apg; - -import android.content.Intent; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; - -public class Service extends android.app.Service { - private final IBinder mBinder = new LocalBinder(); - - public static final String EXTRA_TTL = "ttl"; - - private int mPassPhraseCacheTtl = 15; - private Handler mCacheHandler = new Handler(); - private Runnable mCacheTask = new Runnable() { - public void run() { - // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15), - // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl - int delay = mPassPhraseCacheTtl * 1000 / 2; - // also make sure the delay is not longer than one minute - if (delay > 60000) { - delay = 60000; - } - - delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay); - // don't check too often, even if we were close - if (delay < 5000) { - delay = 5000; - } - - mCacheHandler.postDelayed(this, delay); - } - }; - - static private boolean mIsRunning = false; - - @Override - public void onCreate() { - super.onCreate(); - - mIsRunning = true; - } - - @Override - public void onDestroy() { - super.onDestroy(); - mIsRunning = false; - } - - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); - - if (intent != null) { - mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15); - } - if (mPassPhraseCacheTtl < 15) { - mPassPhraseCacheTtl = 15; - } - mCacheHandler.removeCallbacks(mCacheTask); - mCacheHandler.postDelayed(mCacheTask, 1000); - } - - static public boolean isRunning() { - return mIsRunning; - } - - public class LocalBinder extends Binder { - Service getService() { - return Service.this; - } - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } -} diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java index a0b42ead7..509dfe8b4 100644 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java +++ b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java @@ -14,7 +14,7 @@ package org.thialfihar.android.apg.provider; -import org.thialfihar.android.apg.ApgService2; +import org.thialfihar.android.apg.service.ApgService2; import android.content.ContentUris; import android.content.ContentValues; diff --git a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java index 28e94bf5c..d7bacab76 100644 --- a/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java +++ b/org_apg/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java @@ -14,8 +14,8 @@ package org.thialfihar.android.apg.provider; -import org.thialfihar.android.apg.ApgService2; import org.thialfihar.android.apg.Constants; +import org.thialfihar.android.apg.service.ApgService2; import android.content.ContentProvider; import android.content.ContentValues; diff --git a/org_apg/src/org/thialfihar/android/apg/service/ApgRemoteService.java b/org_apg/src/org/thialfihar/android/apg/service/ApgRemoteService.java new file mode 100644 index 000000000..1cfdf4864 --- /dev/null +++ b/org_apg/src/org/thialfihar/android/apg/service/ApgRemoteService.java @@ -0,0 +1,5 @@ +package org.thialfihar.android.apg.service; + +public class ApgRemoteService { + +} diff --git a/org_apg/src/org/thialfihar/android/apg/service/ApgService2.java b/org_apg/src/org/thialfihar/android/apg/service/ApgService2.java new file mode 100644 index 000000000..efc4d2d0d --- /dev/null +++ b/org_apg/src/org/thialfihar/android/apg/service/ApgService2.java @@ -0,0 +1,672 @@ +/* + * 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.thialfihar.android.apg.service; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import org.thialfihar.android.apg.Apg; +import org.thialfihar.android.apg.service.IApgService2; +import org.thialfihar.android.apg.Id; +import org.thialfihar.android.apg.InputData; +import org.thialfihar.android.apg.Preferences; +import org.thialfihar.android.apg.R; +import org.thialfihar.android.apg.service.IApgService2.Stub; +import org.thialfihar.android.apg.Id.database; +import org.thialfihar.android.apg.R.string; +import org.thialfihar.android.apg.provider.KeyRings; +import org.thialfihar.android.apg.provider.Keys; +import org.thialfihar.android.apg.provider.UserIds; + +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; + +/** + * ATTENTION: + * + * This is the old ApgService used as remote service over aidl interface. + * It will be reworked! + * + */ +public class ApgService2 extends PassphraseCacheService { + private final static String TAG = "ApgService"; + public static final boolean LOCAL_LOGV = true; + public static final boolean LOCAL_LOGD = true; + + @Override + public IBinder onBind(Intent intent) { + if (LOCAL_LOGD) + Log.d(TAG, "bound"); + return mBinder; + } + + /** error status */ + private static enum error { + ARGUMENTS_MISSING, APG_FAILURE, NO_MATCHING_SECRET_KEY, PRIVATE_KEY_PASSPHRASE_WRONG, PRIVATE_KEY_PASSPHRASE_MISSING; + + public int shiftedOrdinal() { + return ordinal() + 100; + } + } + + private static enum call { + encrypt_with_passphrase, encrypt_with_public_key, decrypt, get_keys + } + + /** all arguments that can be passed by calling application */ + public static enum arg { + MESSAGE, // message to encrypt or to decrypt + SYMMETRIC_PASSPHRASE, // key for symmetric en/decryption + PUBLIC_KEYS, // public keys for encryption + ENCRYPTION_ALGORYTHM, // encryption algorithm + HASH_ALGORYTHM, // hash algorithm + ARMORED_OUTPUT, // whether to armor output + FORCE_V3_SIGNATURE, // whether to force v3 signature + COMPRESSION, // what compression to use for encrypted output + SIGNATURE_KEY, // key for signing + PRIVATE_KEY_PASSPHRASE, // passphrase for encrypted private key + KEY_TYPE, // type of key (private or public) + BLOB, // blob passed + } + + /** all things that might be returned */ + private static enum ret { + ERRORS, // string array list with errors + WARNINGS, // string array list with warnings + ERROR, // numeric error + RESULT, // en-/decrypted + FINGERPRINTS, // fingerprints of keys + USER_IDS, // user ids + } + + /** required arguments for each AIDL function */ + private static final HashMap> FUNCTIONS_REQUIRED_ARGS = new HashMap>(); + static { + HashSet args = new HashSet(); + args.add(arg.SYMMETRIC_PASSPHRASE); + FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_passphrase.name(), args); + + args = new HashSet(); + args.add(arg.PUBLIC_KEYS); + FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_public_key.name(), args); + + args = new HashSet(); + FUNCTIONS_REQUIRED_ARGS.put(call.decrypt.name(), args); + + args = new HashSet(); + args.add(arg.KEY_TYPE); + FUNCTIONS_REQUIRED_ARGS.put(call.get_keys.name(), args); + } + + /** optional arguments for each AIDL function */ + private static final HashMap> FUNCTIONS_OPTIONAL_ARGS = new HashMap>(); + static { + HashSet args = new HashSet(); + args.add(arg.ENCRYPTION_ALGORYTHM); + args.add(arg.HASH_ALGORYTHM); + args.add(arg.ARMORED_OUTPUT); + args.add(arg.FORCE_V3_SIGNATURE); + args.add(arg.COMPRESSION); + args.add(arg.PRIVATE_KEY_PASSPHRASE); + args.add(arg.SIGNATURE_KEY); + args.add(arg.BLOB); + args.add(arg.MESSAGE); + FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_passphrase.name(), args); + FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_public_key.name(), args); + + args = new HashSet(); + args.add(arg.SYMMETRIC_PASSPHRASE); + args.add(arg.PUBLIC_KEYS); + args.add(arg.PRIVATE_KEY_PASSPHRASE); + args.add(arg.MESSAGE); + args.add(arg.BLOB); + FUNCTIONS_OPTIONAL_ARGS.put(call.decrypt.name(), args); + } + + /** a map from ApgService parameters to function calls to get the default */ + private static final HashMap FUNCTIONS_DEFAULTS = new HashMap(); + static { + FUNCTIONS_DEFAULTS.put(arg.ENCRYPTION_ALGORYTHM, "getDefaultEncryptionAlgorithm"); + FUNCTIONS_DEFAULTS.put(arg.HASH_ALGORYTHM, "getDefaultHashAlgorithm"); + FUNCTIONS_DEFAULTS.put(arg.ARMORED_OUTPUT, "getDefaultAsciiArmour"); + FUNCTIONS_DEFAULTS.put(arg.FORCE_V3_SIGNATURE, "getForceV3Signatures"); + FUNCTIONS_DEFAULTS.put(arg.COMPRESSION, "getDefaultMessageCompression"); + } + + /** a map of the default function names to their method */ + private static final HashMap FUNCTIONS_DEFAULTS_METHODS = new HashMap(); + static { + try { + FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm", + Preferences.class.getMethod("getDefaultEncryptionAlgorithm")); + FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm", + Preferences.class.getMethod("getDefaultHashAlgorithm")); + FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour", + Preferences.class.getMethod("getDefaultAsciiArmour")); + FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures", + Preferences.class.getMethod("getForceV3Signatures")); + FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression", + Preferences.class.getMethod("getDefaultMessageCompression")); + } catch (Exception e) { + Log.e(TAG, "Function method exception: " + e.getMessage()); + } + } + + private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException { + byte[] buffer = new byte[8]; + int len = 0; + while ((len = is.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + } + + private static Cursor getKeyEntries(HashMap pParams) { + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "(" + + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "." + + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "(" + + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "." + + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') "); + + String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by") + : UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC"; + + String typeVal[] = null; + String typeWhere = null; + if (pParams.containsKey("key_type")) { + typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?"; + typeVal = new String[] { "" + pParams.get("key_type") }; + } + return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere, + typeVal, null, null, orderBy); + } + + /** + * maps a fingerprint or user id of a key to a master key in database + * + * @param search_key + * fingerprint or user id to search for + * @return master key if found, or 0 + */ + private static long getMasterKey(String pSearchKey, Bundle pReturn) { + if (pSearchKey == null || pSearchKey.length() != 8) { + return 0; + } + ArrayList keyList = new ArrayList(); + keyList.add(pSearchKey); + long[] keys = getMasterKey(keyList, pReturn); + if (keys.length > 0) { + return keys[0]; + } else { + return 0; + } + } + + /** + * maps fingerprints or user ids of keys to master keys in database + * + * @param search_keys + * a list of keys (fingerprints or user ids) to look for in database + * @return an array of master keys + */ + private static long[] getMasterKey(ArrayList pSearchKeys, Bundle pReturn) { + + HashMap qParams = new HashMap(); + qParams.put("columns", new String[] { KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0 + UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1 + }); + qParams.put("key_type", Id.database.type_public); + + Cursor mCursor = getKeyEntries(qParams); + + if (LOCAL_LOGV) + Log.v(TAG, "going through installed user keys"); + ArrayList masterKeys = new ArrayList(); + while (mCursor.moveToNext()) { + long curMkey = mCursor.getLong(0); + String curUser = mCursor.getString(1); + + String curFprint = Apg.getSmallFingerPrint(curMkey); + if (LOCAL_LOGV) + Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")"); + if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) { + if (LOCAL_LOGV) + Log.v(TAG, "master key found for: " + curFprint); + masterKeys.add(curMkey); + pSearchKeys.remove(curFprint); + } else { + if (LOCAL_LOGV) + Log.v(TAG, "Installed key " + curFprint + + " is not in the list of public keys to encrypt with"); + } + } + mCursor.close(); + + long[] masterKeyLongs = new long[masterKeys.size()]; + int i = 0; + for (Long key : masterKeys) { + masterKeyLongs[i++] = key; + } + + if (i == 0) { + Log.w(TAG, "Found not one public key"); + pReturn.getStringArrayList(ret.WARNINGS.name()).add( + "Searched for public key(s) but found not one"); + } + + for (String key : pSearchKeys) { + Log.w(TAG, "Searched for key " + key + " but cannot find it in APG"); + pReturn.getStringArrayList(ret.WARNINGS.name()).add( + "Searched for key " + key + " but cannot find it in APG"); + } + + return masterKeyLongs; + } + + /** + * Add default arguments if missing + * + * @param args + * the bundle to add default parameters to if missing + */ + private void addDefaultArguments(String pCall, Bundle pArgs) { + // check whether there are optional elements defined for that call + if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pCall)) { + Preferences preferences = Preferences.getPreferences(getBaseContext(), true); + + Iterator iter = FUNCTIONS_DEFAULTS.keySet().iterator(); + while (iter.hasNext()) { + arg currentArg = iter.next(); + String currentKey = currentArg.name(); + if (!pArgs.containsKey(currentKey) + && FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) { + String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg); + try { + Class returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) + .getReturnType(); + if (returnType == String.class) { + pArgs.putString(currentKey, + (String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) + .invoke(preferences)); + } else if (returnType == boolean.class) { + pArgs.putBoolean(currentKey, + (Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) + .invoke(preferences)); + } else if (returnType == int.class) { + pArgs.putInt(currentKey, + (Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName) + .invoke(preferences)); + } else { + Log.e(TAG, "Unknown return type " + returnType.toString() + + " for default option"); + } + } catch (Exception e) { + Log.e(TAG, "Exception in add_default_arguments " + e.getMessage()); + } + } + } + } + } + + /** + * updates a Bundle with default return values + * + * @param pReturn + * the Bundle to update + */ + private void addDefaultReturns(Bundle pReturn) { + ArrayList errors = new ArrayList(); + ArrayList warnings = new ArrayList(); + + pReturn.putStringArrayList(ret.ERRORS.name(), errors); + pReturn.putStringArrayList(ret.WARNINGS.name(), warnings); + } + + /** + * checks for required arguments and adds them to the error if missing + * + * @param function + * the functions required arguments to check for + * @param pArgs + * the Bundle of arguments to check + * @param pReturn + * the bundle to write errors to + */ + private void checkForRequiredArgs(String pFunction, Bundle pArgs, Bundle pReturn) { + if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) { + Iterator iter = FUNCTIONS_REQUIRED_ARGS.get(pFunction).iterator(); + while (iter.hasNext()) { + String curArg = iter.next().name(); + if (!pArgs.containsKey(curArg)) { + pReturn.getStringArrayList(ret.ERRORS.name()) + .add("Argument missing: " + curArg); + } + } + } + + if (pFunction.equals(call.encrypt_with_passphrase.name()) + || pFunction.equals(call.encrypt_with_public_key.name()) + || pFunction.equals(call.decrypt.name())) { + // check that either MESSAGE or BLOB are there + if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) { + pReturn.getStringArrayList(ret.ERRORS.name()).add( + "Arguments missing: Neither MESSAGE nor BLOG found"); + } + + } + } + + /** + * checks for unknown arguments and add them to warning if found + * + * @param function + * the functions name to check against + * @param pArgs + * the Bundle of arguments to check + * @param pReturn + * the bundle to write warnings to + */ + private void checkForUnknownArgs(String pFunction, Bundle pArgs, Bundle pReturn) { + + HashSet allArgs = new HashSet(); + if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) { + allArgs.addAll(FUNCTIONS_REQUIRED_ARGS.get(pFunction)); + } + if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pFunction)) { + allArgs.addAll(FUNCTIONS_OPTIONAL_ARGS.get(pFunction)); + } + + ArrayList unknownArgs = new ArrayList(); + Iterator iter = pArgs.keySet().iterator(); + while (iter.hasNext()) { + String curKey = iter.next(); + try { + arg curArg = arg.valueOf(curKey); + if (!allArgs.contains(curArg)) { + pReturn.getStringArrayList(ret.WARNINGS.name()).add( + "Unknown argument: " + curKey); + unknownArgs.add(curKey); + } + } catch (Exception e) { + pReturn.getStringArrayList(ret.WARNINGS.name()).add("Unknown argument: " + curKey); + unknownArgs.add(curKey); + } + } + + // remove unknown arguments so our bundle has just what we need + for (String arg : unknownArgs) { + pArgs.remove(arg); + } + } + + private boolean prepareArgs(String pCall, Bundle pArgs, Bundle pReturn) { + Apg.initialize(getBaseContext()); + + /* add default return values for all functions */ + addDefaultReturns(pReturn); + + /* add default arguments if missing */ + addDefaultArguments(pCall, pArgs); + if (LOCAL_LOGV) + Log.v(TAG, "add_default_arguments"); + + /* check for required arguments */ + checkForRequiredArgs(pCall, pArgs, pReturn); + if (LOCAL_LOGV) + Log.v(TAG, "check_required_args"); + + /* check for unknown arguments and add to warning if found */ + checkForUnknownArgs(pCall, pArgs, pReturn); + if (LOCAL_LOGV) + Log.v(TAG, "check_unknown_args"); + + /* return if errors happened */ + if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) { + if (LOCAL_LOGV) + Log.v(TAG, "Errors after preparing, not executing " + pCall); + pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal()); + return false; + } + if (LOCAL_LOGV) + Log.v(TAG, "error return"); + + return true; + } + + private boolean encrypt(Bundle pArgs, Bundle pReturn) { + boolean isBlob = pArgs.containsKey(arg.BLOB.name()); + + long pubMasterKeys[] = {}; + if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) { + ArrayList list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name()); + ArrayList pubKeys = new ArrayList(); + if (LOCAL_LOGV) + Log.v(TAG, "Long size: " + list.size()); + Iterator iter = list.iterator(); + while (iter.hasNext()) { + pubKeys.add(iter.next()); + } + pubMasterKeys = getMasterKey(pubKeys, pReturn); + } + + InputStream inStream = null; + if (isBlob) { + ContentResolver cr = getContentResolver(); + try { + inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); + } catch (Exception e) { + Log.e(TAG, "... exception on opening blob", e); + } + } else { + inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); + } + InputData in = new InputData(inStream, 0); // XXX Size second param? + + OutputStream out = new ByteArrayOutputStream(); + if (LOCAL_LOGV) + Log.v(TAG, "About to encrypt"); + try { + Apg.encrypt(getBaseContext(), // context + in, // input stream + out, // output stream + pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT + pubMasterKeys, // encryption keys + getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature + // key + pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase + null, // progress + pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption + pArgs.getInt(arg.HASH_ALGORYTHM.name()), // hash + pArgs.getInt(arg.COMPRESSION.name()), // compression + pArgs.getBoolean(arg.FORCE_V3_SIGNATURE.name()), // mPreferences.getForceV3Signatures(), + pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) // passPhrase + ); + } catch (Exception e) { + Log.e(TAG, "Exception in encrypt"); + String msg = e.getMessage(); + if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) { + pReturn.getStringArrayList(ret.ERRORS.name()).add( + "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): " + + msg); + pReturn.putInt(ret.ERROR.name(), + error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal()); + } else if (msg.equals(getBaseContext().getString( + R.string.error_couldNotExtractPrivateKey))) { + pReturn.getStringArrayList(ret.ERRORS.name()).add( + "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + + " probably wrong): " + msg); + pReturn.putInt(ret.ERROR.name(), + error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); + } else { + pReturn.getStringArrayList(ret.ERRORS.name()).add( + "Internal failure (" + e.getClass() + ") in APG when encrypting: " + + e.getMessage()); + pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); + } + return false; + } + if (LOCAL_LOGV) + Log.v(TAG, "Encrypted"); + if (isBlob) { + ContentResolver cr = getContentResolver(); + try { + OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB + .name()))); + writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream); + outStream.close(); + } catch (Exception e) { + Log.e(TAG, "... exception on writing blob", e); + } + } else { + pReturn.putString(ret.RESULT.name(), out.toString()); + } + return true; + } + + private final IApgService2.Stub mBinder = new IApgService2.Stub() { + + public boolean getKeys(Bundle pArgs, Bundle pReturn) { + + prepareArgs("get_keys", pArgs, pReturn); + + HashMap qParams = new HashMap(); + qParams.put("columns", new String[] { + KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0 + UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1 + }); + + qParams.put("key_type", pArgs.getInt(arg.KEY_TYPE.name())); + + Cursor cursor = getKeyEntries(qParams); + ArrayList fPrints = new ArrayList(); + ArrayList ids = new ArrayList(); + while (cursor.moveToNext()) { + if (LOCAL_LOGV) + Log.v(TAG, "adding key " + Apg.getSmallFingerPrint(cursor.getLong(0))); + fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0))); + ids.add(cursor.getString(1)); + } + cursor.close(); + + pReturn.putStringArrayList(ret.FINGERPRINTS.name(), fPrints); + pReturn.putStringArrayList(ret.USER_IDS.name(), ids); + return true; + } + + public boolean encryptWithPublicKey(Bundle pArgs, Bundle pReturn) { + if (!prepareArgs("encrypt_with_public_key", pArgs, pReturn)) { + return false; + } + + return encrypt(pArgs, pReturn); + } + + public boolean encryptWithPassphrase(Bundle pArgs, Bundle pReturn) { + if (!prepareArgs("encrypt_with_passphrase", pArgs, pReturn)) { + return false; + } + + return encrypt(pArgs, pReturn); + + } + + public boolean decrypt(Bundle pArgs, Bundle pReturn) { + if (!prepareArgs("decrypt", pArgs, pReturn)) { + return false; + } + + boolean isBlob = pArgs.containsKey(arg.BLOB.name()); + + String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs + .getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs + .getString(arg.PRIVATE_KEY_PASSPHRASE.name()); + + InputStream inStream = null; + if (isBlob) { + ContentResolver cr = getContentResolver(); + try { + inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name()))); + } catch (Exception e) { + Log.e(TAG, "... exception on opening blob", e); + } + } else { + inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes()); + } + + InputData in = new InputData(inStream, 0); // XXX what size in second parameter? + OutputStream out = new ByteArrayOutputStream(); + if (LOCAL_LOGV) + Log.v(TAG, "About to decrypt"); + try { + Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress + pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric + ); + } catch (Exception e) { + Log.e(TAG, "Exception in decrypt"); + String msg = e.getMessage(); + if (msg.equals(getBaseContext().getString(R.string.error_noSecretKeyFound))) { + pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg); + pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal()); + } else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) { + pReturn.getStringArrayList(ret.ERRORS.name()).add( + "Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + + " wrong/missing): " + msg); + pReturn.putInt(ret.ERROR.name(), + error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal()); + } else { + pReturn.getStringArrayList(ret.ERRORS.name()).add( + "Internal failure (" + e.getClass() + ") in APG when decrypting: " + + msg); + pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal()); + } + return false; + } + if (LOCAL_LOGV) + Log.v(TAG, "... decrypted"); + + if (isBlob) { + ContentResolver cr = getContentResolver(); + try { + OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB + .name()))); + writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), + outStream); + outStream.close(); + } catch (Exception e) { + Log.e(TAG, "... exception on writing blob", e); + } + } else { + pReturn.putString(ret.RESULT.name(), out.toString()); + } + return true; + } + + }; +} diff --git a/org_apg/src/org/thialfihar/android/apg/service/IApgService2.aidl b/org_apg/src/org/thialfihar/android/apg/service/IApgService2.aidl new file mode 100644 index 000000000..4eba62655 --- /dev/null +++ b/org_apg/src/org/thialfihar/android/apg/service/IApgService2.aidl @@ -0,0 +1,125 @@ +package org.thialfihar.android.apg.service; + +interface IApgService2 { + + /* All functions fill the returnVals Bundle with the following keys: + * + * ArrayList "WARNINGS" = Warnings, if any + * ArrayList "ERRORS" = Human readable error descriptions, if any + * int "ERROR" = Numeric representation of error, if any + * starting with 100: + * 100: Required argument missing + * 101: Generic failure of APG + * 102: No matching private key found + * 103: Private key's passphrase wrong + * 104: Private key's passphrase missing + */ + + /* ******************************************************** + * Encryption + * ********************************************************/ + + /* All encryption function's arguments + * + * Bundle params' keys: + * (optional/required) + * TYPE "STRING KEY" = EXPLANATION / VALUES + * + * (required) + * String "MESSAGE" = Message to encrypt + * OR + * String "BLOB" = ContentUri to a file handle + * with binary data to encrypt + * (Attention: file will be overwritten + * with encrypted content!) + * + * (optional) + * int "ENCRYPTION_ALGORYTHM" = Encryption Algorithm + * 7: AES-128, 8: AES-192, 9: AES-256, + * 4: Blowfish, 10: Twofish, 3: CAST5, + * 6: DES, 2: Triple DES, 1: IDEA + * (optional) + * int "HASH_ALGORYTHM" = Hash Algorithm + * 1: MD5, 3: RIPEMD-160, 2: SHA-1, + * 11: SHA-224, 8: SHA-256, 9: SHA-384, + * 10: SHA-512 + * (optional) + * Boolean "ARMORED_OUTPUT" = Armor output + * + * (optional) + * Boolean "FORCE_V3_SIGNATURE" = Force V3 Signatures + * + * (optional) + * int "COMPRESSION" = Compression to use + * 0x21070001: none, 1: Zip, 2: Zlib, + * 3: BZip2 + * (optional) + * String "SIGNATURE_KEY" = Key to sign with + * + * (optional) + * String "PRIVATE_KEY_PASSPHRASE" = Passphrase for signing key + * + * Bundle returnVals (in addition to the ERRORS/WARNINGS above): + * If "MESSAGE" was set: + * String "RESULT" = Encrypted message + */ + + /* Additional argument for function below: + * (required) + * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase to use + */ + boolean encryptWithPassphrase(in Bundle params, out Bundle returnVals); + + /* Additional argument: + * (required) + * ArrayList "PUBLIC_KEYS" = Public keys (8char fingerprint "123ABC12" OR + * complete id "Alice Meyer ") + */ + boolean encryptWithPublicKey(in Bundle params, out Bundle returnVals); + + /* ******************************************************** + * Decryption + * ********************************************************/ + + /* Bundle params: + * (required) + * String "MESSAGE" = Message to dencrypt + * OR + * String "BLOB" = ContentUri to a file handle + * with binary data to dencrypt + * (Attention: file will be overwritten + * with dencrypted content!) + * + * (optional) + * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase for decryption + * + * (optional) + * String "PRIVATE_KEY_PASSPHRASE" = Private keys's passphrase on asymmetric encryption + * + * Bundle return_vals: + * If "MESSAGE" was set: + * String "RESULT" = Decrypted message + */ + boolean decrypt(in Bundle params, out Bundle returnVals); + + /* ******************************************************** + * Get key information + * ********************************************************/ + + /* Get info about all available keys + * + * Bundle params: + * (required) + * int "KEY_TYPE" = info about what type of keys to return + * 0: public keys + * 1: private keys + * + * Returns: + * StringArrayList "FINGERPRINTS" = Short fingerprints of keys + * + * StringArrayList "USER_IDS" = User ids of corresponding fingerprints + * (order is the same as in FINGERPRINTS) + */ + boolean getKeys(in Bundle params, out Bundle returnVals); + +} \ No newline at end of file diff --git a/org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java b/org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java new file mode 100644 index 000000000..f8b5959ee --- /dev/null +++ b/org_apg/src/org/thialfihar/android/apg/service/PassphraseCacheService.java @@ -0,0 +1,95 @@ +/* + * 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.thialfihar.android.apg.service; + +import org.thialfihar.android.apg.Apg; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; + +public class PassphraseCacheService extends Service { + private final IBinder mBinder = new LocalBinder(); + + public static final String EXTRA_TTL = "ttl"; + + private int mPassPhraseCacheTtl = 15; + private Handler mCacheHandler = new Handler(); + private Runnable mCacheTask = new Runnable() { + public void run() { + // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15), + // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl + int delay = mPassPhraseCacheTtl * 1000 / 2; + // also make sure the delay is not longer than one minute + if (delay > 60000) { + delay = 60000; + } + + delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay); + // don't check too often, even if we were close + if (delay < 5000) { + delay = 5000; + } + + mCacheHandler.postDelayed(this, delay); + } + }; + + static private boolean mIsRunning = false; + + @Override + public void onCreate() { + super.onCreate(); + + mIsRunning = true; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mIsRunning = false; + } + + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + + if (intent != null) { + mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15); + } + if (mPassPhraseCacheTtl < 15) { + mPassPhraseCacheTtl = 15; + } + mCacheHandler.removeCallbacks(mCacheTask); + mCacheHandler.postDelayed(mCacheTask, 1000); + } + + static public boolean isRunning() { + return mIsRunning; + } + + public class LocalBinder extends Binder { + PassphraseCacheService getService() { + return PassphraseCacheService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/org_apg/src/org/thialfihar/android/apg/ui/BaseActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/BaseActivity.java index 73d71c456..bd883f76d 100644 --- a/org_apg/src/org/thialfihar/android/apg/ui/BaseActivity.java +++ b/org_apg/src/org/thialfihar/android/apg/ui/BaseActivity.java @@ -28,7 +28,7 @@ import org.thialfihar.android.apg.Id; import org.thialfihar.android.apg.PausableThread; import org.thialfihar.android.apg.Preferences; import org.thialfihar.android.apg.ProgressDialogUpdater; -import org.thialfihar.android.apg.Service; +import org.thialfihar.android.apg.service.PassphraseCacheService; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockActivity; @@ -92,8 +92,8 @@ public class BaseActivity extends SherlockFragmentActivity implements Runnable, } public static void startCacheService(Activity activity, Preferences preferences) { - Intent intent = new Intent(activity, Service.class); - intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl()); + Intent intent = new Intent(activity, PassphraseCacheService.class); + intent.putExtra(PassphraseCacheService.EXTRA_TTL, preferences.getPassPhraseCacheTtl()); activity.startService(intent); } diff --git a/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java b/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java index 25a96c31e..138eaba38 100644 --- a/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java +++ b/org_apg/src/org/thialfihar/android/apg/ui/EditKeyActivity.java @@ -84,7 +84,7 @@ public class EditKeyActivity extends SherlockFragmentActivity { // extends BaseA Vector mKeys; Vector mKeysUsages; - // will be set to true to build layout later in handler + // will be set to false to build layout later in handler private boolean mBuildLayout = true; @Override @@ -300,7 +300,6 @@ public class EditKeyActivity extends SherlockFragmentActivity { // extends BaseA } else { mChangePassPhrase.setVisibility(View.VISIBLE); } - } }); diff --git a/org_apg/src/org/thialfihar/android/apg/util/ApgCon.java b/org_apg/src/org/thialfihar/android/apg/util/ApgCon.java index 22a9a7eb5..7341341d5 100644 --- a/org_apg/src/org/thialfihar/android/apg/util/ApgCon.java +++ b/org_apg/src/org/thialfihar/android/apg/util/ApgCon.java @@ -16,7 +16,7 @@ package org.thialfihar.android.apg.util; -import org.thialfihar.android.apg.IApgService; +import org.thialfihar.android.apg.service.IApgService2; import org.thialfihar.android.apg.util.ApgConInterface.OnCallFinishListener; import android.content.ComponentName; @@ -97,13 +97,13 @@ public class ApgCon { private final ArrayList mWarningList = new ArrayList(); /** Remote service for decrypting and encrypting data */ - private IApgService mApgService = null; + private IApgService2 mApgService = null; /** Set apgService accordingly to connection status */ private ServiceConnection mApgConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if( LOCAL_LOGD ) Log.d(TAG, "IApgService bound to apgService"); - mApgService = IApgService.Stub.asInterface(service); + mApgService = IApgService2.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName className) { @@ -231,7 +231,7 @@ public class ApgCon { } try { - mContext.bindService(new Intent(IApgService.class.getName()), mApgConnection, Context.BIND_AUTO_CREATE); + mContext.bindService(new Intent(IApgService2.class.getName()), mApgConnection, Context.BIND_AUTO_CREATE); } catch (Exception e) { Log.e(TAG, "could not bind APG service", e); return false; @@ -370,7 +370,7 @@ public class ApgCon { } try { - Boolean success = (Boolean) IApgService.class.getMethod(function, Bundle.class, Bundle.class).invoke(mApgService, pArgs, pReturn); + Boolean success = (Boolean) IApgService2.class.getMethod(function, Bundle.class, Bundle.class).invoke(mApgService, pArgs, pReturn); mErrorList.addAll(pReturn.getStringArrayList(ret.ERRORS.name())); mWarningList.addAll(pReturn.getStringArrayList(ret.WARNINGS.name())); return success; -- cgit v1.2.3