New API version, import from clipboard

This commit is contained in:
Dominik Schürmann 2013-10-05 18:35:16 +02:00
parent c75c00f935
commit bef6977aad
22 changed files with 954 additions and 219 deletions

View File

@ -26,7 +26,7 @@
<EditText <EditText
android:id="@+id/crypto_provider_demo_message" android:id="@+id/crypto_provider_demo_message"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="150dip" android:layout_height="100dip"
android:scrollHorizontally="true" android:scrollHorizontally="true"
android:scrollbars="vertical" android:scrollbars="vertical"
android:text="message" android:text="message"
@ -41,7 +41,7 @@
<EditText <EditText
android:id="@+id/crypto_provider_demo_ciphertext" android:id="@+id/crypto_provider_demo_ciphertext"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="150dip" android:layout_height="100dip"
android:text="ciphertext" android:text="ciphertext"
android:textAppearance="@android:style/TextAppearance.Small" /> android:textAppearance="@android:style/TextAppearance.Small" />
@ -67,19 +67,17 @@
<Button <Button
android:id="@+id/crypto_provider_demo_encrypt_and_sign" android:id="@+id/crypto_provider_demo_encrypt_and_sign"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:onClick="signAndEncryptOnClick"
android:onClick="encryptAndSignOnClick" android:text="Sign and Encrypt" />
android:text="Encrypt and Sign" />
<Button
android:id="@+id/crypto_provider_demo_decrypt_and_verify"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="decryptAndVerifyOnClick"
android:text="Decrypt and Verify" />
</LinearLayout> </LinearLayout>
<Button
android:id="@+id/crypto_provider_demo_decrypt_and_verify"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="decryptAndVerifyOnClick"
android:text="Decrypt and Verify" />
</LinearLayout> </LinearLayout>

View File

@ -16,6 +16,7 @@
package org.openintents.openpgp; package org.openintents.openpgp;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
@ -24,14 +25,14 @@ interface IOpenPgpCallback {
/** /**
* onSuccess returns on successful OpenPGP operations. * onSuccess returns on successful OpenPGP operations.
* *
* @param outputBytes * @param output
* contains resulting output bytes (decrypted content (when input was encrypted) * contains resulting output (decrypted content (when input was encrypted)
* or content without signature (when input was signed-only)) * or content without signature (when input was signed-only))
* @param signatureResult * @param signatureResult
* signatureResult is only non-null if decryptAndVerify() was called and the content * signatureResult is only non-null if decryptAndVerify() was called and the content
* was encrypted or signed-and-encrypted. * was encrypted or signed-and-encrypted.
*/ */
oneway void onSuccess(in byte[] outputBytes, in OpenPgpSignatureResult signatureResult); oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
/** /**
* onError returns on errors or when allowUserInteraction was set to false, but user interaction * onError returns on errors or when allowUserInteraction was set to false, but user interaction

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2013 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;
import org.openintents.openpgp.OpenPgpError;
interface IOpenPgpKeyIdsCallback {
/**
* onSuccess returns on successful getKeyIds operations.
*
* @param keyIds
* returned key ids
*/
oneway void onSuccess(in long[] keyIds);
/**
* onError returns on errors or when allowUserInteraction was set to false, but user interaction
* was required execute an OpenPGP operation.
*
* @param error
* See OpenPgpError class for more information.
*/
oneway void onError(in OpenPgpError error);
}

View File

