diff options
Diffstat (limited to 'src/org/openintents/openpgp/util/OpenPgpApi.java')
-rw-r--r-- | src/org/openintents/openpgp/util/OpenPgpApi.java | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/org/openintents/openpgp/util/OpenPgpApi.java b/src/org/openintents/openpgp/util/OpenPgpApi.java new file mode 100644 index 00000000..f6a78d0e --- /dev/null +++ b/src/org/openintents/openpgp/util/OpenPgpApi.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.openintents.openpgp.util; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import org.openintents.openpgp.IOpenPgpService; +import org.openintents.openpgp.OpenPgpError; + +import java.io.InputStream; +import java.io.OutputStream; + +public class OpenPgpApi { + + public static final String TAG = "OpenPgp API"; + + public static final int API_VERSION = 3; + public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService"; + + /** + * General extras + * -------------- + * + * required extras: + * int EXTRA_API_VERSION (always required) + * + * returned extras: + * int RESULT_CODE (RESULT_CODE_ERROR, RESULT_CODE_SUCCESS or RESULT_CODE_USER_INTERACTION_REQUIRED) + * OpenPgpError RESULT_ERROR (if RESULT_CODE == RESULT_CODE_ERROR) + * PendingIntent RESULT_INTENT (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED) + */ + + /** + * Sign only + * <p/> + * optional extras: + * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput) + * String EXTRA_PASSPHRASE (key passphrase) + */ + public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN"; + + /** + * Encrypt + * <p/> + * required extras: + * String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT) + * or + * long[] EXTRA_KEY_IDS + * <p/> + * optional extras: + * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput) + * String EXTRA_PASSPHRASE (key passphrase) + */ + public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT"; + + /** + * Sign and encrypt + * <p/> + * required extras: + * String[] EXTRA_USER_IDS (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT) + * or + * long[] EXTRA_KEY_IDS + * <p/> + * optional extras: + * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput) + * String EXTRA_PASSPHRASE (key passphrase) + */ + public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT"; + + /** + * Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted, + * and also signed-only input. + * <p/> + * If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY + * in addition a PendingIntent is returned via RESULT_INTENT to download missing keys. + * <p/> + * optional extras: + * boolean EXTRA_REQUEST_ASCII_ARMOR (request ascii armor for ouput) + * <p/> + * returned extras: + * OpenPgpSignatureResult RESULT_SIGNATURE + */ + public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY"; + + /** + * Get key ids based on given user ids (=emails) + * <p/> + * required extras: + * String[] EXTRA_USER_IDS + * <p/> + * returned extras: + * long[] RESULT_KEY_IDS + */ + public static final String ACTION_GET_KEY_IDS = "org.openintents.openpgp.action.GET_KEY_IDS"; + + /** + * This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key + * corresponding to the given key id in its database. + * <p/> + * It returns RESULT_CODE_USER_INTERACTION_REQUIRED if the Provider does not have the key. + * The PendingIntent from RESULT_INTENT can be used to retrieve those from a keyserver. + * <p/> + * required extras: + * long EXTRA_KEY_ID + */ + public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY"; + + /* Intent extras */ + public static final String EXTRA_API_VERSION = "api_version"; + + public static final String EXTRA_ACCOUNT_NAME = "account_name"; + + // SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY + // request ASCII Armor for output + // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53) + public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor"; + + // ENCRYPT, SIGN_AND_ENCRYPT + public static final String EXTRA_USER_IDS = "user_ids"; + public static final String EXTRA_KEY_IDS = "key_ids"; + // optional extras: + public static final String EXTRA_PASSPHRASE = "passphrase"; + + // GET_KEY + public static final String EXTRA_KEY_ID = "key_id"; + public static final String RESULT_KEY_IDS = "key_ids"; + + /* Service Intent returns */ + public static final String RESULT_CODE = "result_code"; + + // get actual error object from RESULT_ERROR + public static final int RESULT_CODE_ERROR = 0; + // success! + public static final int RESULT_CODE_SUCCESS = 1; + // get PendingIntent from RESULT_INTENT, start PendingIntent with startIntentSenderForResult, + // and execute service method again in onActivityResult + public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2; + + public static final String RESULT_ERROR = "error"; + public static final String RESULT_INTENT = "intent"; + + // DECRYPT_VERIFY + public static final String RESULT_SIGNATURE = "signature"; + + IOpenPgpService mService; + Context mContext; + + public OpenPgpApi(Context context, IOpenPgpService service) { + this.mContext = context; + this.mService = service; + } + + public interface IOpenPgpCallback { + void onReturn(final Intent result); + } + + private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Intent> { + Intent data; + InputStream is; + OutputStream os; + IOpenPgpCallback callback; + + private OpenPgpAsyncTask(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) { + this.data = data; + this.is = is; + this.os = os; + this.callback = callback; + } + + @Override + protected Intent doInBackground(Void... unused) { + return executeApi(data, is, os); + } + + protected void onPostExecute(Intent result) { + callback.onReturn(result); + } + + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) { + OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback); + + // don't serialize async tasks! + // http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } else { + task.execute((Void[]) null); + } + } + + public Intent executeApi(Intent data, InputStream is, OutputStream os) { + try { + data.putExtra(EXTRA_API_VERSION, OpenPgpApi.API_VERSION); + + Intent result; + + // pipe the input and output + ParcelFileDescriptor input = null; + if (is != null) { + input = ParcelFileDescriptorUtil.pipeFrom(is, + new ParcelFileDescriptorUtil.IThreadListener() { + + @Override + public void onThreadFinished(Thread thread) { + //Log.d(OpenPgpApi.TAG, "Copy to service finished"); + } + } + ); + } + ParcelFileDescriptor output = null; + if (os != null) { + output = ParcelFileDescriptorUtil.pipeTo(os, + new ParcelFileDescriptorUtil.IThreadListener() { + + @Override + public void onThreadFinished(Thread thread) { + //Log.d(OpenPgpApi.TAG, "Service finished writing!"); + } + } + ); + } + + // blocks until result is ready + result = mService.execute(data, input, output); + // close() is required to halt the TransferThread + if (output != null) { + output.close(); + } + // TODO: close input? + + // set class loader to current context to allow unparcelling + // of OpenPgpError and OpenPgpSignatureResult + // http://stackoverflow.com/a/3806769 + result.setExtrasClassLoader(mContext.getClassLoader()); + + return result; + } catch (Exception e) { + Log.e(OpenPgpApi.TAG, "Exception in executeApi call", e); + Intent result = new Intent(); + result.putExtra(RESULT_CODE, RESULT_CODE_ERROR); + result.putExtra(RESULT_ERROR, + new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage())); + return result; + } + } + +} |