@ -16,7 +16,9 @@
package org.openintents.openpgp; package org.openintents.openpgp;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.IOpenPgpCallback; import org.openintents.openpgp.IOpenPgpCallback;
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
/** /**
* All methods are oneway, which means they are asynchronous and non-blocking. * All methods are oneway, which means they are asynchronous and non-blocking.
@ -29,54 +31,76 @@ interface IOpenPgpService {
* *
* After successful encryption, callback's onSuccess will contain the resulting output bytes. * After successful encryption, callback's onSuccess will contain the resulting output bytes.
* *
* @param inputBytes * @param input
* Byte array you want to encrypt * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param encryptionUserIds * @param output
* User Ids (emails) of recipients * Request output format by defining OpenPgpData object
* @param asciiArmor *
* Encode result for ASCII (Radix-64, 33 percent overhead compared to binary) * new OpenPgpData(OpenPgpData.TYPE_STRING)
* @param allowUserInteraction * Returns as String
* Allows the OpenPGP Provider to handle missing keys by showing activities * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param keyIds
* Key Ids of recipients. Can be retrieved with getKeyIds()
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void encrypt(in byte[] inputBytes, in String[] encryptionUserIds, in boolean asciiArmor, oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
in IOpenPgpCallback callback);
/** /**
* Sign * Sign
* *
* After successful signing, callback's onSuccess will contain the resulting output bytes. * After successful signing, callback's onSuccess will contain the resulting output bytes.
* *
* @param inputBytes * @param input
* Byte array you want to sign * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param asciiArmor * @param output
* Encode result for ASCII (Radix-64, 33 percent overhead compared to binary) * Request output format by defining OpenPgpData object
* @param allowUserInteraction *
* Allows the OpenPGP Provider to handle missing keys by showing activities * new OpenPgpData(OpenPgpData.TYPE_STRING)
* Returns as String
* (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void sign(in byte[] inputBytes, in boolean asciiArmor, in IOpenPgpCallback callback); oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
/** /**
* Sign then encrypt * Sign then encrypt
* *
* After successful signing and encryption, callback's onSuccess will contain the resulting output bytes. * After successful signing and encryption, callback's onSuccess will contain the resulting output bytes.
* *
* @param inputBytes * @param input
* Byte array you want to sign and encrypt * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param encryptionUserIds * @param output
* User Ids (emails) of recipients * Request output format by defining OpenPgpData object
* @param asciiArmor *
* Encode result for ASCII (Radix-64, 33 percent overhead compared to binary) * new OpenPgpData(OpenPgpData.TYPE_STRING)
* @param allowUserInteraction * Returns as String
* Allows the OpenPGP Provider to handle missing keys by showing activities * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param keyIds
* Key Ids of recipients. Can be retrieved with getKeyIds()
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void signAndEncrypt(in byte[] inputBytes, in String[] encryptionUserIds, in boolean asciiArmor, oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
in IOpenPgpCallback callback);
/** /**
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted, * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
@ -85,15 +109,35 @@ interface IOpenPgpService {
* After successful decryption/verification, callback's onSuccess will contain the resulting output bytes. * After successful decryption/verification, callback's onSuccess will contain the resulting output bytes.
* The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given. * The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
* *
* @param inputBytes * @param input
* Byte array you want to decrypt and verify * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param allowUserInteraction * @param output
* Allows the OpenPGP Provider to handle missing keys by showing activities * Request output format by defining OpenPgpData object
*
* new OpenPgpData(OpenPgpData.TYPE_STRING)
* Returns as String
* (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void decryptAndVerify(in byte[] inputBytes, in IOpenPgpCallback callback); oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
boolean isKeyAvailable(in String[] userIds); /**
* Get available key ids based on given user ids
*
* @param ids
* User Ids (emails) of recipients OR key ids
* @param allowUserInteraction
* Enable user interaction to lookup and import unknown keys
* @param callback
* Callback where to return results (different type than callback in other functions!)
*/
oneway void getKeyIds(in String[] ids, in boolean allowUserInteraction, in IOpenPgpKeyIdsCallback callback);
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2013 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;
// Declare OpenPgpData so AIDL can find it and knows that it implements the parcelable protocol.
parcelable OpenPgpData;

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2013 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;
import android.net.Uri;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
public class OpenPgpData implements Parcelable {
public static final int TYPE_STRING = 0;
public static final int TYPE_BYTE_ARRAY = 1;
public static final int TYPE_FILE_DESCRIPTOR = 2;
public static final int TYPE_URI = 3;
int type;
String string;
byte[] bytes = new byte[0];
ParcelFileDescriptor fileDescriptor;
Uri uri;
public int getType() {
return type;
}
public String getString() {
return string;
}
public byte[] getBytes() {
return bytes;
}
public ParcelFileDescriptor getFileDescriptor() {
return fileDescriptor;
}
public Uri getUri() {
return uri;
}
public OpenPgpData() {
}
/**
* Not a real constructor. This can be used to define requested output type.
*
* @param type
*/
public OpenPgpData(int type) {
this.type = type;
}
public OpenPgpData(String string) {
this.string = string;
this.type = TYPE_STRING;
}
public OpenPgpData(byte[] bytes) {
this.bytes = bytes;
this.type = TYPE_BYTE_ARRAY;
}
public OpenPgpData(ParcelFileDescriptor fileDescriptor) {
this.fileDescriptor = fileDescriptor;
this.type = TYPE_FILE_DESCRIPTOR;
}
public OpenPgpData(Uri uri) {
this.uri = uri;
this.type = TYPE_URI;
}
public OpenPgpData(OpenPgpData b) {
this.string = b.string;
this.bytes = b.bytes;
this.fileDescriptor = b.fileDescriptor;
this.uri = b.uri;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(string);
dest.writeInt(bytes.length);
dest.writeByteArray(bytes);
dest.writeParcelable(fileDescriptor, 0);
dest.writeParcelable(uri, 0);
}
public static final Creator<OpenPgpData> CREATOR = new Creator<OpenPgpData>() {
public OpenPgpData createFromParcel(final Parcel source) {
OpenPgpData vr = new OpenPgpData();
vr.string = source.readString();
vr.bytes = new byte[source.readInt()];
source.readByteArray(vr.bytes);
vr.fileDescriptor = source.readParcelable(ParcelFileDescriptor.class.getClassLoader());
vr.fileDescriptor = source.readParcelable(Uri.class.getClassLoader());
return vr;
}
public OpenPgpData[] newArray(final int size) {
return new OpenPgpData[size];
}
};
}

View File

@ -23,7 +23,9 @@ import android.app.AlertDialog.Builder;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.preference.DialogPreference; import android.preference.DialogPreference;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -34,15 +36,17 @@ import android.widget.ListAdapter;
import android.widget.TextView; import android.widget.TextView;
public class OpenPgpListPreference extends DialogPreference { public class OpenPgpListPreference extends DialogPreference {
static final Intent intent = new Intent(IOpenPgpService.class.getName());
ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>(); ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
private String mSelectedPackage; private String mSelectedPackage;
public static final int REQUIRED_API_VERSION = 1;
public OpenPgpListPreference(Context context, AttributeSet attrs) { public OpenPgpListPreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0); List<ResolveInfo> resInfo =
context.getPackageManager().queryIntentServices(
new Intent(IOpenPgpService.class.getName()), PackageManager.GET_META_DATA);
if (!resInfo.isEmpty()) { if (!resInfo.isEmpty()) {
for (ResolveInfo resolveInfo : resInfo) { for (ResolveInfo resolveInfo : resInfo) {
if (resolveInfo.serviceInfo == null) if (resolveInfo.serviceInfo == null)
@ -52,7 +56,13 @@ public class OpenPgpListPreference extends DialogPreference {
String simpleName = String.valueOf(resolveInfo.serviceInfo String simpleName = String.valueOf(resolveInfo.serviceInfo
.loadLabel(context.getPackageManager())); .loadLabel(context.getPackageManager()));
Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager()); Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager());
mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
// get api version
ServiceInfo si = resolveInfo.serviceInfo;
int apiVersion = si.metaData.getInt("api_version");
mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon,
apiVersion));
} }
} }
} }
@ -68,8 +78,10 @@ public class OpenPgpListPreference extends DialogPreference {
* @param simpleName * @param simpleName
* @param icon * @param icon
*/ */
public void addProvider(int position, String packageName, String simpleName, Drawable icon) { public void addProvider(int position, String packageName, String simpleName, Drawable icon,
mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon)); int apiVersion) {
mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon,
apiVersion));
} }
@Override @Override
@ -91,13 +103,23 @@ public class OpenPgpListPreference extends DialogPreference {
int dp5 = (int) (5 * getContext().getResources().getDisplayMetrics().density + 0.5f); int dp5 = (int) (5 * getContext().getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp5); tv.setCompoundDrawablePadding(dp5);
// disable if it has the wrong api_version
if (mProviderList.get(position).apiVersion == REQUIRED_API_VERSION) {
tv.setEnabled(true);
} else {
tv.setEnabled(false);
tv.setText(tv.getText() + " (API v"
+ mProviderList.get(position).apiVersion + ", needs v"
+ REQUIRED_API_VERSION + ")");
}
return v; return v;
} }
}; };
builder.setSingleChoiceItems(adapter, getIndexOfProviderList(getValue()), builder.setSingleChoiceItems(adapter, getIndexOfProviderList(getValue()),
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
mSelectedPackage = mProviderList.get(which).packageName; mSelectedPackage = mProviderList.get(which).packageName;
@ -167,11 +189,14 @@ public class OpenPgpListPreference extends DialogPreference {
private String packageName; private String packageName;
private String simpleName; private String simpleName;
private Drawable icon; private Drawable icon;
private int apiVersion;
public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) { public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon,
int apiVersion) {
this.packageName = packageName; this.packageName = packageName;
this.simpleName = simpleName; this.simpleName = simpleName;
this.icon = icon; this.icon = icon;
this.apiVersion = apiVersion;
} }
@Override @Override

View File

@ -25,40 +25,50 @@ public class OpenPgpSignatureResult implements Parcelable {
// successfully verified signature, with trusted public key // successfully verified signature, with trusted public key
public static final int SIGNATURE_SUCCESS_TRUSTED = 1; public static final int SIGNATURE_SUCCESS_TRUSTED = 1;
// no public key was found for this signature verification // no public key was found for this signature verification
// you can retrieve the key with
// getKeys(new String[] {String.valueOf(signatureResult.getKeyId)}, true, callback)
public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2; public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
// successfully verified signature, but with untrusted public key // successfully verified signature, but with untrusted public key
public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3; public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3;
int signatureStatus; int status;
String signatureUserId;
boolean signatureOnly; boolean signatureOnly;
String userId;
long keyId;
public int getSignatureStatus() { public int getStatus() {
return signatureStatus; return status;
}
public String getSignatureUserId() {
return signatureUserId;
} }
public boolean isSignatureOnly() { public boolean isSignatureOnly() {
return signatureOnly; return signatureOnly;
} }
public String getUserId() {
return userId;
}
public long getKeyId() {
return keyId;
}
public OpenPgpSignatureResult() { public OpenPgpSignatureResult() {
} }
public OpenPgpSignatureResult(int signatureStatus, String signatureUserId, boolean signatureOnly) { public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
this.signatureStatus = signatureStatus; boolean signatureOnly, long keyId) {
this.signatureUserId = signatureUserId; this.status = signatureStatus;
this.signatureOnly = signatureOnly; this.signatureOnly = signatureOnly;
this.userId = signatureUserId;
this.keyId = keyId;
} }
public OpenPgpSignatureResult(OpenPgpSignatureResult b) { public OpenPgpSignatureResult(OpenPgpSignatureResult b) {
this.signatureStatus = b.signatureStatus; this.status = b.status;
this.signatureUserId = b.signatureUserId; this.userId = b.userId;
this.signatureOnly = b.signatureOnly; this.signatureOnly = b.signatureOnly;
this.keyId = b.keyId;
} }
public int describeContents() { public int describeContents() {
@ -66,17 +76,19 @@ public class OpenPgpSignatureResult implements Parcelable {
} }
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(signatureStatus); dest.writeInt(status);
dest.writeString(signatureUserId);
dest.writeByte((byte) (signatureOnly ? 1 : 0)); dest.writeByte((byte) (signatureOnly ? 1 : 0));
dest.writeString(userId);
dest.writeLong(keyId);
} }
public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() { public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() {
public OpenPgpSignatureResult createFromParcel(final Parcel source) { public OpenPgpSignatureResult createFromParcel(final Parcel source) {
OpenPgpSignatureResult vr = new OpenPgpSignatureResult(); OpenPgpSignatureResult vr = new OpenPgpSignatureResult();
vr.signatureStatus = source.readInt(); vr.status = source.readInt();
vr.signatureUserId = source.readString();
vr.signatureOnly = source.readByte() == 1; vr.signatureOnly = source.readByte() == 1;
vr.userId = source.readString();
vr.keyId = source.readLong();
return vr; return vr;
} }
@ -88,9 +100,10 @@ public class OpenPgpSignatureResult implements Parcelable {
@Override @Override
public String toString() { public String toString() {
String out = new String(); String out = new String();
out += "\nsignatureStatus: " + signatureStatus; out += "\nstatus: " + status;
out += "\nsignatureUserId: " + signatureUserId; out += "\nuserId: " + userId;
out += "\nsignatureOnly: " + signatureOnly; out += "\nsignatureOnly: " + signatureOnly;
out += "\nkeyId: " + keyId;
return out; return out;
} }

View File

@ -20,6 +20,6 @@ public final class Constants {
public static final boolean DEBUG = BuildConfig.DEBUG; public static final boolean DEBUG = BuildConfig.DEBUG;
public static final String TAG = "Keychain API"; public static final String TAG = "Keychain";
} }

View File

@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.demo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpServiceConnection; import org.openintents.openpgp.OpenPgpServiceConnection;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
@ -66,12 +68,75 @@ public class OpenPgpProviderActivity extends Activity {
} }
/** /**
* Callback from remote crypto service * Callback from remote openpgp service
*/ */
final IOpenPgpKeyIdsCallback.Stub getKeysEncryptCallback = new IOpenPgpKeyIdsCallback.Stub() {
@Override
public void onSuccess(final long[] keyIds) throws RemoteException {
Log.d(Constants.TAG, "getKeysEncryptCallback keyId " + keyIds[0]);
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
// encrypt after getting key ids
String inputStr = mMessage.getText().toString();
OpenPgpData input = new OpenPgpData(inputStr);
Log.d(Constants.TAG, "getKeysEncryptCallback inputStr " + inputStr);
try {
mCryptoServiceConnection.getService().encrypt(input,
new OpenPgpData(OpenPgpData.TYPE_STRING), keyIds, encryptCallback);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoProviderDemo", e);
}
}
});
}
@Override
public void onError(OpenPgpError error) throws RemoteException {
handleError(error);
}
};
final IOpenPgpKeyIdsCallback.Stub getKeysSignAndEncryptCallback = new IOpenPgpKeyIdsCallback.Stub() {
@Override
public void onSuccess(final long[] keyIds) throws RemoteException {
Log.d(Constants.TAG, "getKeysSignAndEncryptCallback keyId " + keyIds[0]);
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
// encrypt after getting key ids
String inputStr = mMessage.getText().toString();
OpenPgpData input = new OpenPgpData(inputStr);
try {
mCryptoServiceConnection.getService().signAndEncrypt(input,
new OpenPgpData(OpenPgpData.TYPE_STRING), keyIds, encryptCallback);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoProviderDemo", e);
}
}
});
}
@Override
public void onError(OpenPgpError error) throws RemoteException {
handleError(error);
}
};
final IOpenPgpCallback.Stub encryptCallback = new IOpenPgpCallback.Stub() { final IOpenPgpCallback.Stub encryptCallback = new IOpenPgpCallback.Stub() {
@Override @Override
public void onSuccess(final byte[] outputBytes, OpenPgpSignatureResult signatureResult) public void onSuccess(final OpenPgpData output, OpenPgpSignatureResult signatureResult)
throws RemoteException { throws RemoteException {
Log.d(Constants.TAG, "encryptCallback"); Log.d(Constants.TAG, "encryptCallback");
@ -79,7 +144,7 @@ public class OpenPgpProviderActivity extends Activity {
@Override @Override
public void run() { public void run() {
mCiphertext.setText(new String(outputBytes)); mCiphertext.setText(output.getString());
} }
}); });
} }
@ -94,7 +159,7 @@ public class OpenPgpProviderActivity extends Activity {
final IOpenPgpCallback.Stub decryptAndVerifyCallback = new IOpenPgpCallback.Stub() { final IOpenPgpCallback.Stub decryptAndVerifyCallback = new IOpenPgpCallback.Stub() {
@Override @Override
public void onSuccess(final byte[] outputBytes, final OpenPgpSignatureResult signatureResult) public void onSuccess(final OpenPgpData output, final OpenPgpSignatureResult signatureResult)
throws RemoteException { throws RemoteException {
Log.d(Constants.TAG, "decryptAndVerifyCallback"); Log.d(Constants.TAG, "decryptAndVerifyCallback");
@ -102,7 +167,7 @@ public class OpenPgpProviderActivity extends Activity {
@Override @Override
public void run() { public void run() {
mMessage.setText(new String(outputBytes)); mMessage.setText(output.getString());
if (signatureResult != null) { if (signatureResult != null) {
Toast.makeText(OpenPgpProviderActivity.this, Toast.makeText(OpenPgpProviderActivity.this,
"signature result:\n" + signatureResult.toString(), "signature result:\n" + signatureResult.toString(),
@ -135,43 +200,43 @@ public class OpenPgpProviderActivity extends Activity {
} }
public void encryptOnClick(View view) { public void encryptOnClick(View view) {
byte[] inputBytes = mMessage.getText().toString().getBytes();
try { try {
mCryptoServiceConnection.getService().encrypt(inputBytes, mCryptoServiceConnection.getService().getKeyIds(
mEncryptUserIds.getText().toString().split(","), true, encryptCallback); mEncryptUserIds.getText().toString().split(","), true, getKeysEncryptCallback);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoProviderDemo", e); Log.e(Constants.TAG, "CryptoProviderDemo", e);
} }
} }
public void signOnClick(View view) { public void signOnClick(View view) {
byte[] inputBytes = mMessage.getText().toString().getBytes(); String inputStr = mMessage.getText().toString();
OpenPgpData input = new OpenPgpData(inputStr);
try { try {
mCryptoServiceConnection.getService().sign(inputBytes, true, encryptCallback); mCryptoServiceConnection.getService().sign(input,
new OpenPgpData(OpenPgpData.TYPE_STRING), encryptCallback);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoProviderDemo", e); Log.e(Constants.TAG, "CryptoProviderDemo", e);
} }
} }
public void encryptAndSignOnClick(View view) { public void signAndEncryptOnClick(View view) {
byte[] inputBytes = mMessage.getText().toString().getBytes();
try { try {
mCryptoServiceConnection.getService().signAndEncrypt(inputBytes, mCryptoServiceConnection.getService().getKeyIds(
mEncryptUserIds.getText().toString().split(","), true, encryptCallback); mEncryptUserIds.getText().toString().split(","), true,
getKeysSignAndEncryptCallback);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoProviderDemo", e); Log.e(Constants.TAG, "CryptoProviderDemo", e);
} }
} }
public void decryptAndVerifyOnClick(View view) { public void decryptAndVerifyOnClick(View view) {
byte[] inputBytes = mCiphertext.getText().toString().getBytes(); String inputStr = mCiphertext.getText().toString();
OpenPgpData input = new OpenPgpData(inputStr);
try { try {
mCryptoServiceConnection.getService().decryptAndVerify(inputBytes, mCryptoServiceConnection.getService().decryptAndVerify(input,
decryptAndVerifyCallback); new OpenPgpData(OpenPgpData.TYPE_STRING), decryptAndVerifyCallback);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoProviderDemo", e); Log.e(Constants.TAG, "CryptoProviderDemo", e);
} }

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/import_clipboard_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/import_from_clipboard" />
</LinearLayout>

View File

@ -53,6 +53,7 @@
<item>@string/menu_importFromFile</item> <item>@string/menu_importFromFile</item>
<item>@string/menu_keyServer</item> <item>@string/menu_keyServer</item>
<item>@string/menu_importFromQrCode</item> <item>@string/menu_importFromQrCode</item>
<item>@string/import_from_clipboard</item>
<item>@string/menu_importFromNfc</item> <item>@string/menu_importFromNfc</item>
</string-array> </string-array>

View File

@ -351,6 +351,7 @@
<string name="import_import">Import selected keys</string> <string name="import_import">Import selected keys</string>
<string name="import_sign_and_upload">Import, Sign, and upload selected keys</string> <string name="import_sign_and_upload">Import, Sign, and upload selected keys</string>
<string name="import_finish">Finish</string> <string name="import_finish">Finish</string>
<string name="import_from_clipboard">Import from Clipboard</string>
<!-- Intent labels --> <!-- Intent labels -->
<string name="intent_decrypt_file">OpenPGP: Decrypt File</string> <string name="intent_decrypt_file">OpenPGP: Decrypt File</string>

View File

@ -16,6 +16,7 @@
package org.openintents.openpgp; package org.openintents.openpgp;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
@ -24,14 +25,14 @@ interface IOpenPgpCallback {
/** /**
* onSuccess returns on successful OpenPGP operations. * onSuccess returns on successful OpenPGP operations.
* *
* @param outputBytes * @param output
* contains resulting output bytes (decrypted content (when input was encrypted) * contains resulting output (decrypted content (when input was encrypted)
* or content without signature (when input was signed-only)) * or content without signature (when input was signed-only))
* @param signatureResult * @param signatureResult
* signatureResult is only non-null if decryptAndVerify() was called and the content * signatureResult is only non-null if decryptAndVerify() was called and the content
* was encrypted or signed-and-encrypted. * was encrypted or signed-and-encrypted.
*/ */
oneway void onSuccess(in byte[] outputBytes, in OpenPgpSignatureResult signatureResult); oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
/** /**
* onError returns on errors or when allowUserInteraction was set to false, but user interaction * onError returns on errors or when allowUserInteraction was set to false, but user interaction

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2013 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;
import org.openintents.openpgp.OpenPgpError;
interface IOpenPgpKeyIdsCallback {
/**
* onSuccess returns on successful getKeyIds operations.
*
* @param keyIds
* returned key ids
*/
oneway void onSuccess(in long[] keyIds);
/**
* onError returns on errors or when allowUserInteraction was set to false, but user interaction
* was required execute an OpenPGP operation.
*
* @param error
* See OpenPgpError class for more information.
*/
oneway void onError(in OpenPgpError error);
}

View File

@ -16,7 +16,9 @@
package org.openintents.openpgp; package org.openintents.openpgp;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.IOpenPgpCallback; import org.openintents.openpgp.IOpenPgpCallback;
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
/** /**
* All methods are oneway, which means they are asynchronous and non-blocking. * All methods are oneway, which means they are asynchronous and non-blocking.
@ -29,54 +31,76 @@ interface IOpenPgpService {
* *
* After successful encryption, callback's onSuccess will contain the resulting output bytes. * After successful encryption, callback's onSuccess will contain the resulting output bytes.
* *
* @param inputBytes * @param input
* Byte array you want to encrypt * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param encryptionUserIds * @param output
* User Ids (emails) of recipients * Request output format by defining OpenPgpData object
* @param asciiArmor *
* Encode result for ASCII (Radix-64, 33 percent overhead compared to binary) * new OpenPgpData(OpenPgpData.TYPE_STRING)
* @param allowUserInteraction * Returns as String
* Allows the OpenPGP Provider to handle missing keys by showing activities * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param keyIds
* Key Ids of recipients. Can be retrieved with getKeyIds()
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void encrypt(in byte[] inputBytes, in String[] encryptionUserIds, in boolean asciiArmor, oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
in IOpenPgpCallback callback);
/** /**
* Sign * Sign
* *
* After successful signing, callback's onSuccess will contain the resulting output bytes. * After successful signing, callback's onSuccess will contain the resulting output bytes.
* *
* @param inputBytes * @param input
* Byte array you want to sign * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param asciiArmor * @param output
* Encode result for ASCII (Radix-64, 33 percent overhead compared to binary) * Request output format by defining OpenPgpData object
* @param allowUserInteraction *
* Allows the OpenPGP Provider to handle missing keys by showing activities * new OpenPgpData(OpenPgpData.TYPE_STRING)
* Returns as String
* (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void sign(in byte[] inputBytes, in boolean asciiArmor, in IOpenPgpCallback callback); oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
/** /**
* Sign then encrypt * Sign then encrypt
* *
* After successful signing and encryption, callback's onSuccess will contain the resulting output bytes. * After successful signing and encryption, callback's onSuccess will contain the resulting output bytes.
* *
* @param inputBytes * @param input
* Byte array you want to sign and encrypt * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param encryptionUserIds * @param output
* User Ids (emails) of recipients * Request output format by defining OpenPgpData object
* @param asciiArmor *
* Encode result for ASCII (Radix-64, 33 percent overhead compared to binary) * new OpenPgpData(OpenPgpData.TYPE_STRING)
* @param allowUserInteraction * Returns as String
* Allows the OpenPGP Provider to handle missing keys by showing activities * (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param keyIds
* Key Ids of recipients. Can be retrieved with getKeyIds()
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void signAndEncrypt(in byte[] inputBytes, in String[] encryptionUserIds, in boolean asciiArmor, oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
in IOpenPgpCallback callback);
/** /**
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted, * Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
@ -85,15 +109,35 @@ interface IOpenPgpService {
* After successful decryption/verification, callback's onSuccess will contain the resulting output bytes. * After successful decryption/verification, callback's onSuccess will contain the resulting output bytes.
* The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given. * The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
* *
* @param inputBytes * @param input
* Byte array you want to decrypt and verify * OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
* @param allowUserInteraction * @param output
* Allows the OpenPGP Provider to handle missing keys by showing activities * Request output format by defining OpenPgpData object
*
* new OpenPgpData(OpenPgpData.TYPE_STRING)
* Returns as String
* (OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
* new OpenPgpData(OpenPgpData.TYPE_BYTE_ARRAY)
* Returns as byte[]
* new OpenPgpData(uri)
* Writes output to given Uri
* new OpenPgpData(fileDescriptor)
* Writes output to given ParcelFileDescriptor
* @param callback * @param callback
* Callback where to return results * Callback where to return results
*/ */
oneway void decryptAndVerify(in byte[] inputBytes, in IOpenPgpCallback callback); oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
boolean isKeyAvailable(in String[] userIds); /**
* Get available key ids based on given user ids
*
* @param ids
* User Ids (emails) of recipients OR key ids
* @param allowUserInteraction
* Enable user interaction to lookup and import unknown keys
* @param callback
* Callback where to return results (different type than callback in other functions!)
*/
oneway void getKeyIds(in String[] ids, in boolean allowUserInteraction, in IOpenPgpKeyIdsCallback callback);
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2013 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;
// Declare OpenPgpData so AIDL can find it and knows that it implements the parcelable protocol.
parcelable OpenPgpData;

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2013 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;
import android.net.Uri;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
public class OpenPgpData implements Parcelable {
public static final int TYPE_STRING = 0;
public static final int TYPE_BYTE_ARRAY = 1;
public static final int TYPE_FILE_DESCRIPTOR = 2;
public static final int TYPE_URI = 3;
int type;
String string;
byte[] bytes = new byte[0];
ParcelFileDescriptor fileDescriptor;
Uri uri;
public int getType() {
return type;
}
public String getString() {
return string;
}
public byte[] getBytes() {
return bytes;
}
public ParcelFileDescriptor getFileDescriptor() {
return fileDescriptor;
}
public Uri getUri() {
return uri;
}
public OpenPgpData() {
}
/**
* Not a real constructor. This can be used to define requested output type.
*
* @param type
*/
public OpenPgpData(int type) {
this.type = type;
}
public OpenPgpData(String string) {
this.string = string;
this.type = TYPE_STRING;
}
public OpenPgpData(byte[] bytes) {
this.bytes = bytes;
this.type = TYPE_BYTE_ARRAY;
}
public OpenPgpData(ParcelFileDescriptor fileDescriptor) {
this.fileDescriptor = fileDescriptor;
this.type = TYPE_FILE_DESCRIPTOR;
}
public OpenPgpData(Uri uri) {
this.uri = uri;
this.type = TYPE_URI;
}
public OpenPgpData(OpenPgpData b) {
this.string = b.string;
this.bytes = b.bytes;
this.fileDescriptor = b.fileDescriptor;
this.uri = b.uri;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(string);
dest.writeInt(bytes.length);
dest.writeByteArray(bytes);
dest.writeParcelable(fileDescriptor, 0);
dest.writeParcelable(uri, 0);
}
public static final Creator<OpenPgpData> CREATOR = new Creator<OpenPgpData>() {
public OpenPgpData createFromParcel(final Parcel source) {
OpenPgpData vr = new OpenPgpData();
vr.string = source.readString();
vr.bytes = new byte[source.readInt()];
source.readByteArray(vr.bytes);
vr.fileDescriptor = source.readParcelable(ParcelFileDescriptor.class.getClassLoader());
vr.fileDescriptor = source.readParcelable(Uri.class.getClassLoader());
return vr;
}
public OpenPgpData[] newArray(final int size) {
return new OpenPgpData[size];
}
};
}

View File

@ -25,40 +25,50 @@ public class OpenPgpSignatureResult implements Parcelable {
// successfully verified signature, with trusted public key // successfully verified signature, with trusted public key
public static final int SIGNATURE_SUCCESS_TRUSTED = 1; public static final int SIGNATURE_SUCCESS_TRUSTED = 1;
// no public key was found for this signature verification // no public key was found for this signature verification
// you can retrieve the key with
// getKeys(new String[] {String.valueOf(signatureResult.getKeyId)}, true, callback)
public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2; public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2;
// successfully verified signature, but with untrusted public key // successfully verified signature, but with untrusted public key
public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3; public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3;
int signatureStatus; int status;
String signatureUserId;
boolean signatureOnly; boolean signatureOnly;
String userId;
long keyId;
public int getSignatureStatus() { public int getStatus() {
return signatureStatus; return status;
}
public String getSignatureUserId() {
return signatureUserId;
} }
public boolean isSignatureOnly() { public boolean isSignatureOnly() {
return signatureOnly; return signatureOnly;
} }
public String getUserId() {
return userId;
}
public long getKeyId() {
return keyId;
}
public OpenPgpSignatureResult() { public OpenPgpSignatureResult() {
} }
public OpenPgpSignatureResult(int signatureStatus, String signatureUserId, boolean signatureOnly) { public OpenPgpSignatureResult(int signatureStatus, String signatureUserId,
this.signatureStatus = signatureStatus; boolean signatureOnly, long keyId) {
this.signatureUserId = signatureUserId; this.status = signatureStatus;
this.signatureOnly = signatureOnly; this.signatureOnly = signatureOnly;
this.userId = signatureUserId;
this.keyId = keyId;
} }
public OpenPgpSignatureResult(OpenPgpSignatureResult b) { public OpenPgpSignatureResult(OpenPgpSignatureResult b) {
this.signatureStatus = b.signatureStatus; this.status = b.status;
this.signatureUserId = b.signatureUserId; this.userId = b.userId;
this.signatureOnly = b.signatureOnly; this.signatureOnly = b.signatureOnly;
this.keyId = b.keyId;
} }
public int describeContents() { public int describeContents() {
@ -66,17 +76,19 @@ public class OpenPgpSignatureResult implements Parcelable {
} }
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(signatureStatus); dest.writeInt(status);
dest.writeString(signatureUserId);
dest.writeByte((byte) (signatureOnly ? 1 : 0)); dest.writeByte((byte) (signatureOnly ? 1 : 0));
dest.writeString(userId);
dest.writeLong(keyId);
} }
public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() { public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() {
public OpenPgpSignatureResult createFromParcel(final Parcel source) { public OpenPgpSignatureResult createFromParcel(final Parcel source) {
OpenPgpSignatureResult vr = new OpenPgpSignatureResult(); OpenPgpSignatureResult vr = new OpenPgpSignatureResult();
vr.signatureStatus = source.readInt(); vr.status = source.readInt();
vr.signatureUserId = source.readString();
vr.signatureOnly = source.readByte() == 1; vr.signatureOnly = source.readByte() == 1;
vr.userId = source.readString();
vr.keyId = source.readLong();
return vr; return vr;
} }
@ -88,9 +100,10 @@ public class OpenPgpSignatureResult implements Parcelable {
@Override @Override
public String toString() { public String toString() {
String out = new String(); String out = new String();
out += "\nsignatureStatus: " + signatureStatus; out += "\nstatus: " + status;
out += "\nsignatureUserId: " + signatureUserId; out += "\nuserId: " + userId;
out += "\nsignatureOnly: " + signatureOnly; out += "\nsignatureOnly: " + signatureOnly;
out += "\nkeyId: " + keyId;
return out; return out;
} }

View File

@ -25,9 +25,12 @@ import java.util.ArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import org.openintents.openpgp.IOpenPgpCallback; import org.openintents.openpgp.IOpenPgpCallback;
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
import org.openintents.openpgp.IOpenPgpService; import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
@ -114,8 +117,8 @@ public class OpenPgpService extends RemoteService {
* @param encryptionUserIds * @param encryptionUserIds
* @return * @return
*/ */
private long[] getKeyIdsFromEmails(String[] encryptionUserIds, long ownKeyId, private long[] getKeyIdsFromEmails(String[] encryptionUserIds, boolean allowUserInteraction)
boolean allowUserInteraction) throws UserInteractionRequiredException { throws UserInteractionRequiredException {
// find key ids to given emails in database // find key ids to given emails in database
ArrayList<Long> keyIds = new ArrayList<Long>(); ArrayList<Long> keyIds = new ArrayList<Long>();
@ -142,9 +145,6 @@ public class OpenPgpService extends RemoteService {
} }
} }
// also encrypt to our self (so that we can decrypt it later!)
keyIds.add(ownKeyId);
// convert to long[] // convert to long[]
long[] keyIdsArray = new long[keyIds.size()]; long[] keyIdsArray = new long[keyIds.size()];
for (int i = 0; i < keyIdsArray.length; i++) { for (int i = 0; i < keyIdsArray.length; i++) {
@ -215,24 +215,47 @@ public class OpenPgpService extends RemoteService {
} }
}; };
private synchronized void encryptAndSignSafe(byte[] inputBytes, String[] encryptionUserIds, private synchronized void getKeyIdsSafe(String[] userIds, boolean allowUserInteraction,
boolean asciiArmor, boolean allowUserInteraction, IOpenPgpCallback callback, IOpenPgpKeyIdsCallback callback, AppSettings appSettings) {
AppSettings appSettings, boolean sign) {
try { try {
// build InputData and write into OutputStream long[] keyIds = getKeyIdsFromEmails(userIds, allowUserInteraction);
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputData = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
long[] keyIds = getKeyIdsFromEmails(encryptionUserIds, appSettings.getKeyId(),
allowUserInteraction);
if (keyIds == null) { if (keyIds == null) {
throw new NoUserIdsException("No user ids!"); throw new NoUserIdsException("No user ids!");
} }
PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream); callback.onSuccess(keyIds);
} catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (NoUserIdsException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_USER_IDS, e.getMessage());
} catch (Exception e) {
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
}
}
private synchronized void encryptAndSignSafe(OpenPgpData inputData,
final OpenPgpData outputData, long[] keyIds, boolean allowUserInteraction,
IOpenPgpCallback callback, AppSettings appSettings, boolean sign) {
try {
// TODO: other options of OpenPgpData!
byte[] inputBytes = getInput(inputData);
boolean asciiArmor = false;
if (outputData.getType() == OpenPgpData.TYPE_STRING) {
asciiArmor = true;
}
// add own key for encryption
keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
keyIds[keyIds.length - 1] = appSettings.getKeyId();
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputDt = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
PgpOperation operation = new PgpOperation(getContext(), null, inputDt, outputStream);
if (sign) { if (sign) {
String passphrase = getCachedPassphrase(appSettings.getKeyId(), String passphrase = getCachedPassphrase(appSettings.getKeyId(),
allowUserInteraction); allowUserInteraction);
@ -253,12 +276,17 @@ public class OpenPgpService extends RemoteService {
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
OpenPgpData output = null;
if (asciiArmor) {
output = new OpenPgpData(new String(outputBytes));
} else {
output = new OpenPgpData(outputBytes);
}
// return over handler on client side // return over handler on client side
callback.onSuccess(outputBytes, null); callback.onSuccess(output, null);
} catch (UserInteractionRequiredException e) { } catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage()); callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (NoUserIdsException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_USER_IDS, e.getMessage());
} catch (WrongPassphraseException e) { } catch (WrongPassphraseException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage()); callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
} catch (Exception e) { } catch (Exception e) {
@ -289,9 +317,10 @@ public class OpenPgpService extends RemoteService {
outputStream.close(); outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
OpenPgpData output = new OpenPgpData(new String(outputBytes));
// return over handler on client side // return over handler on client side
callback.onSuccess(outputBytes, null); callback.onSuccess(output, null);
} catch (UserInteractionRequiredException e) { } catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage()); callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (WrongPassphraseException e) { } catch (WrongPassphraseException e) {
@ -406,8 +435,8 @@ public class OpenPgpService extends RemoteService {
OpenPgpSignatureResult sigResult = null; OpenPgpSignatureResult sigResult = null;
if (signature) { if (signature) {
// long signatureKeyId = outputBundle long signatureKeyId = outputBundle
// .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
String signatureUserId = outputBundle String signatureUserId = outputBundle
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
boolean signatureSuccess = outputBundle boolean signatureSuccess = outputBundle
@ -422,11 +451,13 @@ public class OpenPgpService extends RemoteService {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY; signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
} }
sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId, signedOnly); sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
signedOnly, signatureKeyId);
} }
OpenPgpData output = new OpenPgpData(new String(outputBytes));
// return over handler on client side // return over handler on client side
callback.onSuccess(outputBytes, sigResult); callback.onSuccess(output, sigResult);
} catch (UserInteractionRequiredException e) { } catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage()); callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (WrongPassphraseException e) { } catch (WrongPassphraseException e) {
@ -452,49 +483,26 @@ public class OpenPgpService extends RemoteService {
} }
} }
private void callbackOpenPgpError(IOpenPgpKeyIdsCallback callback, int errorId, String message) {
try {
callback.onError(new OpenPgpError(0, message));
} catch (Exception t) {
Log.e(Constants.TAG,
"Exception while returning OpenPgpError to client via callback.onError()", t);
}
}
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
@Override @Override
public void encrypt(final byte[] inputBytes, final String[] encryptionUserIds, public void encrypt(final OpenPgpData input, final OpenPgpData output, final long[] keyIds,
final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, true, callback,
settings, false);
}
};
checkAndEnqueue(r);
}
@Override
public void signAndEncrypt(final byte[] inputBytes, final String[] encryptionUserIds,
final boolean asciiArmor, final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
encryptAndSignSafe(inputBytes, encryptionUserIds, asciiArmor, true, callback,
settings, true);
}
};
checkAndEnqueue(r);
}
@Override
public void sign(final byte[] inputBytes, boolean asciiArmor,
final IOpenPgpCallback callback) throws RemoteException { final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings(); final AppSettings settings = getAppSettings();
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
signSafe(inputBytes, true, callback, settings); encryptAndSignSafe(input, output, keyIds, true, callback, settings, false);
} }
}; };
@ -502,15 +510,45 @@ public class OpenPgpService extends RemoteService {
} }
@Override @Override
public void decryptAndVerify(final byte[] inputBytes, final IOpenPgpCallback callback) public void signAndEncrypt(final OpenPgpData input, final OpenPgpData output,
throws RemoteException { final long[] keyIds, final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
encryptAndSignSafe(input, output, keyIds, true, callback, settings, true);
}
};
checkAndEnqueue(r);
}
@Override
public void sign(final OpenPgpData input, final OpenPgpData output,
final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
signSafe(getInput(input), true, callback, settings);
}
};
checkAndEnqueue(r);
}
@Override
public void decryptAndVerify(final OpenPgpData input, final OpenPgpData output,
final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings(); final AppSettings settings = getAppSettings();
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
decryptAndVerifySafe(inputBytes, true, callback, settings); decryptAndVerifySafe(getInput(input), true, callback, settings);
} }
}; };
@ -518,13 +556,44 @@ public class OpenPgpService extends RemoteService {
} }
@Override @Override
public boolean isKeyAvailable(String[] userIds) throws RemoteException { public void getKeyIds(final String[] userIds, final boolean allowUserInteraction,
// TODO final IOpenPgpKeyIdsCallback callback) throws RemoteException {
return false;
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
getKeyIdsSafe(userIds, allowUserInteraction, callback, settings);
}
};
checkAndEnqueue(r);
} }
}; };
private static byte[] getInput(OpenPgpData data) {
// TODO: support Uri and ParcelFileDescriptor
byte[] inBytes = null;
switch (data.getType()) {
case OpenPgpData.TYPE_STRING:
inBytes = data.getString().getBytes();
break;
case OpenPgpData.TYPE_BYTE_ARRAY:
inBytes = data.getBytes();
break;
default:
Log.e(Constants.TAG, "Uri and ParcelFileDescriptor not supported right now!");
break;
}
return inBytes;
}
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return mBinder; return mBinder;

View File

@ -166,7 +166,7 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
mListFragment = ImportKeysListFragment.newInstance(bytes, filename); mListFragment = ImportKeysListFragment.newInstance(bytes, filename);
// Add the fragment to the 'fragment_container' FrameLayout // Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent wierd crashes! // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(R.id.import_keys_list_container, mListFragment) .replace(R.id.import_keys_list_container, mListFragment)
.commitAllowingStateLoss(); .commitAllowingStateLoss();
@ -189,6 +189,9 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[itemPosition]); loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[itemPosition]);
break; break;
case 3: case 3:
loadFragment(ImportKeysClipboardFragment.class, null, mNavigationStrings[itemPosition]);
break;
case 4:
loadFragment(ImportKeysNFCFragment.class, null, mNavigationStrings[itemPosition]); loadFragment(ImportKeysNFCFragment.class, null, mNavigationStrings[itemPosition]);
break; break;

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
public class ImportKeysClipboardFragment extends Fragment {
private ImportKeysActivity mImportActivity;
private Button mButton;
/**
* Creates new instance of this fragment
*/
public static ImportKeysClipboardFragment newInstance() {
ImportKeysClipboardFragment frag = new ImportKeysClipboardFragment();
Bundle args = new Bundle();
frag.setArguments(args);
return frag;
}
/**
* Inflate the layout for this fragment
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_clipboard_fragment, container, false);
mButton = (Button) view.findViewById(R.id.import_clipboard_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
mImportActivity.loadCallback(clipboardText.toString().getBytes(), null);
}
});
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mImportActivity = (ImportKeysActivity) getActivity();
}
}