master merge
14
CHANGELOG
@ -1,3 +1,17 @@
|
|||||||
|
2.3.1
|
||||||
|
* hotfix for crash when upgrading from old versions
|
||||||
|
|
||||||
|
2.3
|
||||||
|
* remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
|
||||||
|
* fix setting expiry dates on keys (thanks to Ash Hughes)
|
||||||
|
* more internal fixes when editing keys (thanks to Ash Hughes)
|
||||||
|
* querying keyservers directly from the import screen
|
||||||
|
* fix layout and dialog style on Android 2.2-3.0
|
||||||
|
* fix crash on keys with empty user ids
|
||||||
|
* fix crash and empty lists when coming back from signing screen
|
||||||
|
* Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
|
||||||
|
* fix upload of key from signing screen
|
||||||
|
|
||||||
2.2
|
2.2
|
||||||
* New design with navigation drawer
|
* New design with navigation drawer
|
||||||
* New public key list design
|
* New public key list design
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
|
|
||||||
interface IOpenPgpCallback {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* onSuccess returns on successful OpenPGP operations.
|
|
||||||
*
|
|
||||||
* @param output
|
|
||||||
* contains resulting output (decrypted content (when input was encrypted)
|
|
||||||
* or content without signature (when input was signed-only))
|
|
||||||
* @param signatureResult
|
|
||||||
* signatureResult is only non-null if decryptAndVerify() was called and the content
|
|
||||||
* was encrypted or signed-and-encrypted.
|
|
||||||
*/
|
|
||||||
oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.IOpenPgpCallback;
|
|
||||||
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All methods are oneway, which means they are asynchronous and non-blocking.
|
|
||||||
* Results are returned to the callback, which has to be implemented on client side.
|
|
||||||
*/
|
|
||||||
interface IOpenPgpService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign
|
|
||||||
*
|
|
||||||
* After successful signing, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt
|
|
||||||
*
|
|
||||||
* After successful encryption, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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 keyIds
|
|
||||||
* Key Ids of recipients. Can be retrieved with getKeyIds()
|
|
||||||
* @param callback
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign then encrypt
|
|
||||||
*
|
|
||||||
* After successful signing and encryption, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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 keyIds
|
|
||||||
* Key Ids of recipients. Can be retrieved with getKeyIds()
|
|
||||||
* @param callback
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
|
|
||||||
* and also signed-only input.
|
|
||||||
*
|
|
||||||
* After successful decryption/verification, callback's onSuccess will contain the resulting output.
|
|
||||||
* The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OpenPgpError so AIDL can find it and knows that it implements the parcelable protocol.
|
|
||||||
parcelable OpenPgpError;
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OpenPgpSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
|
|
||||||
parcelable OpenPgpSignatureResult;
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
|
|
||||||
interface IOpenPgpCallback {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* onSuccess returns on successful OpenPGP operations.
|
|
||||||
*
|
|
||||||
* @param output
|
|
||||||
* contains resulting output (decrypted content (when input was encrypted)
|
|
||||||
* or content without signature (when input was signed-only))
|
|
||||||
* @param signatureResult
|
|
||||||
* signatureResult is only non-null if decryptAndVerify() was called and the content
|
|
||||||
* was encrypted or signed-and-encrypted.
|
|
||||||
*/
|
|
||||||
oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.IOpenPgpCallback;
|
|
||||||
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All methods are oneway, which means they are asynchronous and non-blocking.
|
|
||||||
* Results are returned to the callback, which has to be implemented on client side.
|
|
||||||
*/
|
|
||||||
interface IOpenPgpService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign
|
|
||||||
*
|
|
||||||
* After successful signing, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt
|
|
||||||
*
|
|
||||||
* After successful encryption, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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 keyIds
|
|
||||||
* Key Ids of recipients. Can be retrieved with getKeyIds()
|
|
||||||
* @param callback
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign then encrypt
|
|
||||||
*
|
|
||||||
* After successful signing and encryption, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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 keyIds
|
|
||||||
* Key Ids of recipients. Can be retrieved with getKeyIds()
|
|
||||||
* @param callback
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
|
|
||||||
* and also signed-only input.
|
|
||||||
*
|
|
||||||
* After successful decryption/verification, callback's onSuccess will contain the resulting output.
|
|
||||||
* The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.openintents.openpgp;
|
|
||||||
|
|
||||||
public class OpenPgpConstants {
|
|
||||||
|
|
||||||
public static final String TAG = "OpenPgp API";
|
|
||||||
|
|
||||||
public static final int REQUIRED_API_VERSION = 1;
|
|
||||||
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.writeInt(type);
|
|
||||||
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.type = source.readInt();
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OpenPgpError so AIDL can find it and knows that it implements the parcelable protocol.
|
|
||||||
parcelable OpenPgpError;
|
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
|
|
||||||
public class OpenPgpHelper {
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public static Pattern PGP_MESSAGE = Pattern.compile(
|
|
||||||
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
|
|
||||||
|
|
||||||
public static Pattern PGP_SIGNED_MESSAGE = Pattern
|
|
||||||
.compile(
|
|
||||||
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
|
||||||
Pattern.DOTALL);
|
|
||||||
|
|
||||||
public OpenPgpHelper(Context context) {
|
|
||||||
super();
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAvailable() {
|
|
||||||
Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
|
|
||||||
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
|
|
||||||
if (!resInfo.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OpenPgpSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
|
|
||||||
parcelable OpenPgpSignatureResult;
|
|
@ -1,168 +0,0 @@
|
|||||||
///*
|
|
||||||
// * Copyright (C) 2012 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.sufficientlysecure.keychain.demo;
|
|
||||||
//
|
|
||||||
//import java.util.ArrayList;
|
|
||||||
//import java.util.List;
|
|
||||||
//
|
|
||||||
//import org.sufficientlysecure.keychain.demo.R;
|
|
||||||
//import org.sufficientlysecure.keychain.integration.KeychainData;
|
|
||||||
//import org.sufficientlysecure.keychain.integration.KeychainIntentHelper;
|
|
||||||
//import org.sufficientlysecure.keychain.service.IKeychainKeyService;
|
|
||||||
//import org.sufficientlysecure.keychain.service.handler.IKeychainGetKeyringsHandler;
|
|
||||||
//
|
|
||||||
//import android.annotation.SuppressLint;
|
|
||||||
//import android.app.Activity;
|
|
||||||
//import android.app.AlertDialog;
|
|
||||||
//import android.content.ComponentName;
|
|
||||||
//import android.content.Context;
|
|
||||||
//import android.content.Intent;
|
|
||||||
//import android.content.ServiceConnection;
|
|
||||||
//import android.os.Bundle;
|
|
||||||
//import android.os.IBinder;
|
|
||||||
//import android.os.RemoteException;
|
|
||||||
//import android.util.Base64;
|
|
||||||
//import android.view.View;
|
|
||||||
//import android.widget.TextView;
|
|
||||||
//
|
|
||||||
//public class AidlDemoActivity2 extends Activity {
|
|
||||||
// Activity mActivity;
|
|
||||||
//
|
|
||||||
// TextView mKeyringsTextView;
|
|
||||||
//
|
|
||||||
// KeychainIntentHelper mKeychainIntentHelper;
|
|
||||||
// KeychainData mKeychainData;
|
|
||||||
//
|
|
||||||
// byte[] keysBytes;
|
|
||||||
// ArrayList<String> keysStrings;
|
|
||||||
//
|
|
||||||
// private IKeychainKeyService service = null;
|
|
||||||
// private ServiceConnection svcConn = new ServiceConnection() {
|
|
||||||
// public void onServiceConnected(ComponentName className, IBinder binder) {
|
|
||||||
// service = IKeychainKeyService.Stub.asInterface(binder);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void onServiceDisconnected(ComponentName className) {
|
|
||||||
// service = null;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onCreate(Bundle icicle) {
|
|
||||||
// super.onCreate(icicle);
|
|
||||||
// setContentView(R.layout.aidl_demo2);
|
|
||||||
//
|
|
||||||
// mActivity = this;
|
|
||||||
//
|
|
||||||
// mKeyringsTextView = (TextView) findViewById(R.id.aidl_demo_keyrings);
|
|
||||||
//
|
|
||||||
// mKeychainIntentHelper = new KeychainIntentHelper(mActivity);
|
|
||||||
// mKeychainData = new KeychainData();
|
|
||||||
//
|
|
||||||
// bindService(new Intent(IKeychainKeyService.class.getName()), svcConn,
|
|
||||||
// Context.BIND_AUTO_CREATE);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void getKeyringsStringsOnClick(View view) {
|
|
||||||
// try {
|
|
||||||
// service.getPublicKeyRings(mKeychainData.getPublicKeys(), true, getKeyringsHandler);
|
|
||||||
// } catch (RemoteException e) {
|
|
||||||
// exceptionImplementation(-1, e.toString());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void getKeyringsBytesOnClick(View view) {
|
|
||||||
// try {
|
|
||||||
// service.getPublicKeyRings(mKeychainData.getPublicKeys(), false, getKeyringsHandler);
|
|
||||||
// } catch (RemoteException e) {
|
|
||||||
// exceptionImplementation(-1, e.toString());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @SuppressLint("NewApi")
|
|
||||||
// private void updateView() {
|
|
||||||
// if (keysBytes != null) {
|
|
||||||
// mKeyringsTextView.setText(Base64.encodeToString(keysBytes, Base64.DEFAULT));
|
|
||||||
// } else if (keysStrings != null) {
|
|
||||||
// mKeyringsTextView.setText("");
|
|
||||||
// for (String output : keysStrings) {
|
|
||||||
// mKeyringsTextView.append(output);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onDestroy() {
|
|
||||||
// super.onDestroy();
|
|
||||||
//
|
|
||||||
// unbindService(svcConn);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private void exceptionImplementation(int exceptionId, String error) {
|
|
||||||
// AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
// builder.setTitle("Exception!").setMessage(error).setPositiveButton("OK", null).show();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private final IKeychainGetKeyringsHandler.Stub getKeyringsHandler = new IKeychainGetKeyringsHandler.Stub() {
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onException(final int exceptionId, final String message) throws RemoteException {
|
|
||||||
// runOnUiThread(new Runnable() {
|
|
||||||
// public void run() {
|
|
||||||
// exceptionImplementation(exceptionId, message);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onSuccess(final byte[] outputBytes, final List<String> outputStrings)
|
|
||||||
// throws RemoteException {
|
|
||||||
// runOnUiThread(new Runnable() {
|
|
||||||
// public void run() {
|
|
||||||
// if (outputBytes != null) {
|
|
||||||
// keysBytes = outputBytes;
|
|
||||||
// keysStrings = null;
|
|
||||||
// } else if (outputStrings != null) {
|
|
||||||
// keysBytes = null;
|
|
||||||
// keysStrings = (ArrayList<String>) outputStrings;
|
|
||||||
// }
|
|
||||||
// updateView();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// public void selectEncryptionKeysOnClick(View view) {
|
|
||||||
// mKeychainIntentHelper.selectPublicKeys("user@example.com");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
||||||
// // this updates the mKeychainData object to the result of the methods
|
|
||||||
// boolean result = mKeychainIntentHelper.onActivityResult(requestCode, resultCode, data,
|
|
||||||
// mKeychainData);
|
|
||||||
// if (result) {
|
|
||||||
// updateView();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // continue with other activity results
|
|
||||||
// super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
@ -1,352 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.sufficientlysecure.keychain.demo;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
|
|
||||||
import org.openintents.openpgp.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
import org.openintents.openpgp.OpenPgpServiceConnection;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.openintents.openpgp.IOpenPgpCallback;
|
|
||||||
import org.openintents.openpgp.IOpenPgpService;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ListAdapter;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
public class OpenPgpProviderActivity extends Activity {
|
|
||||||
Activity mActivity;
|
|
||||||
|
|
||||||
EditText mMessage;
|
|
||||||
EditText mCiphertext;
|
|
||||||
EditText mEncryptUserIds;
|
|
||||||
|
|
||||||
private OpenPgpServiceConnection mCryptoServiceConnection;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle icicle) {
|
|
||||||
super.onCreate(icicle);
|
|
||||||
setContentView(R.layout.crypto_provider_demo);
|
|
||||||
|
|
||||||
mActivity = this;
|
|
||||||
|
|
||||||
mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message);
|
|
||||||
mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext);
|
|
||||||
mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id);
|
|
||||||
|
|
||||||
selectCryptoProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final OpenPgpData output, OpenPgpSignatureResult signatureResult)
|
|
||||||
throws RemoteException {
|
|
||||||
Log.d(Constants.TAG, "encryptCallback");
|
|
||||||
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mCiphertext.setText(output.getString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(OpenPgpError error) throws RemoteException {
|
|
||||||
handleError(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
final IOpenPgpCallback.Stub decryptAndVerifyCallback = new IOpenPgpCallback.Stub() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(final OpenPgpData output, final OpenPgpSignatureResult signatureResult)
|
|
||||||
throws RemoteException {
|
|
||||||
Log.d(Constants.TAG, "decryptAndVerifyCallback");
|
|
||||||
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mMessage.setText(output.getString());
|
|
||||||
if (signatureResult != null) {
|
|
||||||
Toast.makeText(OpenPgpProviderActivity.this,
|
|
||||||
"signature result:\n" + signatureResult.toString(),
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(OpenPgpError error) throws RemoteException {
|
|
||||||
handleError(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
private void handleError(final OpenPgpError error) {
|
|
||||||
mActivity.runOnUiThread(new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(mActivity,
|
|
||||||
"onError id:" + error.getErrorId() + "\n\n" + error.getMessage(),
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
|
|
||||||
Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void encryptOnClick(View view) {
|
|
||||||
try {
|
|
||||||
mCryptoServiceConnection.getService().getKeyIds(
|
|
||||||
mEncryptUserIds.getText().toString().split(","), true, getKeysEncryptCallback);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoProviderDemo", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void signOnClick(View view) {
|
|
||||||
String inputStr = mMessage.getText().toString();
|
|
||||||
OpenPgpData input = new OpenPgpData(inputStr);
|
|
||||||
|
|
||||||
try {
|
|
||||||
mCryptoServiceConnection.getService().sign(input,
|
|
||||||
new OpenPgpData(OpenPgpData.TYPE_STRING), encryptCallback);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoProviderDemo", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void signAndEncryptOnClick(View view) {
|
|
||||||
try {
|
|
||||||
mCryptoServiceConnection.getService().getKeyIds(
|
|
||||||
mEncryptUserIds.getText().toString().split(","), true,
|
|
||||||
getKeysSignAndEncryptCallback);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoProviderDemo", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void decryptAndVerifyOnClick(View view) {
|
|
||||||
String inputStr = mCiphertext.getText().toString();
|
|
||||||
OpenPgpData input = new OpenPgpData(inputStr);
|
|
||||||
|
|
||||||
try {
|
|
||||||
mCryptoServiceConnection.getService().decryptAndVerify(input,
|
|
||||||
new OpenPgpData(OpenPgpData.TYPE_STRING), decryptAndVerifyCallback);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoProviderDemo", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
|
|
||||||
if (mCryptoServiceConnection != null) {
|
|
||||||
mCryptoServiceConnection.unbindFromService();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class OpenPgpProviderElement {
|
|
||||||
private String packageName;
|
|
||||||
private String simpleName;
|
|
||||||
private Drawable icon;
|
|
||||||
|
|
||||||
public OpenPgpProviderElement(String packageName, String simpleName, Drawable icon) {
|
|
||||||
this.packageName = packageName;
|
|
||||||
this.simpleName = simpleName;
|
|
||||||
this.icon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return simpleName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectCryptoProvider() {
|
|
||||||
Intent intent = new Intent(IOpenPgpService.class.getName());
|
|
||||||
|
|
||||||
final ArrayList<OpenPgpProviderElement> providerList = new ArrayList<OpenPgpProviderElement>();
|
|
||||||
|
|
||||||
List<ResolveInfo> resInfo = getPackageManager().queryIntentServices(intent, 0);
|
|
||||||
if (!resInfo.isEmpty()) {
|
|
||||||
for (ResolveInfo resolveInfo : resInfo) {
|
|
||||||
if (resolveInfo.serviceInfo == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String packageName = resolveInfo.serviceInfo.packageName;
|
|
||||||
String simpleName = String.valueOf(resolveInfo.serviceInfo
|
|
||||||
.loadLabel(getPackageManager()));
|
|
||||||
Drawable icon = resolveInfo.serviceInfo.loadIcon(getPackageManager());
|
|
||||||
providerList.add(new OpenPgpProviderElement(packageName, simpleName, icon));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AlertDialog.Builder alert = new AlertDialog.Builder(this);
|
|
||||||
alert.setTitle("Select OpenPGP Provider!");
|
|
||||||
alert.setCancelable(false);
|
|
||||||
|
|
||||||
if (!providerList.isEmpty()) {
|
|
||||||
// add "disable OpenPGP provider"
|
|
||||||
providerList.add(0, new OpenPgpProviderElement(null, "Disable OpenPGP Provider",
|
|
||||||
getResources().getDrawable(android.R.drawable.ic_menu_close_clear_cancel)));
|
|
||||||
|
|
||||||
// Init ArrayAdapter with OpenPGP Providers
|
|
||||||
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderElement>(this,
|
|
||||||
android.R.layout.select_dialog_item, android.R.id.text1, providerList) {
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
// User super class to create the View
|
|
||||||
View v = super.getView(position, convertView, parent);
|
|
||||||
TextView tv = (TextView) v.findViewById(android.R.id.text1);
|
|
||||||
|
|
||||||
// Put the image on the TextView
|
|
||||||
tv.setCompoundDrawablesWithIntrinsicBounds(providerList.get(position).icon,
|
|
||||||
null, null, null);
|
|
||||||
|
|
||||||
// Add margin between image and text (support various screen densities)
|
|
||||||
int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
|
|
||||||
tv.setCompoundDrawablePadding(dp5);
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
alert.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
|
|
||||||
|
|
||||||
public void onClick(DialogInterface dialog, int position) {
|
|
||||||
String packageName = providerList.get(position).packageName;
|
|
||||||
|
|
||||||
if (packageName == null) {
|
|
||||||
dialog.cancel();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind to service
|
|
||||||
mCryptoServiceConnection = new OpenPgpServiceConnection(
|
|
||||||
OpenPgpProviderActivity.this, packageName);
|
|
||||||
mCryptoServiceConnection.bindToService();
|
|
||||||
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
alert.setMessage("No OpenPGP Provider installed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dialog.cancel();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AlertDialog ad = alert.create();
|
|
||||||
ad.show();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" >
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/aidl_demo_select_encryption_key"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="selectEncryptionKeysOnClick"
|
|
||||||
android:text="Select encryption key(s)" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/aidl_demo_keyrings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="keyrings output"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/aidl_demo_get_keyrings_strings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="getKeyringsStringsOnClick"
|
|
||||||
android:text="getKeyrings as Strings" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/aidl_demo_get_keyrings_bytes"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="getKeyringsBytesOnClick"
|
|
||||||
android:text="getKeyringsBytes" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
@ -1,83 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Encrypt UserIds (split with ',')"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/crypto_provider_demo_encrypt_user_id"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="dominik@dominikschuermann.de"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Message"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/crypto_provider_demo_message"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="100dip"
|
|
||||||
android:scrollHorizontally="true"
|
|
||||||
android:scrollbars="vertical"
|
|
||||||
android:text="message"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Ciphertext"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/crypto_provider_demo_ciphertext"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="100dip"
|
|
||||||
android:text="ciphertext"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" >
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/crypto_provider_demo_encrypt"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:onClick="encryptOnClick"
|
|
||||||
android:text="Encrypt" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/crypto_provider_demo_sign"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:onClick="signOnClick"
|
|
||||||
android:text="Sign" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/crypto_provider_demo_encrypt_and_sign"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="signAndEncryptOnClick"
|
|
||||||
android:text="Sign and Encrypt" />
|
|
||||||
</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>
|
|
@ -1,93 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" >
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical" >
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/Button02"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="encryptOnClick"
|
|
||||||
android:text="Encrypt" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_create_new_key"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="createNewKeyOnClick"
|
|
||||||
android:text="Create new key" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_select_secret_key"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="selectSecretKeyOnClick"
|
|
||||||
android:text="Select secret key" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_select_encryption_key"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="selectEncryptionKeysOnClick"
|
|
||||||
android:text="Select encryption key(s)" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/intent_demo_message"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="150dip"
|
|
||||||
android:text="message"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/intent_demo_ciphertext"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="150dip"
|
|
||||||
android:text="ciphertext"
|
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_encrypt"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="encryptOnClick"
|
|
||||||
android:text="Encrypt" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_encrypt"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="encryptAndReturnOnClick"
|
|
||||||
android:text="Encrypt and return result" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_decrypt"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="decryptOnClick"
|
|
||||||
android:text="Decrypt" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/intent_demo_decrypt"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="decryptAndReturnOnClick"
|
|
||||||
android:text="Decrypt and return result" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="APG Data:" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/intent_demo_data"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:minLines="10" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
3
OpenPGP-Keychain-API/build.gradle
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
task wrapper(type: Wrapper) {
|
||||||
|
gradleVersion = '1.10'
|
||||||
|
}
|
@ -1,21 +1,19 @@
|
|||||||
|
// please leave this here, so this library builds on its own
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:0.8.0'
|
classpath 'com.android.tools.build:gradle:0.8.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'android'
|
apply plugin: 'android'
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.android.support:support-v4:19.0.1'
|
compile 'com.android.support:support-v4:19.0.1'
|
||||||
|
compile project(':libraries:keychain-api-library')
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -23,7 +21,7 @@ android {
|
|||||||
buildToolsVersion "19.0.1"
|
buildToolsVersion "19.0.1"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 8
|
minSdkVersion 9
|
||||||
targetSdkVersion 19
|
targetSdkVersion 19
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@ -5,16 +5,16 @@
|
|||||||
android:versionName="1.1">
|
android:versionName="1.1">
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="8"
|
android:minSdkVersion="9"
|
||||||
android:targetSdkVersion="19" />
|
android:targetSdkVersion="19" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="OpenPGP Keychain API Demo" >
|
android:label="OpenKeychain API Demo">
|
||||||
<activity
|
<activity
|
||||||
android:name=".BaseActivity"
|
android:name=".BaseActivity"
|
||||||
android:label="OpenPGP Keychain API Demo" >
|
android:label="OpenKeychain API Demo">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
@ -26,9 +26,8 @@
|
|||||||
android:label="OpenPGP Provider"
|
android:label="OpenPGP Provider"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".AidlDemoActivity2"
|
android:name=".IntentActivity"
|
||||||
android:label="Aidl Demo (ACCESS_KEYS permission)"
|
android:label="Intents" />
|
||||||
android:windowSoftInputMode="stateHidden" />
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.demo;
|
package org.sufficientlysecure.keychain.demo;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.demo.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
@ -27,13 +24,8 @@ import android.preference.PreferenceActivity;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
public class BaseActivity extends PreferenceActivity {
|
public class BaseActivity extends PreferenceActivity {
|
||||||
private Activity mActivity;
|
|
||||||
|
|
||||||
private Preference mIntentDemo;
|
private Preference mIntentDemo;
|
||||||
private Preference mContentProviderDemo;
|
|
||||||
private Preference mCryptoProvider;
|
private Preference mCryptoProvider;
|
||||||
private Preference mAidlDemo;
|
|
||||||
private Preference mAidlDemo2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the activity is first created.
|
* Called when the activity is first created.
|
||||||
@ -42,23 +34,17 @@ public class BaseActivity extends PreferenceActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
mActivity = this;
|
|
||||||
|
|
||||||
// load preferences from xml
|
// load preferences from xml
|
||||||
addPreferencesFromResource(R.xml.base_preference);
|
addPreferencesFromResource(R.xml.base_preference);
|
||||||
|
|
||||||
// find preferences
|
// find preferences
|
||||||
mIntentDemo = (Preference) findPreference("intent_demo");
|
mIntentDemo = (Preference) findPreference("intent_demo");
|
||||||
mContentProviderDemo = (Preference) findPreference("content_provider_demo");
|
|
||||||
mCryptoProvider = (Preference) findPreference("openpgp_provider_demo");
|
mCryptoProvider = (Preference) findPreference("openpgp_provider_demo");
|
||||||
mAidlDemo = (Preference) findPreference("aidl_demo");
|
|
||||||
mAidlDemo2 = (Preference) findPreference("aidl_demo2");
|
|
||||||
|
|
||||||
mIntentDemo.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
mIntentDemo.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
// startActivity(new Intent(mActivity, IntentDemoActivity.class));
|
startActivity(new Intent(BaseActivity.this, IntentActivity.class));
|
||||||
Toast.makeText(BaseActivity.this, "Not implemented!", Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -67,21 +53,11 @@ public class BaseActivity extends PreferenceActivity {
|
|||||||
mCryptoProvider.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
mCryptoProvider.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
startActivity(new Intent(mActivity, OpenPgpProviderActivity.class));
|
startActivity(new Intent(BaseActivity.this, OpenPgpProviderActivity.class));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// mAidlDemo2.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
|
||||||
// @Override
|
|
||||||
// public boolean onPreferenceClick(Preference preference) {
|
|
||||||
// startActivity(new Intent(mActivity, AidlDemoActivity2.class));
|
|
||||||
//
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,9 +17,5 @@
|
|||||||
package org.sufficientlysecure.keychain.demo;
|
package org.sufficientlysecure.keychain.demo;
|
||||||
|
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
|
|
||||||
public static final boolean DEBUG = BuildConfig.DEBUG;
|
|
||||||
|
|
||||||
public static final String TAG = "Keychain";
|
public static final String TAG = "Keychain";
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,583 @@
|
|||||||
|
/*
|
||||||
|
* 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.sufficientlysecure.keychain.demo;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.Preference.OnPreferenceClickListener;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.api.OpenKeychainIntents;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
public class IntentActivity extends PreferenceActivity {
|
||||||
|
private Preference mEncrypt;
|
||||||
|
private Preference mEncryptUri;
|
||||||
|
private Preference mDecrypt;
|
||||||
|
private Preference mImportKey;
|
||||||
|
private Preference mImportKeyFromKeyserver;
|
||||||
|
private Preference mImportKeyFromQrCode;
|
||||||
|
private Preference mOpenpgp4fpr;
|
||||||
|
|
||||||
|
private static final int REQUEST_CODE_SELECT_PHOTO = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the activity is first created.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// load preferences from xml
|
||||||
|
addPreferencesFromResource(R.xml.intent_preference);
|
||||||
|
|
||||||
|
// find preferences
|
||||||
|
mEncrypt = (Preference) findPreference("ENCRYPT");
|
||||||
|
mEncryptUri = (Preference) findPreference("ENCRYPT_URI");
|
||||||
|
mDecrypt = (Preference) findPreference("DECRYPT");
|
||||||
|
mImportKey = (Preference) findPreference("IMPORT_KEY");
|
||||||
|
mImportKeyFromKeyserver = (Preference) findPreference("IMPORT_KEY_FROM_KEYSERVER");
|
||||||
|
mImportKeyFromQrCode = (Preference) findPreference("IMPORT_KEY_FROM_QR_CODE");
|
||||||
|
mOpenpgp4fpr = (Preference) findPreference("openpgp4fpr");
|
||||||
|
|
||||||
|
|
||||||
|
mEncrypt.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(OpenKeychainIntents.ENCRYPT);
|
||||||
|
intent.putExtra(OpenKeychainIntents.ENCRYPT_EXTRA_TEXT, "Hello world!");
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mEncryptUri.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
|
||||||
|
photoPickerIntent.setType("image/*");
|
||||||
|
startActivityForResult(photoPickerIntent, REQUEST_CODE_SELECT_PHOTO);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mDecrypt.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(OpenKeychainIntents.DECRYPT);
|
||||||
|
intent.putExtra(OpenKeychainIntents.DECRYPT_EXTRA_TEXT, TEST_SIGNED_MESSAGE);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mImportKey.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY);
|
||||||
|
byte[] pubkey = null;
|
||||||
|
try {
|
||||||
|
pubkey = TEST_PUBKEY.getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
intent.putExtra(OpenKeychainIntents.IMPORT_KEY_EXTRA_KEY_BYTES, pubkey);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mImportKeyFromKeyserver.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER);
|
||||||
|
intent.putExtra(OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER_QUERY, "Richard Stallman");
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mImportKeyFromQrCode.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mOpenpgp4fpr.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse("openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282"));
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_CODE_SELECT_PHOTO:
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
Uri selectedImage = imageReturnedIntent.getData();
|
||||||
|
|
||||||
|
String[] filePathColumn = {MediaStore.Images.Media.DATA};
|
||||||
|
|
||||||
|
Cursor cursor = getContentResolver().query(
|
||||||
|
selectedImage, filePathColumn, null, null, null);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
|
||||||
|
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
|
||||||
|
String filePath = cursor.getString(columnIndex);
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
// TODO: after fixing DECRYPT, we could use Uri selectedImage directly
|
||||||
|
Log.d(Constants.TAG, "filePath: " + filePath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(OpenKeychainIntents.ENCRYPT);
|
||||||
|
Uri dataUri = Uri.parse("file://" + filePath);
|
||||||
|
Log.d(Constants.TAG, "Uri: " + dataUri);
|
||||||
|
intent.setData(dataUri);
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(IntentActivity.this, "Activity not found!", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TEST_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
||||||
|
"Hash: SHA1\n" +
|
||||||
|
"\n" +
|
||||||
|
"Hello world!\n" +
|
||||||
|
"-----BEGIN PGP SIGNATURE-----\n" +
|
||||||
|
"Version: GnuPG v1.4.12 (GNU/Linux)\n" +
|
||||||
|
"Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/\n" +
|
||||||
|
"\n" +
|
||||||
|
"iQEcBAEBAgAGBQJS/7vTAAoJEHGMBwEAASKCkGYH/2jBLzamVyqd61jrjMQM0jUv\n" +
|
||||||
|
"MkDcPUxPrYH3wZOO0HcgdBQEo66GZEC2ATmo8izJUMk35Q5jas99k0ac9pXhPUPE\n" +
|
||||||
|
"5qDXdQS10S07R6J0SeDYFWDSyrSiDTCZpFkVu3JGP/3S0SkMYXPzfYlh8Ciuxu7i\n" +
|
||||||
|
"FR5dmIiz3VQaBgTBSCBFEomNFM5ypynBJqKIzIty8v0NbV72Rtg6Xg76YqWQ/6MC\n" +
|
||||||
|
"/MlT3y3++HhfpEmLf5WLEXljbuZ4SfCybgYXG9gBzhJu3+gmBoSicdYTZDHSxBBR\n" +
|
||||||
|
"BwI+ueLbhgRz+gU+WJFE7xNw35xKtBp1C4PR0iKI8rZCSHLjsRVzor7iwDaR51M=\n" +
|
||||||
|
"=3Ydc\n" +
|
||||||
|
"-----END PGP SIGNATURE-----";
|
||||||
|
|
||||||
|
private static final String TEST_PUBKEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Version: GnuPG v1.4.12 (GNU/Linux)\n" +
|
||||||
|
"\n" +
|
||||||
|
"mQENBEuG/qABCACZeLHynGVXn7Ou6MKE2GzSTGPWGrA86uHwHPUbbTUR7tYTUWeA\n" +
|
||||||
|
"Ur+l+lR3GRTbcQY4ColGUcDcTVlW/cp9jhHnbbSIS0uJvW+4yu3I5eSIIoI09PLY\n" +
|
||||||
|
"KjT0U5l2z6t6daL7qWfZ1pQkCuCMe43eMLBPvyao1+zEd1zESbMz/bySZRlYMKAC\n" +
|
||||||
|
"aD9pGnFHS+EOU+lQXxfzCpKEQcHmPrrBFh2Gr2JFWWjZArKh7B1lQLekD2KS8aFb\n" +
|
||||||
|
"Lg1WGo5tK1sSk6MnMmqs1zNw1n15p5UDnJ7Qh8ecfMyDLy/ZyUjfFjy4BE0p+4mS\n" +
|
||||||
|
"J5iDU0pTYK3BpdfujY6NE+S2Ca2J6QoNRN8XABEBAAG0MURvbWluaWsgU2Now7xy\n" +
|
||||||
|
"bWFubiA8ZG9taW5pa0Bkb21pbmlrc2NodWVybWFubi5kZT6JAT8EEwECACkCGyMF\n" +
|
||||||
|
"CQlmAYAGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAUCS4b/ZQIZAQAKCRBxjAcBAAEi\n" +
|
||||||
|
"gjHWB/9w+D8DOxOGeUzNxfGn98C1nYVkt8zNcTnODBd8VsaPx1pKOXUu6IfqaTxa\n" +
|
||||||
|
"qS4hsAmgV9l0xLA2CkRndZAangsl3ZwURh8UiX/uqJRA9c9py7O+8GxpARtwtOPQ\n" +
|
||||||
|
"VxXx/O8vkXxFpYsFzpotN5XlGkLWWVySotKbTcSfaBifcS3oFT+d6VAZ/D68iTaH\n" +
|
||||||
|
"YBRfwaganevuqUsrJQiCOX11d6Lnr5cDzvmR2yagsLZPUi3CI02bVZYH99uNAr8b\n" +
|
||||||
|
"O7OkrgbcN7U5VlMuXdzj5fU43QpAzrT11JY9jmYsxbJ3t0Zgb5tnGXq9UBkPgGKC\n" +
|
||||||
|
"T01QW7aKHBN5YeDtOY1d0DQMNLkKiQEcBBMBAgAGBQJL/m2NAAoJENjTIPvvLEnw\n" +
|
||||||
|
"9TsIAIEV6UjXLSfxOggm7/0pG43P6OP1HvItUCg/wPfLexHGKJQAh2SuotUNNq7h\n" +
|
||||||
|
"i67PajHBgS/iZNrT868sDWGN5FT2toFOsY3YMTwv1GpsZGTSB7my+ej0Lq5OxCkD\n" +
|
||||||
|
"1rGsOLhetFzIqRuybmLRgAWxj9tg8ib2bkpI1S+67TRtrDkbKUdVrEvQp4rhItNl\n" +
|
||||||
|
"L17mxdViOkmboNgdTxlfcjyRh96dkAMgzKL/LkwKsgFxYYAOOKGodg1pOGdeTku6\n" +
|
||||||
|
"c0h5UmTY3DwLiY4FeAIzx3L8TYru3wx+if3j1MuVKRt3p51fX5/4dmyfWbrSRPO4\n" +
|
||||||
|
"8fh7RM5JMWtcI9DKLoFIhydppl+IRgQTEQIABgUCTFF5iQAKCRBlHG1tQKlVAhQN\n" +
|
||||||
|
"AKDTCAl9IVdBuuu7NEi3LOKKi/8ekgCeMG5UX1a5YrH7E7n8AqOk9efihuKJARwE\n" +
|
||||||
|
"EwECAAYFAkxReagACgkQVhWX/ckAx3iKEgf+PinRsmf6sGSM7HMZigJPGQuw81jM\n" +
|
||||||
|
"LPC0P3qc9wHpqCpoGcKzZO0wKKKdQc6uKwJUMLKyJeK7BwwJz+r75KWHz8zeUAoY\n" +
|
||||||
|
"Iyqf5ukM26tADxr+oDpOJegHkvucAdKQjKDu04cXBkILRJ3lnytpqE6tiqS1v1Jx\n" +
|
||||||
|
"SvgdtdC6WHHgpwJPRqtZz4KPllQ40SyrNhzgZ4V8qFNrt1jhO+6Z1rgyTEDwUwxM\n" +
|
||||||
|
"VCUz8fIQX9Ic6il4XfwKKt4kA+kiQC8Chs6hSkUERyNZwNAkToUnhwfcULpWnDnz\n" +
|
||||||
|
"q0mKBAhLe++KaigVwzXMFVGoy/YaYR35dxLkSlJEZC2CP962vO4nfkvrFohGBBAR\n" +
|
||||||
|
"AgAGBQJMpHMVAAoJEOQ/9ZAjofikEHYAoJz13rWjPl7S1VCSLO48qPq6DEN3AKC7\n" +
|
||||||
|
"HX/YUbDR3iskXSRDICKUwK8P54hKBBARAgAKBQJNnEjdAwUBeAAKCRDFl97MF3r9\n" +
|
||||||
|
"fEVRAJ9AobzDC3824/Flhk4aomgwFuIJ7QCfbyXutcVsiFk3GJNa4jSThR9/4yuJ\n" +
|
||||||
|
"AhwEEAECAAYFAlEFpa8ACgkQskE8Zt0sP+rkyA/+M2E53FXWzjlRttQQsfY9kH+/\n" +
|
||||||
|
"nQ6x7HAEsa11rysB1seM4JJGXsvyp8e2KHgxAgwcOEbWt56NB5Vlx2qms11iEC5l\n" +
|
||||||
|
"JrqTajWmF7sWcHxFaC8RVm+A0HHK0qWGga6T8E15izsKUM2esDJloWoYqA9Ddfdw\n" +
|
||||||
|
"B0JXH3F8u+2OmWhgU08jIyDt2iM7HMCFssben3VlXribsCP0etOeHFITWyZXOCKZ\n" +
|
||||||
|
"A/zotpLd9LdT1Z9fKFVlgdQbt9PNyxKraUuyG7TAYgU2tZMuDZ0y+FKM/MfsdWHL\n" +
|
||||||
|
"9jBbzPcWYgh/GBkHAEpmuuX5KBVEJAQ+zCv+lBuHGZ0srLH/YZZNE8fSc99kC97i\n" +
|
||||||
|
"IlU2Rj5IkiDySxPeEx3LWDzINV/vPejJ6XoWb6LuV/8PzRIWOdutaEdyjcFwQo0K\n" +
|
||||||
|
"Ht9B3oETz5idGH200aYHJy3Ex3/vvX0xAyUL3naW2ipZH2CLivuD7H4LIdqC2ukh\n" +
|
||||||
|
"BxKB0gufCGqc2YBB54jYU9fvCPGJJ7NYqymYKTBf0DKHe3zVCRkdxQYyLkWCFP3m\n" +
|
||||||
|
"izpxSebbhdfdLbCSssRDA1e4nu8EASDuxDaaz43b+56KZ3Ca+/Q+zQopgaHNHuSy\n" +
|
||||||
|
"7CIo9qO6okjCiLO7FkeeJxmD4GAGi9hSHGiW3hR/h3qv3XHjExeys7AFSVRyOXpo\n" +
|
||||||
|
"DrGwnadlYBkAh8tMbYWJAhwEEAECAAYFAlEFzk4ACgkQ0DssUbNe6zwDuhAAjhwP\n" +
|
||||||
|
"HGUwvS9YsF0+D8Vp8OSYh1Q0/S7iSSVLT1ekilpbOdCIXXETNz1Mphe/7LkRpSmn\n" +
|
||||||
|
"Y6AjHQexvDHJB8VTGO06LJcpaNl+FRB5z7rmmpRSR6zl6mxuNo6UmM8UKxoFnXvk\n" +
|
||||||
|
"Ta23uBo1Q3CylrX5B/6UrhGS+0WiJE/cbfAKWTvOQqD2s7j0AMeJgn/qcInziHik\n" +
|
||||||
|
"W+0PWuHWjUCpy/BPNWDIsLn5ueYoXSmgUeGuWmI9VnyC6PdjWzxE+2QzYEpBANYQ\n" +
|
||||||
|
"Yd09QaBvhQA8/p6FBV4Ofa1ZdMcaGCPA+QSA355a7+uRElzSHa3tN3vThhdwKOjO\n" +
|
||||||
|
"d8l8baYq2RcXVELU3fjDwp6/L00ldSyV0HHlbBLZZ8RGpB76e7LEMqE14DzRkyyp\n" +
|
||||||
|
"r0m1SkDzSYWcTIFuxGnZ1t2/FUe8Vt2WW8UwlKiHSo3VDQzLxvlGpkmV9uV4Z4FE\n" +
|
||||||
|
"+LMFKSZfIa4qG0LUQeaJ8OZ8l1yZgZPkwtuGI2qKh7XFchfB79PIB2QUtMlAzoZf\n" +
|
||||||
|
"CxPF8ifQ3hGtHaKI83LGaQpTHEkzPZYHzOEskSt5oGIPg90wq9c/bbN8nPuwmg9h\n" +
|
||||||
|
"Lzsf5+V5ZZuJcSOQd8Rbx4Juj5NI72jvH0BY7XtVSWL6itJsqbvU8YzCbEcOMAQJ\n" +
|
||||||
|
"jxuvTAQ3BnkOrETgH66TRjFPodTikZOG6hUbLaqJARwEEAECAAYFAlEGSowACgkQ\n" +
|
||||||
|
"Zf77hz/aPZlvmQf8CJwn0S5l7fF1MMgFDs1gVYQLxNPDp+w+ijdiQy9AbHL0eeSA\n" +
|
||||||
|
"5wFAPbMKNke7DiaGlXuJZkGjYE/gelVdiauno60R8sO/V8X0FXQNSb/XLaPVykzF\n" +
|
||||||
|
"ajQBwcvCEGsyNIuYRIQpuKS9eROx+eZS7/Ez4bao6rOEIGWiormSjkUybkxnzXIR\n" +
|
||||||
|
"i2fKUGrFaxSmRFmG3WiozudD5lbY/HD8d8ofZQrhbWGYRsG30VzZk0XY+CkofwYd\n" +
|
||||||
|
"MEoooBc5N6vtskHLkl8Z91laNphC6pk4QQ9EOPfoxE9t9ZyT2wiF6YkysXMOUeJB\n" +
|
||||||
|
"K6BD8Aim7HarCZU74C1permfnE43CKoSEk0HzokCHAQQAQIABgUCUQZgWwAKCRBi\n" +
|
||||||
|
"udz/ZiTBorLyD/9J7Ub2sogWskFA8SPEw5SaCyAgxpNzb7ykJ6Tb1z8zq29be/zS\n" +
|
||||||
|
"BsRWgcQWtOXPc55uhSY0Jwaw07ejT3/fQIDLZCWvXmPBgTh6OLdTJYZWNimBHDp5\n" +
|
||||||
|
"8H35ZBdvEtha6zCGkA20c/F3dnrz56dPFeI8c4hHu0LBOzqZYgoRHFh85fAnHTHc\n" +
|
||||||
|
"TfdvZ/obNeV0NhyKJZQgZepKdAN2Z9cXIHkfdjLeGuA6CEJlVBMk96BvozNqm/4X\n" +
|
||||||
|
"KC+NGxbp5J+yARb80gT56/OGaB08WotRUcFRub6fc7gT7QmsXrx8YWYaBIWlhW/U\n" +
|
||||||
|
"/yliJdZeMawKc7tgLkpf6qM1GerTgzkf44r3rXHl+ImlizdQhFhUGJFbkisiqYzU\n" +
|
||||||
|
"caMmNkPWNg28e6dcUU7nfprt99IbSSdhPAxPd6B6bzUawwV/VnpNXIrH+048lgsh\n" +
|
||||||
|
"FO45JceUfSadpK2p2UXVUP/TfYYY2xBwvoCjAsD4Q9YYheJsWh7i3xi9/0EzfSPB\n" +
|
||||||
|
"1NpLdaujAO8CuTTZ4NlpTLOZFmksQSF4BSpcPC+8TUGwAKXIm2wwIUNm1sBwXNsZ\n" +
|
||||||
|
"aOtAz3vQH3pKTzvXQEoCMCqoqkYS+uTGilwhmlSivfenB7DT3TX8DHnUzT1eNlh/\n" +
|
||||||
|
"PfCmdufvGS4BodAYPgTfmq2B7bpohFOXzjwiIJ4AJfaYhdyQ/PqN9JLShokCHAQQ\n" +
|
||||||
|
"AQIABgUCUQZr1QAKCRDIIhUzoaoywpfcD/wJveyGF8c24F6O5O2LX7yWmJ9LWs9t\n" +
|
||||||
|
"9z7c0gMom/eQMyoFh2VWHnBhNfR635RtSWFasMJSEpPd0iyIz95eaXpsjdn408jC\n" +
|
||||||
|
"8yGZD8N9EMkSbtGrcMExoyH+Tobx2Xs0mBDrG0TZdK07dW3q75asNFtU52isxC60\n" +
|
||||||
|
"WTJKUJud5vfms0cnp+sRCewvwssOyaIYzqg9J4/GZIpY6d9Roi9u+7FIUnzQJN4d\n" +
|
||||||
|
"zsDxBOsKZAVnyDaaaDMWXEV15BVwwFCap58e5HzL/NKK2uJMyMwBpPjZM4CYiWL+\n" +
|
||||||
|
"DVNvedids4iuNezyYKJns+LSo7zl/Rv1IQKPhg6BRbxgKu+DZt7iYI0vEl7PJNIM\n" +
|
||||||
|
"Ex77BZI/DUVZWXlE9g1bB4sP3iMBq1998VvWsTbA6CLRMYQvN5GWzlPyCICx2N9V\n" +
|
||||||
|
"dRD8Y+ySBtXMSLTe/kMXvnpMvYl5Yree/mSRirqA5Z/sZtw+SpTdtXEgoPpuCouc\n" +
|
||||||
|
"KKvxWkyDZX3Ehre/wDsOFN9XZfEp79SYym27rqMr6QPx6UQOPYd05soqM/OHBP89\n" +
|
||||||
|
"OCQqcbKkdcmv+bvGHdrlMjSfQGf3sBHy2TdL0LzzDOiGhoMU4Q4QGkrVYZwzpLDv\n" +
|
||||||
|
"InPbtO9GvAiJeZpgZyyvUHwGrmJDlpO3QPBMJj1AxOc06mJmSwg92ziIkK+jX8s9\n" +
|
||||||
|
"xlxcKmFXxTFUVokBHAQTAQIABgUCUQbiuAAKCRCylLwGx1Uqeo/dCACjY9xvAup2\n" +
|
||||||
|
"MCcs2nvHtKCy3NVmzm9Qsc5hferWJ8xPqUEi+OrpeyknWQWMQUlwCTRX/5I2HLh4\n" +
|
||||||
|
"PI4ycieWGiNh1FLbAcTW+xqkjNfE1iAmD3h/c2wBqlsMIdTnPNKFD1zhRAjixn58\n" +
|
||||||
|
"so1uW5+sTmPs6VVU9Ll6hcr+LzsUoS9t/sHOD53KYG3JKeCKLMzG95Ev8yJvxYZg\n" +
|
||||||
|
"DC2NZZXEeQq8XM59gpBoGrTx0xmWIFio6w3XIYHlhwcvNormrpbaZDxq093qy2hx\n" +
|
||||||
|
"AWVVSb+Rby/Vp4AsJyoQ6p0DzWbxj97o+rFV/Av0pgEuKnhpnpyB5mDvhwPGvWwp\n" +
|
||||||
|
"xGi79eZYSN8qiQEcBBABAgAGBQJRCCrMAAoJEDVPpeI+qihik/EIAJXinMI3lDhV\n" +
|
||||||
|
"KbhE6PYQLwfd8OBrV8fX4/vQbzosjCQBwiZFot1LO4aRAZgLcAwMoxDGQco2h+bf\n" +
|
||||||
|
"UWskvhMGCC7rqvDkmoalGfQ+IwKnnHeZAghM1/Dd/C9ijl+2LQeeNlcaaWsMTjcV\n" +
|
||||||
|
"q3cZtPInLJfJpci8nhDET4dHGl8tai80Oen30Wd19nDDaeL4qeF3E7YaPIwcg7jR\n" +
|
||||||
|
"PF2fYQBIfh7+1Y4tbhAqyLRgFx1aB+nqgVbsLtRXEK4OTPeigN6cEawot4XRC+nR\n" +
|
||||||
|
"Qtp2ZqDTzOF5KH9tG9+S5cQZsTUIFtWevBxrIg8kimIt6sOxt1wkeipb4QPBbkjS\n" +
|
||||||
|
"+6zkLMhO8I+IRgQQEQIABgUCUQq1+AAKCRAEtb81V3CDSkjdAJ9Hq4iFYWxNRpJc\n" +
|
||||||
|
"Hqv2AH1F4yWtjQCgvGQK6MsOuO9QBcqFJmVBHPUPWk2JAhwEEwEIAAYFAlEMbTQA\n" +
|
||||||
|
"CgkQrpk18w7U8BBHfg//be7uVhY/hE04S4NrR6IG/k+9gMQHmH6OAylKWvfd8CuV\n" +
|
||||||
|
"npd3ZmZGosWAxRaizaET8OPATP+Wojbuved0/dFL2cras/+GKWleuWI3qxFAAqSx\n" +
|
||||||
|
"SGDilpladdQZbyHfrAHWFwpwEl51wsc9LkcAd9lywv0wbPzV4oFqEqbPM+wNW5jS\n" +
|
||||||
|
"N1W5doj2A3MUw17ocRtk2XmzhF819w++t16Alweg0QrfEx5mwli/Z17FcYUC21Wc\n" +
|
||||||
|
"04eDL8s/j3U3SYjBvzNIrtx2JiS/MdtewjvAWAeEoFusNcwbYn8J+2qiTrh3twR3\n" +
|
||||||
|
"AU7xU1s2a3GxjjQ+J/HXr52Eujd4nk/V8Au+NYAWlCoR99ByVfzG7tsjaGPysHb7\n" +
|
||||||
|
"KkGVIAlXM0brKwQvRhvGsN0+8mjfM+xl58AVV+w/K2MUTyWHyAMigvXtK0lBGcHb\n" +
|
||||||
|
"YATLBD98dUTkeF49oHAFriw2fLqes4ayqqouiLj7RfNHDQ4X41PkxlaSq8GrXHig\n" +
|
||||||
|
"jUKKod//taTViZU2JYL9ZIAGzDaV483pVZpQlBqeWBaaHTlk/fBrFfIxQ8gxrAX+\n" +
|
||||||
|
"Izano++z5tFPgKPBFisDcLt6g5x53ADh3Ax94a0sR8aoBzeJYlvRLG2OLuzok059\n" +
|
||||||
|
"CcvAG/lCYJTpz3LSsWdV2VqDk9LLqh+uzHmA1SVvYyxHz6IEYLGxNhEOPqC4JDSJ\n" +
|
||||||
|
"ARwEEAECAAYFAlEZK/kACgkQ6kRcQQePqI3KNQf/cAik9KuU5q2LRzagJLpVqIGg\n" +
|
||||||
|
"f7Yu57EQ5yENFbL8BJoVn/CMXsx8btDeouGkUXYVDtn5ThrGOHAs2OYEQSF3HSFp\n" +
|
||||||
|
"xqUci5rVLBoYwq8WcbGihg/YA3T1m9T+hGx7uhvDQOUDxjggcwxuTaHGmbIoDHaw\n" +
|
||||||
|
"BtlS2iznyku43ip0yazqrz79CPTJ7DrGe25q6ApVXhZeZ0Tmj2qa/ZB3nwqW5mov\n" +
|
||||||
|
"0UqIVBoyO4WIP/rMGdK7e1HUu5vpsaeAJKBdU0FMADuDc+vVuQildwIejSxW6etZ\n" +
|
||||||
|
"fHiKX05gZjN5KHfrjxaCr0Tv0xBmJ8QhFe7+4qZlfEEswUGk3gXUK5nkatEn+YhG\n" +
|
||||||
|
"BBMRAgAGBQJReqVtAAoJECuuPPba/7AA2rgAnjbHP4UG4AU4DjSjIK+gAwN9Ekxz\n" +
|
||||||
|
"AJ9/LSzNx/UzZYyw2qXMOdzXODTX67QqRG9taW5payBTY2jDvHJtYW5uIDxkLnNj\n" +
|
||||||
|
"aHVlcm1hbm5AdHUtYnMuZGU+iQE8BBMBAgAmAhsjBQkJZgGABgsJCAcDAgQVAggD\n" +
|
||||||
|
"BBYCAwECHgECF4AFAkuG/2UACgkQcYwHAQABIoLM2gf+Kzd0jobczAyFcvK8Tpu2\n" +
|
||||||
|
"ica+3Cd9ZL9PmzCHKO5S7mAgzKGuUSl7/IUG3vuu+ijfVg6ujsSW9QjKptFZAU3C\n" +
|
||||||
|
"/r0LL2+wUFjGscVCkE3ovFEZ+jOWUDUVQoKFN0h7ue1AkYtilYUNwHcKENxeqLAb\n" +
|
||||||
|
"+nAl6U43eRUVe6IbHBtQcdszZ81C6R6Wm5qGCTRaZVWWhW4iRTxw4XMvZ5jeXO6U\n" +
|
||||||
|
"2h7WOiqlAv0QJr7xARVp0k1qMGSKVMaoqvHj0oai4VeBBDMuYYjfHMo9Yo8beKPY\n" +
|
||||||
|
"pmIAy96y7oE/Lb/WYkPcoZ+tmPVg8ZwvlZTTAbrEUlV1xBEjs8/3ldB/qn3Vf8q1\n" +
|
||||||
|
"6YkBHAQTAQIABgUCS/5tjQAKCRDY0yD77yxJ8IIPB/9BcmtqFesUgLavyEGTCQu7\n" +
|
||||||
|
"9DGDcn4oAQNnBxrIO4Am2jfnEwGt0b+9kIl06PG2zNMxhA8NIFxc6XGnfvqr3BkZ\n" +
|
||||||
|
"gCN9dgNPSXQ4XazESylJk7F3h5yozAel2ZLy4lY04Sy63n/3J48coZaLSPLoUDq0\n" +
|
||||||
|
"2gudqQBTG+sLs69PLTrwYdp4kZpJunmenpgcGSxqpaf0Dvo2Fvq4ftRre4pjaNzQ\n" +
|
||||||
|
"9ZXsWJbC16boJd7Hbo/7oZNNMvZC2XU3PxhiEPhwGP6H/Sjv53MtGNp3/Hcjef1G\n" +
|
||||||
|
"TFQsN79m4FHBB+VnRa7wieZXa3cQWy2RamxuVW15fiaZvAs3pKzvdDwSrTFuVWqv\n" +
|
||||||
|
"iEYEExECAAYFAkxReYkACgkQZRxtbUCpVQJM8QCfWIZL9tcmOuVe1hHq6la4GBWI\n" +
|
||||||
|
"QFEAoMdagHqMu/YZ+jeffnD0XzojV5Q+iQEcBBMBAgAGBQJMUXmoAAoJEFYVl/3J\n" +
|
||||||
|
"AMd4cicIALpzq7i2P29c5C0a+cvJsVTGJWA3tQyi+BpCMtIwneqWH/ojsh0vM/KK\n" +
|
||||||
|
"e6jrUAmQ8kQkLGHbMpDTlvVyhGw5kO6WSlIDKAx8TmzmMa/wuCBR8g8zi27fx06C\n" +
|
||||||
|
"RQ7NcDJy4AmU2cKzK7rKpPkLTBHf3zNbUoISJW+icf2tfMBjsJ5tS6o54+f/zhnf\n" +
|
||||||
|
"QM+S9IdRgfH2by59J11H+Oykiy0I77jMNXO04LbMfp/ZqJE1Cwa1piygNodBeWfm\n" +
|
||||||
|
"mlB4WzCiplKJDqVCK6pQHjAnv7f5O3O1MH7w5FTjE/AYSeZ1AZtHbjv8QeXLbRuf\n" +
|
||||||
|
"S3amF3w3yjZIpLSmp8DD3umJe2lpWaOIRgQQEQIABgUCTKRzFQAKCRDkP/WQI6H4\n" +
|
||||||
|
"pIrtAJ9Ri2cWkWnJbvgYjCWF0mNCV2Zx4QCeJBeudrxAYqwTmU5clrFGb8kiuSiI\n" +
|
||||||
|
"SgQQEQIACgUCTZxI4gMFAXgACgkQxZfezBd6/XwgQACbBOBJSdnAeuJHufGi7ETZ\n" +
|
||||||
|
"cwRYGxcAn1B3NirdNJ8dJ6bVV1qEKoJZgpzUiQIcBBABAgAGBQJRBc5OAAoJENA7\n" +
|
||||||
|
"LFGzXus8gCgP/jfftQRpM/PyGwW4O7w9lDf3EyWshqnoO4MGNEy1wX4TW48vpOZs\n" +
|
||||||
|
"KMR1/e1r7hTIC0HXQIfUWGSWd2h+FJIVO36sGXqwgJnopOe1S/3W7MsWa8zfkZEz\n" +
|
||||||
|
"fNXWmK8PUNIGc5hOFxfbAQk+4ZpRtu6nqnlZ4bqP9tDyZ3673jkbth2W9PqNifE7\n" +
|
||||||
|
"wzKOYUIW/cWkyIh3HZLLKpLXu6o5p8P3nIP2IuznybPqNFMfhaFghYT7bWWpixLE\n" +
|
||||||
|
"K2svy7tGKvhJAxfnGvtEYDzjhyh6L2Cmm0X+c/4HcLLlCdErE4tU0SjQSaf+HDce\n" +
|
||||||
|
"+WmwpHL2q+EX8Yd2C2rM2vm5wMVZPiH5GL9Q1O+B+xkmmD7I2TEA3B7ZGw4pZpBz\n" +
|
||||||
|
"X0U6QhRIM+ojMfFYkE03+S8kkhQdFjtPDEIJXAIkCZ/bROa1ByBuVdLm0TQAN1Hg\n" +
|
||||||
|
"m8A39kylJ9kHXPiuQAYggbh0ynx8PYv3w+IxDg2lSnjz/pUrOBmGJ3Hw1MZU/9mj\n" +
|
||||||
|
"riwmxOGg2LqAQ1uJN51pDFV9TGE6WoGi7cob3F1zLrzMZC75C+WpzWlur/gA7vIJ\n" +
|
||||||
|
"Y9NSjND+FOIo8F/oQJO/PyfC1bEI5X/ofQ9yNTKBa9xHIxx+lgiCrDVlbD/pQQBT\n" +
|
||||||
|
"9TyvhT2qjSdM9ipN19c8Mpc3pJrrs+RY96r4u4tZC/AO3+2nW7kWpmqiiQEcBBAB\n" +
|
||||||
|
"AgAGBQJRBkqSAAoJEGX++4c/2j2ZTAQIAKXOo0o4XUzPrMKRBBj3iGTGFZ1ABZ+1\n" +
|
||||||
|
"Zs99t+I9Ksy2LmPsQ96CwK2AzqfbcOlZ9+eMCzYhfX9alvJ7Ms5CTkKj9xo9wDcg\n" +
|
||||||
|
"/fzqG+xVlt3oXeLMc1juW8nLLKhLBn4vELmjh4JuvkjEaMaGwZCbeAQKFyXtZQOq\n" +
|
||||||
|
"YxcKnq8Fe7xW+rHdB9F1m4uCKRW2L9IglUDlOiflUTvCt/3blea936mzsDPhoQJO\n" +
|
||||||
|
"O+zGCF0NXbvJ4lzQmgyWpvd81mbN/DQ055UQjG1DNNS6q7O/EqNRy5Jnv1/qSCAF\n" +
|
||||||
|
"+afVQgrDvrvcAuQRUfw3i66HXNGEm++43C6K+0fkPteh8ESx+H1WSgeJAhwEEAEC\n" +
|
||||||
|
"AAYFAlEGYFsACgkQYrnc/2YkwaIpgA/9HKKyfO5oJpV3bUXf0IZGTgrVISiVY90t\n" +
|
||||||
|
"IbX0qkyGVFwEovp8eRJ0B6cluQiNypjKY+5xh0wYbexk2El53dNDkTfisfVM5ib7\n" +
|
||||||
|
"a7aMAMQ6R99zFVtag/eXmAWzKcL8x3RdVyRFSttwrGwDlrv8VpQByYYSnUPWvzZs\n" +
|
||||||
|
"YJQL+XgHqVLzK16/oZ5rzBvzbIH0oFm7HoGqKsRGDEL2hkNRhjuDlxrzijSqQfqY\n" +
|
||||||
|
"qhMqQAjf6fenpVFFPXr8TY4RXRRcBFq1aP3Xp2Vq1ekwgzHTokryWEyZTdsVXoMU\n" +
|
||||||
|
"Tmjk/sZ4R61N5YW3EEyGuG2E9wgZD8FmUElJTAduZPH16JcfO4KUXsNSXap8yKJ6\n" +
|
||||||
|
"yZ47TvbNcwQq7IhxbwimhaR4pbBpcOfQEpdHA24csOBPJ5Ly02LpAs0ZuhmOvDLW\n" +
|
||||||
|
"Yxxr9++Sm+5UBcAMav+N69f3lUnIc/MhDI0uYLe762z7cs5opIx8Fr64GZn30SY9\n" +
|
||||||
|
"OMpce9UwsmhurO9T/0CKpKeZEynKUHcCWgsdsbDULhuCLr9WypzCy9wJm79bYwqE\n" +
|
||||||
|
"sAJGqK5i1Qxdp0O7VJkPaR1FTdTWazW6phWCnlskpWKtRmu09v9vosqnzd6vSKqo\n" +
|
||||||
|
"q/uL1i1lGvAyKdLSEBc8yrTUTrH82uFRZejcUgR2+f2BslZvPMtQlyQW35D9373A\n" +
|
||||||
|
"MQxZYPg7vNmJAhwEEAECAAYFAlEGa9UACgkQyCIVM6GqMsI8jRAAxjj0Z/62i+Jd\n" +
|
||||||
|
"Yy9iYuUXZyfLh5vexn89pesMgETaopNlv0OAT4rthpujmRCVLV/hN3XG94H/G5yW\n" +
|
||||||
|
"trwzokBz3a7JDdPVSWsLWibANx1zzukG2FkKEl1gWJyoTQ69xet7jZK3p9xl/xEH\n" +
|
||||||
|
"zS7t3rhniTqxViIpRIiHb5tSR/ESDIlR9tvoQwuoriI8TZd0tOkLS1myN+US39jf\n" +
|
||||||
|
"Dbo1sla85bTEAQusTtpHTe7OztzON45saJvfRRIdHlZify3lv7+6Y7jOpFHTe6g5\n" +
|
||||||
|
"1Qou5B9+mwZRb/2Pe4moWsQCKScZanJQDliyggY5s7a2gufEN2hTLzDniTc8FI0E\n" +
|
||||||
|
"YZK+14DiI5uoPhhGJo3kKGcviye07l1VNvxsKxPwW0Xf/hYvTwgn1xIKN1rs1dTY\n" +
|
||||||
|
"3wJpbGLZDdPfRDkqj4ZKAQTujjkqL1RQjdaBoFYmF3At6jV2dWCCK4Cppjv+rm6i\n" +
|
||||||
|
"hNgvKtYpPrTX3m0FJ31x9G8UkGlqhxT0lQ3f5MVLC8K7rqOEUHCIdy4jBaNDEWV7\n" +
|
||||||
|
"YJ/mU69yIb/xBGtVqrSDCMYh9sOy2zxaLQulUiSZRLRs1zr7npVvNf638DqErBAq\n" +
|
||||||
|
"rTjzTNVzkEKcgp1Xpn97xl+pDtS9qm+4P0bp7RPSwIzM9kym69Gnwy6xk6v/Gizf\n" +
|
||||||
|
"xZaRMdUyqXFuptsN5AAN5rn7ukZ3BAOJAhwEEAECAAYFAlEFpa8ACgkQskE8Zt0s\n" +
|
||||||
|
"P+paKQ//QCkex3hC4v2xPOCnMtUtOZ/s+8ptjUaxBmcud985Kl1vuzpfqhRE5SpB\n" +
|
||||||
|
"M3kgEWbqDmVhjvIDf2BwMxm1uLn3Ahl4fy3qZ0mOPlxTh1QRNINgPzf3Ch560jpy\n" +
|
||||||
|
"rug21kUmr9QQRX4yFKe+4g1+NSuC/A7P2AzJKSgkvQM2orR9noNMLNMYO61mr8bN\n" +
|
||||||
|
"cJgna/6G9PEwPunWkiU+ircp7gbDqZR0WDPIoj8WAHGHite7JA/tLD4t9gpNRSYw\n" +
|
||||||
|
"hXqUWXObbB0a6sFSzgJt4QwEqOP6M/eggymohBlVjexA1Zh95mfJkNGnjhCkLXG4\n" +
|
||||||
|
"qPMTq9Sk4cExv2Y5jSCEK/qDyz+IGRPGMIAdC8GFsLrQbWWcHPYWSAxGj5242gDg\n" +
|
||||||
|
"DyYUl0KxMignGOY51eEL35a3Yha/B3L64+6fwStKbWx2X4L5+m26BUAJ9nNhdCmB\n" +
|
||||||
|
"TMXB3uHhoHmstrI512md/M1voO76aq/20akGNcORTlKcfm2W805pSQfg1kfCQswP\n" +
|
||||||
|
"Ja1j9/L1ELmUy+VaDHj2y8MRNIEo00Ax++ElHIM3/+eehyesmdCSLh11IPwxnWhw\n" +
|
||||||
|
"GiJ+QPnqUqJe2e9LApff+Y+m4yPDUcZRnPfWDNRnfL4dEADR2P2ALF3YUS+OIDjh\n" +
|
||||||
|
"/U9njqx2WdWGpI57H9D84EiayOrVE7r3FWJB3qtbYRU9ZrHxDfiJARwEEwECAAYF\n" +
|
||||||
|
"AlEG4rgACgkQspS8BsdVKnrSDQgAiBoqUrh9dVmjo6EqEgJ+C+VsLdVP4t8DVWNV\n" +
|
||||||
|
"Ufpc2lndtrJRpvdyqFN9Kc/7gBEyFuNCM5P5JRKfVoKSY0i9sTq3yWQjsv2iMsQ/\n" +
|
||||||
|
"aDVaSzdmVvl4u7YtTJRGEgnIALL/X1Br9QmLcp/6Fju5p7f1mm5Sbwiqvi6G2cxP\n" +
|
||||||
|
"GHm0ptHsfr96I4JjCAKfNbiZ8I7d9tPnejT6sSuuoB307E/Dr4J+hS2HWuevNm4D\n" +
|
||||||
|
"KVrHvc/9+YTUIgkLSAqyAiOKUEsBIpDejyHfBCVM6x8S/BTBpLzJsIdXF4ip3ww9\n" +
|
||||||
|
"GRPq1m9y0yuC/hvnnbNAP4cFUfLV9KWtOMvlhoFGHplW5GZYV4kBHAQQAQIABgUC\n" +
|
||||||
|
"UQgq6AAKCRA1T6XiPqooYvSnB/9aJgpZ/LNn+QsXGQ0gz/D3aOT6P+coN/h2kfCU\n" +
|
||||||
|
"p0TQ3djdOodrWJ4SQz3a4AmMmRkdcQa0XPKQqZJSir76IHoKnQep07oPSWD+JQyE\n" +
|
||||||
|
"6Ix2BPM4Er5RqscQ3udbiwDatR57Hb5UIJChapiZqseu6Lx8uU/Swi9HjlFpKs3h\n" +
|
||||||
|
"sDP6ocpD2LW2yLadtE5ivLTcLO3qPEzsecflq2XIi3zuaZRlPzkhnj4bnVWo3ET3\n" +
|
||||||
|
"JfScv4OLTTMCWhF2zWSHjrwxBqMTdE/QrwaSMUvPdyaGjg8G9eDNRW3BylcDlWH7\n" +
|
||||||
|
"SzDeFZGb2SjmwR9ie/mbiUjD05lIEQCk9NGQC4GTE+8c/qFoiEYEEBECAAYFAlEK\n" +
|
||||||
|
"tfgACgkQBLW/NVdwg0qDGACeKq99OOyDJS1cCvAGJZeFRN6mmSkAoIDydwBZu23Z\n" +
|
||||||
|
"ghKLi9JFSnQj80A6iQIcBBMBCAAGBQJRDG00AAoJEK6ZNfMO1PAQbeYP/1eyE285\n" +
|
||||||
|
"TN3RO0OdaV7PyWkG9tpo+qiVMdEc77YP6DPkb46hDKcD5oTW5X9ySJIRP+SWFNUw\n" +
|
||||||
|
"3kTeuVYnZz1VtnxZWW4ODeSk9czbvxN7+aRCDtS67mjVG+KFhC+o+iiwi7Ex51gY\n" +
|
||||||
|
"BaFBTbowoIUBIAHcFz2nyBtY+8k2DRzcdiIAx+0CuRUWpWd3hqd5tK32SRAea366\n" +
|
||||||
|
"kCcGBbBBMmpMRMvlkzXVvI4CC3BRoqhFQDgj7liJhSqQ831SAhR5FqxbioXTVVA+\n" +
|
||||||
|
"h19Tzp+45YSjqyfE+VZe8PSg8P6hbLqUpqABZ+92e0HhR314U9XjPTpEEapaNMpm\n" +
|
||||||
|
"34b8u+Ix5w2IL91fCJd7P5GAboYu+BoQagDP5NV4fXQOj5gTulhn6nIHX64+/nRK\n" +
|
||||||
|
"F5IB+fcb0HZYFCQ2t7nMt2wM9QHmoPaGB9KhLrsre15raQURk0R2R9AEgh5kjdrY\n" +
|
||||||
|
"sWkkhng4kAkO7zIOMZiti5TkMWiCXh0Uq0jGIHS5Bqg1MhLoEC9pcCNBcOVjIPFt\n" +
|
||||||
|
"4jDBsxHAwp+x7Mmeo5ljFMoODAkcMq5JNhL1BI4kiSux5g32lU42aF6r1x79UPzE\n" +
|
||||||
|
"9MvycTBaCQLGiRTHaUZyOeUrcwIK8+4TgvYHTrL9f0de/og16Qair+K7T+HDBQpM\n" +
|
||||||
|
"p0evZHphbrnryKCUEKynIySP3IOTLAFevmLdiEYEExECAAYFAlF6pW0ACgkQK648\n" +
|
||||||
|
"9tr/sACqOACcCAjaMFIUCWY4VPnZ6CjiMohki6oAnRz9LeE27s05FM3qF3r7yqTB\n" +
|
||||||
|
"bLVetDREb21pbmlrIFNjaMO8cm1hbm4gPGQuc2NodWVybWFubkB0dS1icmF1bnNj\n" +
|
||||||
|
"aHdlaWcuZGU+iQE+BBMBAgAoBQJMO4BcAhsjBQkJZgGABgsJCAcDAgYVCAIJCgsE\n" +
|
||||||
|
"FgIDAQIeAQIXgAAKCRBxjAcBAAEigvbfB/9jjRtyvBDda1PbB5HMkS+5YZuy1mTj\n" +
|
||||||
|
"WmMYMtza1p8L3uRhZLb09Ve2sQ0tSNJSnUcL4MEJapXSnwsz3l7Zh7aOo6GjUAO9\n" +
|
||||||
|
"2LZzV/DWoCIei/caJhEiNV44HzdJUlN2+FBl5tMt9DFordfZIEm0jPWR8kTzF/l0\n" +
|
||||||
|
"sGMxVUBo7JrdodTX/2nybPLnSpSIhTrSfA8sn2VJV1FrN50nXOOnGJCYOx0HoyFP\n" +
|
||||||
|
"zX+QVoGO2S2lFl1dLcnrYKfNcMnkPZyxN9K+7/+4D6jKMCfn2hKBH2+in9D9yNWl\n" +
|
||||||
|
"Dbb9fxYP3AW1ObyrvyKFe1pCEBDpifH5+n9W2gqbNS/w7Xoh1/Phn9vsiEYEExEC\n" +
|
||||||
|
"AAYFAkxReYkACgkQZRxtbUCpVQKCkQCeKQ/i3XXlHunMU3blZu+vHoLO0XcAn3L+\n" +
|
||||||
|
"erc3GGnUT+fUix8RmeY1oPiOiQEcBBMBAgAGBQJMUXmoAAoJEFYVl/3JAMd4ZisH\n" +
|
||||||
|
"/0XuGH+G7cROn9u0cgjXSPScDdCTDVjaRwj1KYgZ3y63naqbvCe18gZ5sSsmsrBg\n" +
|
||||||
|
"WSnI9ynpQmU4HFfqOnZFXoV8qXkkoSv6E43QUtsrKBJf77VYRRtmpNsQEs6MQ7l0\n" +
|
||||||
|
"OPhWhnrEKWyeoa1PhMxN9vBXuqT/DvK9vQCCwAJ0i0mlLslnsw78tY6Dw3km0w7S\n" +
|
||||||
|
"1AS7ZQ0R5Hv/VtxAwQEsQ0ON3sptVzy9Mv1mpyqT8VPcpVSoMs76MLvHv1FpdUJr\n" +
|
||||||
|
"zxBwuapZjZcgH2L+QEzcgtUGIZKNfsw4w4T+S/fSzKQbhnROaLZG64cOAUuBAsxl\n" +
|
||||||
|
"S1xpg9tupgk86g8Gu+GTKNuIRgQQEQIABgUCTKRzFQAKCRDkP/WQI6H4pP4gAJ9a\n" +
|
||||||
|
"EpJPzGtsV1Hrp+L3J96kbX5cswCdH+IKmnveVUZBhWnDy2xCoW5X0BeISgQQEQIA\n" +
|
||||||
|
"CgUCTZxI4gMFAXgACgkQxZfezBd6/Xz5YgCfRouhQNbaBelpk+pgwk8XbVi+C3sA\n" +
|
||||||
|
"niQ3EIOLdXEDEsozpDcrKsd08rRAiQEcBBABAgAGBQJRBYnKAAoJENjTIPvvLEnw\n" +
|
||||||
|
"CAcH/Rmciw+bRgCPbroPGkzQHTD8y9RWTEclBDv6mnJLNlzacKzfFhafvMnP/CuH\n" +
|
||||||
|
"9gEVKf/nM1vCS9G98t5CksGrLsEXJoVRGeOG41bREafUc+n2dxEoHAW3yUuvfnVZ\n" +
|
||||||
|
"zLEgNBk066v4wuNh6mt/vEUz+8k2kJ/1BRe+V3x6kFKKfN5ezszXs8UWMwROrLHA\n" +
|
||||||
|
"ElYOZKeDEL6oLpykHXFokjLHMgOxnvwOuT3tOMuHo2kW2LyV5DyGlJbYx+pHbdaC\n" +
|
||||||
|
"9dzXe+uPQ2YzKCuc1TgyMAjCDcG9OOiZEqTdFAY8Lr5eUdNG8Rozv9+rteSk2QaQ\n" +
|
||||||
|
"FqCbKmpvV6u7cnO5dydego2t2O+JAhwEEAECAAYFAlEFzk4ACgkQ0DssUbNe6zwO\n" +
|
||||||
|
"Tg//Qi20qePBfw+fsq77Pddt6s5kAulMzIK2vbUQYY+63MCnIbiiTC5464K1xwMz\n" +
|
||||||
|
"56erQJSqltW5r7MxgLJdP2IISkG8PfRCBQqJWlsriHL/EuJ16AsLUCncWggHik1L\n" +
|
||||||
|
"oaHegyplc35Ai3Nm70nxCVtmC/62k8EHlFuw7rJbhqg5s1hAKjl7HRryAHhzag/o\n" +
|
||||||
|
"LwzIQxKiGg4jIRhhPS3Ye1NnJR1yv0JywovwgIbGYfvKqmNInym7au4o/DSKfigd\n" +
|
||||||
|
"hA8t1LwmcGaXrTEyxTm2wj6hXu1BITzZhmhayrCZv3ZnEE3r99bdq/Qr9f1qrVPD\n" +
|
||||||
|
"7W3OMve7MW2H2gpd48uVre29SV1RCl4qKnVGO7v6weppVudbnpYh/I+jfrpDC0QT\n" +
|
||||||
|
"h8qPf8/4aec/j9tD8tXYMtBK/+J1xEYTO4o+j5Gg2u4Nv22xT0TUD53m9SPo2PXr\n" +
|
||||||
|
"hIZLlE5t24Mj8lyK8f0nAspq9RZRoSaxdGzLzyrIVpXaaqo+3ldCA3JWPp+cAMay\n" +
|
||||||
|
"dj7TTEJ+v3DlEmqsI1UQMTcsDXA+PaEqVryRxQ7rSu4HXKeszEJfAxPQslsSIQcy\n" +
|
||||||
|
"deVqAhG06QYYgIgHGD8DNVvOOtp6IXj2vt2Ss77APVNMtIUualtb1R+tT+p/H3ti\n" +
|
||||||
|
"bFn295UYYnCJOjG/3QnWGBBzrgwNqSbrdIUNEAf3w7ogUk2JARwEEAECAAYFAlEG\n" +
|
||||||
|
"SpIACgkQZf77hz/aPZm5/wf/Z7uOa3Vg90aTBRa6UV22p7VK7kWYJW19MHNBNYQO\n" +
|
||||||
|
"vDEPMPVkJz0GXyckOnYnPz+9fZvIeO3RzvYVc9YOYAYvmBlu4934R5ZGiCqjvy+M\n" +
|
||||||
|
"KR9q6X0hXHZ/cioW2Li6zRIqdfdfomXHiK7IrK+yCLyJIIua8P5S0YzY6A0/Xfaj\n" +
|
||||||
|
"xgO1QCA8O1wNaP7vcCxIN2a5fptlmOEsNe0okfX/2I/lKMF+//pJHGa8kYC9rnFo\n" +
|
||||||
|
"Y5I4IcDuI/jXaJCattmUijAtvSaDMox5/MozEVv5lTbdet4cZyUQ6ZjgdrwjTs2e\n" +
|
||||||
|
"nnvU6C4PDZWng/kbBxkp+ne+iaiKrT0iCUgBDIOWu+8VZokCHAQQAQIABgUCUQZg\n" +
|
||||||
|
"WwAKCRBiudz/ZiTBoo65D/4vK0rAodk71PQvbWM2Z+p5+fWHmPrtg1v8jN3NTmWX\n" +
|
||||||
|
"RG7+ujO5sX0gA3K4aY4X0zNRUROVMhJi8A9m9fU0ZlaZ3dXxbOGmEuhG8PMAcnwY\n" +
|
||||||
|
"pTEYmHGOOcEOJ7dUE7zu9NIBKI+Hi1mzKLvQqLXbw9cRoAscHLK8M00hpmANSxb/\n" +
|
||||||
|
"MWJS1+l2gqkWE8u6s1Jxih00a+ex6ealhKsgaxMpSd98FQzu8s3achTQYFy7zEGL\n" +
|
||||||
|
"T5iEnXqspoEmrIrQoUL/yHJg6Sol5dofP/dWhMm7FewjrYZWykgo4yeGMPfIbALH\n" +
|
||||||
|
"KlQu2p5i7NdTfwVcei20rtlk5R+ZqU/k520qcU2mwfgKu1Oma9cxPEbJ6Cn6tVHl\n" +
|
||||||
|
"eelotjH6aCj8MratzZw+BO7u15st2j7BMFs5qPOqm98qCVJ/ujZbXgMvxuk/KloR\n" +
|
||||||
|
"GRsPsr6r146GsbkcrtdWTvvSwiYcA2rRbdJkqqUkXc3Pr1pdKNkc51rnRnuaUp1P\n" +
|
||||||
|
"EEyuBxSfiZdClpVf/yXiAZfPVf+db5mWhu32rvRq4GLQ5uXM/T/eX91YPWCcmOKn\n" +
|
||||||
|
"wM+4RK0wmpcn7Iak8f+stJKnHF9QcInqHvb2JiHS7K/UOdjpzeQ3gr0xjoSyT5tq\n" +
|
||||||
|
"Rhp13/PSr6tcgIWcghVTolmTtBj9BlAdf32+zfC/sE5fiuzQf+ckYHmyVIBjLAaH\n" +
|
||||||
|
"lYkCHAQQAQIABgUCUQZr1QAKCRDIIhUzoaoywhGzD/9PW8BdkzJXyR6fCXi4z682\n" +
|
||||||
|
"0/DvZkfYxHkOsaDBthjDwBnMaRZfNyP8QDQ9APequPSI43Kd3/RI+lof0NE2yXE2\n" +
|
||||||
|
"j7W33K1RnSXTunrZ+knKL2vsU2t0mpoBX3D7QGF9IwMl31JuOPV/pPJ9gK6mVyD5\n" +
|
||||||
|
"eq8fJgHkyI351OOnLFK7THDHF6lY2MeBSs8EsH8u0Qe4drb8AShOIEQxbG3NoCSp\n" +
|
||||||
|
"SEPeAuPO8KoYSsUCDrJqHhK/UtLkORjVQpwv1T2hZSXe4kEoUn9rccpc+dY8mype\n" +
|
||||||
|
"FZlq233hOfPRsYWX4z22JLK6XjuC9LmRN14ZjSQsYTbmHUKKn/yd5+JFeh9jaxQe\n" +
|
||||||
|
"vKg7ZYeHOOl+9xNiMOCyeADvz15tqFSmeNtPMpzw/gUrMuootmrYVw6wsgG3rWQx\n" +
|
||||||
|
"ljKMtR2Xq6/VEvE6RgVzE6Qp6ylFpQ332VuMCErrbUGwaimXbRQkX/C54U5pWdxg\n" +
|
||||||
|
"O4OxNWanKawYNJXQ//gnNosr9EOQQudQ/Adkq5BnnC57XRzpGz7G3gwndBzI1nkp\n" +
|
||||||
|
"lXJEpbh6+4WBxBulFbrv9VD2ot17uO9kQVWM7RLq4GI++x3pg1CQVdxo5yYMRcca\n" +
|
||||||
|
"7gEGeBR/OzYKJNKyFxOcSbtMT54WGeptWU5IPSaR3corZyBcu0LJCzldXLgfF5jY\n" +
|
||||||
|
"sP9hNqhK3hxgKelsI0ECd4kCHAQQAQIABgUCUQWlrwAKCRCyQTxm3Sw/6oi/D/9M\n" +
|
||||||
|
"70bk62Gvqhe9X3bUvrrff1yieTa2UhTDqT3EG1cMRdLa1aGJsjbEBy2hr9u7vCWP\n" +
|
||||||
|
"2NVYkPSIo2Q2+t9LfeT19Q+nzG11ynAZ+MM+pY63aHN8a/YrSEGLYbRM41Q8337/\n" +
|
||||||
|
"SzppV737R9HYibj9pLo2m8q03DnjoacEfBO6RExoXoVuNn3J7rkaA52XNgWrj/MP\n" +
|
||||||
|
"fcMVCJqUsBA69ZliFWNmizUeeM3yWvj6HSeDBxwz7l5pmj3Gq/50qw0vzYe6t05M\n" +
|
||||||
|
"BGow8xqI2rstvr7wF/D2WZyABIIDEwlpE1kfBCB6Yifdq8go10dBJGH7KCETo7er\n" +
|
||||||
|
"/a5NVV8ur4sgSJsOBrHYW0aHMJWvCp2mSooX4VOWhd91PJ1vUD6+3H8IB1NGWB5v\n" +
|
||||||
|
"CVrqVTpYViZDhYcAbIEqF98vOwkjJga4w0BFUqNC5IwbWQ4VH5pDHuSviUyFIWii\n" +
|
||||||
|
"ejAJLmZu5ycg3fHXJ51HDJlgyttP16W5NPJnPpOe7bKipcUcKER7YDOlPF/z2y7E\n" +
|
||||||
|
"Af9lp3uPLx7iIN46iAAlbwSkMny52DNSxCaOsdvmuB5nIkfn762+1cURFvgACYh0\n" +
|
||||||
|
"NeQawtn2tQGTYQjw6P21uJGXi8kmy12iFHGhi7vptVVZxNDT1GvcozyZ3bdOWN2T\n" +
|
||||||
|
"/S/x3o+RO8kbdMgHjkOOHKNHYvfQpKAhAbVD5lCNCokBHAQTAQIABgUCUQbiuAAK\n" +
|
||||||
|
"CRCylLwGx1UqeiraB/9yKTH4xcj0e13D8zRCyTcpQzoJwihllFVbtOYV07dcKi3d\n" +
|
||||||
|
"SKoMPpW3W2yr3ADHFDTpHhNj55ZOqq985k9hrR2KccbFmvSAkqDJluBeK49AK7uF\n" +
|
||||||
|
"4UW1kAHg2XqZB8ieiyITNsvNpZaB9a0dIGnuAoNJdE/b+Jwx8h9di5IPjVc9P0Sz\n" +
|
||||||
|
"z6u1z+H4xlUc7rB0VW3xlLJEUvmflg2fqGZvJ/jE6F5/Sn3oPZ2Bevoz+F7gqsOW\n" +
|
||||||
|
"LLjQbrulG/vLg5zXFqYNPU/2x34Z6bwEmmvWSYwY+bXlfYH71rEVzSzK3oA2KyyU\n" +
|
||||||
|
"nCD4v6XbqdEj9Iaiqvz5wggs+pzRh7s4py9TjuhFiQEcBBABAgAGBQJRCCroAAoJ\n" +
|
||||||
|
"EDVPpeI+qihix50H/3bfZkaaYo3OnmyQbj6KGcuptkBSdr77CfRgho3R0mOrFT63\n" +
|
||||||
|
"1vUv8l3pUwCNxCWH1wm5v3QvYpCKs/G+J8fJvzJjInw+/CcEUtPJuO/A6WCsJYZ+\n" +
|
||||||
|
"42O1Eu5IE6BpQhQwvq/v+ggJNdWZLNDipVBTVDtB6J8RBHk3ncUx6upTWVcQlvSv\n" +
|
||||||
|
"kCwJ7hRMglM9V8jYBqhlR/oxDxbDaj9TXCkpQc6VuM8VLNKaA1Ih7tEvCtoW1+0d\n" +
|
||||||
|
"ZQIqEn3DkWun1CtezBP/xR9BeA4tGselDnAZACUD9FxSza70FCoD2m/bUHFvsvgY\n" +
|
||||||
|
"4cGNSjg+ZRgS3BikUgVKJAL/A3qhyF25ATSLFT6IRgQQEQIABgUCUQq1+AAKCRAE\n" +
|
||||||
|
"tb81V3CDSkyLAJ9LEg8I/lyaUp0W6XUCfZ+yeFJk7QCggB2oBTyBin4VRDYg5aFW\n" +
|
||||||
|
"2vDbKHSJAhwEEwEIAAYFAlEMbTQACgkQrpk18w7U8BDCuxAAiDD0h9v8UksJVjiz\n" +
|
||||||
|
"RpAA6qiZyddjghzcO4GgAqxv3fdqBNZ5uYCtbYEeEHLWHmd/O2f98i4PRgHe6xlo\n" +
|
||||||
|
"gC+7TQAG/O4YVNtnntCFmx0G4z6/1nZc+IbfYHSmk0tszo6zIO0NOzek5N8t8GzD\n" +
|
||||||
|
"QknSixKh8z0hWmseUz0RJKagmxkbnDOLvfVAOIbJW3iKVauIeyxqE/5gNIWn+/vT\n" +
|
||||||
|
"p87MxSpMMrgWHjMHuyaxdN93t+ea7XZ+iWQCd9HQ7RhylATUPsoeiwjUm0O16jqX\n" +
|
||||||
|
"OGLFJM9PFKS4DIRMze4JRrdFlIxOQP4xjbu8VS4hGJK8Gi46QMhB8TLR3qOzpZyU\n" +
|
||||||
|
"S2f+Kjt4RoYa8iwbWbfB8jCSGf+lgQPsNDVEdaRJQPqClKqkfldlt32E9GULx9ln\n" +
|
||||||
|
"Ncyfb0CXt06Gt9dFXIP/tU0cgZm8KsmSEl11TofZ/UL/KLgIJjiw80ZqUSrFKARz\n" +
|
||||||
|
"6UfxQwkbIWMu5BXU5t/8P/SQawpSbXugnejQ9Q7wNZ593SgH8VdXrGS5zNagGaIj\n" +
|
||||||
|
"GJsj4LzCuYc2a12w1zuWeVIGCbJyoWzd6PYfIleHZo2ISRnAR6S24yTKPkMwiutT\n" +
|
||||||
|
"VthVNeE33Yek6YQZ5Xdmgfy/q98IdV12U+sA1LQOABoJF+goBNHh1AlfCAuLbgmo\n" +
|
||||||
|
"BYSjSGXQ7XjaiNUeexAV8f7TEhiIRgQTEQIABgUCUXqlbQAKCRArrjz22v+wABZn\n" +
|
||||||
|
"AKCs+Z19eY/NmrSzPQsZ7SlHBNremACeJehgL8VdZkPMiW3xUbEki2ji62u0MURv\n" +
|
||||||
|
"bWluaWsgU2Now7xybWFubiA8KzQ5MTcxNjU4MTQ1MkBjcnlwdG9jYWxsLm9yZz6J\n" +
|
||||||
|
"AR8EMAECAAkFAlD393UCHQAACgkQcYwHAQABIoJawQf/RpeNorZbtnIZmNz8y2Ko\n" +
|
||||||
|
"3xNKEGlf4XoFY7irtJo4ImO5Muftr+Y20rhIQYTf7tBNaFabj2nb223d7Apg84lR\n" +
|
||||||
|
"MGSUA9+5V+C0yjALA1SttqRW2evd4NX9/N5WNrf4z+S3C2QfD0mL41eUiIgLgJhc\n" +
|
||||||
|
"Hmll9wiZyJzr2t9GNkOk0iYJzkqDBhdxj2Zl3OcD3v6P6IUM+3RWzk5tFmt/YHvN\n" +
|
||||||
|
"aXPWgND/8OVAdd470p/aK10qZ9v51ZxWN1OT/HVZrNh5rLdfroeNjFKtS/pl1wMT\n" +
|
||||||
|
"ImtN03lhhyWR0a++Eowh6zEJKeDfg7C+2djqsB9C8nMDZbmQdNLFJNQRVSiK4i8E\n" +
|
||||||
|
"4YkBPgQTAQIAKAUCTp8R3QIbIwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgEC\n" +
|
||||||
|
"F4AACgkQcYwHAQABIoI6KAf/THY5iMm+CH5dJOTAwuHUyuKduvSVFxq+1WX3rX21\n" +
|
||||||
|
"x670fhHx9WarvE+CsgreUzfBVZxq1cq2oB72KyFsa45utKt761x4QEOM01CRQO21\n" +
|
||||||
|
"hIgl+wed9CRgzO17OzZ/E/G47/P8pHxm8kXwictTWqZ4rlgfzOg7YcY5An05rFH2\n" +
|
||||||
|
"J+fxUVfdjZ2u75XDE6CAHV1hMvrRwatnJQ33S1/yRCdhT3qad7U7wrbtiu7Y4KNi\n" +
|
||||||
|
"gM4ur+kGqRSNWN6/4v7OHRgj0Pp6jbs2pXqccR9rAhlKhnatd6RKb1+LlYEyblSC\n" +
|
||||||
|
"76PIZm26h8qhY8UKrj9a2ydmWDY2uquxbRLvjrT8suZZebQvRG9taW5payBTY2jD\n" +
|
||||||
|
"vHJtYW5uIDxzY2h1ZXJtYW5uQGlici5jcy50dS1icy5kZT6JAT4EEwECACgFAlLq\n" +
|
||||||
|
"hOACGyMFCQlmAYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHGMBwEAASKC\n" +
|
||||||
|
"nKcH/ik9LmnpEclsCDfzYqTEbVOSNZA30YcvwdlsHlzjRm+KjJC3D350147o5D60\n" +
|
||||||
|
"xq0UBUxKeXJPPMofeRgzTiAjTdv2ilJEZe8bMM5b7gcmp92q4tAY3OxcGNprIswc\n" +
|
||||||
|
"eb5hG4kY905WAy076iaQOD9z0Y+bXWQo0OHc07lc4+8ZLG9u+rGDjfF0x4UWgkAQ\n" +
|
||||||
|
"d8Thth4lTzdZR/kLLBCdlOyb9sAKqfbxbfATDQEceex7dZF/uJRCvFojHMtDbhxe\n" +
|
||||||
|
"xdfEjWbsJRQR0KKTHYS02zqhVu34elwuRSWf1OOR7ynh5nD5CCAAmVbi+x7y281i\n" +
|
||||||
|
"YYTchi/s71CSs81OtFtaBfVNSeq5AQ0ES4b+oAEIANr825Ns9mewUTHNfZ3/xK7R\n" +
|
||||||
|
"mp+nVLgOoyJDZF+Qum08RnFiECCiDTPlHIUuZt6jUu8vb/TKH5bdviFkC2MQPhm0\n" +
|
||||||
|
"/5sbbbqbV6wMnXfMd/RTPkIeeheEumY/5n4oYYGuVTZ+0MBouPY/wXfxp6HkqtuI\n" +
|
||||||
|
"qUZm8Bmy9AEScxiBURBu4MOr9/c9niLFlnpFLhEsSm17nS6/tdEJGdMRb3WNFn5+\n" +
|
||||||
|
"bE8w9e8RqPlye9SFZHsjmv9jCZaW5fZkcdDTcDClPVvIBtUl6y/kkh0RfIwdU+T5\n" +
|
||||||
|
"GRI8XekgI8WkvEqxTaQqn03C79zU3nhRuSgy8E492uaTmwpmAXC/m4Z6luTNPrEA\n" +
|
||||||
|
"EQEAAYkBJQQYAQIADwUCS4b+oAIbDAUJCWYBgAAKCRBxjAcBAAEignQvB/915fHh\n" +
|
||||||
|
"7di/yoyJfmufnj4fJ9Lt6OYyXvKetXpC+dLx7zH61KCeKosgWIN5HyY2Si1ZfGdO\n" +
|
||||||
|
"JQ1L0d9Y+TsRVslU34uY7DuYLs4yGNwFdI4r6Y+PHIAE0Cd3xxf8xFr8oiinPMvm\n" +
|
||||||
|
"SVDO2MbF0W/TnYwvyoN7Of0uAUdFY0sRupamPgNEz7dTZ+UoKgRFzfPh4zUb5Hav\n" +
|
||||||
|
"loqJCE/BEJ4wkxYTaJfFdJq+3WAZdd0f1OZLLDcCCvbZHNYBvpPauoVq3LD8MHXz\n" +
|
||||||
|
"hCRY9Rp2ZxX92PrFiSNpKheP30iZM8VInDfPGaApQU1y8R2uLL0I/7XWiFtpmR6e\n" +
|
||||||
|
"k3wUxv46o0y15asU\n" +
|
||||||
|
"=Bbew\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,302 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013-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.sufficientlysecure.keychain.demo;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpApi;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
public class OpenPgpProviderActivity extends Activity {
|
||||||
|
private EditText mMessage;
|
||||||
|
private EditText mCiphertext;
|
||||||
|
private EditText mEncryptUserIds;
|
||||||
|
private Button mSign;
|
||||||
|
private Button mEncrypt;
|
||||||
|
private Button mSignAndEncrypt;
|
||||||
|
private Button mDecryptAndVerify;
|
||||||
|
|
||||||
|
private OpenPgpServiceConnection mServiceConnection;
|
||||||
|
|
||||||
|
public static final int REQUEST_CODE_SIGN = 9910;
|
||||||
|
public static final int REQUEST_CODE_ENCRYPT = 9911;
|
||||||
|
public static final int REQUEST_CODE_SIGN_AND_ENCRYPT = 9912;
|
||||||
|
public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
setContentView(R.layout.openpgp_provider);
|
||||||
|
|
||||||
|
mMessage = (EditText) findViewById(R.id.crypto_provider_demo_message);
|
||||||
|
mCiphertext = (EditText) findViewById(R.id.crypto_provider_demo_ciphertext);
|
||||||
|
mEncryptUserIds = (EditText) findViewById(R.id.crypto_provider_demo_encrypt_user_id);
|
||||||
|
mSign = (Button) findViewById(R.id.crypto_provider_demo_sign);
|
||||||
|
mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt);
|
||||||
|
mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt);
|
||||||
|
mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify);
|
||||||
|
|
||||||
|
mSign.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
sign(new Bundle());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
encrypt(new Bundle());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mSignAndEncrypt.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
signAndEncrypt(new Bundle());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mDecryptAndVerify.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
decryptAndVerify(new Bundle());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
String providerPackageName = settings.getString("openpgp_provider_list", "");
|
||||||
|
if (TextUtils.isEmpty(providerPackageName)) {
|
||||||
|
Toast.makeText(this, "No OpenPGP Provider selected!", Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
// bind to service
|
||||||
|
mServiceConnection = new OpenPgpServiceConnection(
|
||||||
|
OpenPgpProviderActivity.this, providerPackageName);
|
||||||
|
mServiceConnection.bindToService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleError(final OpenPgpError error) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(OpenPgpProviderActivity.this,
|
||||||
|
"onError id:" + error.getErrorId() + "\n\n" + error.getMessage(),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
Log.e(Constants.TAG, "onError getErrorId:" + error.getErrorId());
|
||||||
|
Log.e(Constants.TAG, "onError getMessage:" + error.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSignature(final OpenPgpSignatureResult sigResult) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(OpenPgpProviderActivity.this,
|
||||||
|
sigResult.toString(),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes input from message or ciphertext EditText and turns it into a ByteArrayInputStream
|
||||||
|
*
|
||||||
|
* @param ciphertext
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private InputStream getInputstream(boolean ciphertext) {
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
String inputStr = null;
|
||||||
|
if (ciphertext) {
|
||||||
|
inputStr = mCiphertext.getText().toString();
|
||||||
|
} else {
|
||||||
|
inputStr = mMessage.getText().toString();
|
||||||
|
}
|
||||||
|
is = new ByteArrayInputStream(inputStr.getBytes("UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyCallback implements OpenPgpApi.IOpenPgpCallback {
|
||||||
|
boolean returnToCiphertextField;
|
||||||
|
ByteArrayOutputStream os;
|
||||||
|
int requestCode;
|
||||||
|
|
||||||
|
private MyCallback(boolean returnToCiphertextField, ByteArrayOutputStream os, int requestCode) {
|
||||||
|
this.returnToCiphertextField = returnToCiphertextField;
|
||||||
|
this.os = os;
|
||||||
|
this.requestCode = requestCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReturn(Bundle result) {
|
||||||
|
switch (result.getInt(OpenPgpConstants.RESULT_CODE)) {
|
||||||
|
case OpenPgpConstants.RESULT_CODE_SUCCESS: {
|
||||||
|
try {
|
||||||
|
Log.d(OpenPgpConstants.TAG, "result: " + os.toByteArray().length
|
||||||
|
+ " str=" + os.toString("UTF-8"));
|
||||||
|
|
||||||
|
if (returnToCiphertextField) {
|
||||||
|
mCiphertext.setText(os.toString("UTF-8"));
|
||||||
|
} else {
|
||||||
|
mMessage.setText(os.toString("UTF-8"));
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getBoolean(OpenPgpConstants.RESULT_SIGNATURE, false)) {
|
||||||
|
OpenPgpSignatureResult sigResult
|
||||||
|
= result.getParcelable(OpenPgpConstants.RESULT_SIGNATURE);
|
||||||
|
handleSignature(sigResult);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED: {
|
||||||
|
PendingIntent pi = result.getParcelable(OpenPgpConstants.RESULT_INTENT);
|
||||||
|
try {
|
||||||
|
OpenPgpProviderActivity.this.startIntentSenderForResult(pi.getIntentSender(),
|
||||||
|
requestCode, null, 0, 0, 0);
|
||||||
|
} catch (IntentSender.SendIntentException e) {
|
||||||
|
Log.e(Constants.TAG, "SendIntentException", e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OpenPgpConstants.RESULT_CODE_ERROR: {
|
||||||
|
OpenPgpError error = result.getParcelable(OpenPgpConstants.RESULT_ERRORS);
|
||||||
|
handleError(error);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sign(Bundle params) {
|
||||||
|
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
|
InputStream is = getInputstream(false);
|
||||||
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||||
|
api.sign(params, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encrypt(Bundle params) {
|
||||||
|
params.putStringArray(OpenPgpConstants.PARAMS_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||||
|
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
|
InputStream is = getInputstream(false);
|
||||||
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||||
|
api.encrypt(params, is, os, new MyCallback(true, os, REQUEST_CODE_ENCRYPT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void signAndEncrypt(Bundle params) {
|
||||||
|
params.putStringArray(OpenPgpConstants.PARAMS_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||||
|
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
|
InputStream is = getInputstream(false);
|
||||||
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||||
|
api.signAndEncrypt(params, is, os, new MyCallback(true, os, REQUEST_CODE_SIGN_AND_ENCRYPT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decryptAndVerify(Bundle params) {
|
||||||
|
params.putBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
|
InputStream is = getInputstream(true);
|
||||||
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
OpenPgpApi api = new OpenPgpApi(this, mServiceConnection.getService());
|
||||||
|
api.decryptAndVerify(params, is, os, new MyCallback(false, os, REQUEST_CODE_DECRYPT_AND_VERIFY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
Log.d(Constants.TAG, "onActivityResult resultCode: " + resultCode);
|
||||||
|
|
||||||
|
// try again after user interaction
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
/*
|
||||||
|
* The params originally given to the pgp method are are again
|
||||||
|
* returned here to be used when calling again after user interaction.
|
||||||
|
*
|
||||||
|
* They also contain results from the user interaction which happened,
|
||||||
|
* for example selected key ids.
|
||||||
|
*/
|
||||||
|
Bundle params = data.getBundleExtra(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_CODE_SIGN: {
|
||||||
|
sign(params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REQUEST_CODE_ENCRYPT: {
|
||||||
|
encrypt(params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REQUEST_CODE_SIGN_AND_ENCRYPT: {
|
||||||
|
signAndEncrypt(params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case REQUEST_CODE_DECRYPT_AND_VERIFY: {
|
||||||
|
decryptAndVerify(params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
if (mServiceConnection != null) {
|
||||||
|
mServiceConnection.unbindFromService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
@ -0,0 +1,108 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/parent_scroll"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Encrypt UserIds (split with ',')"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/crypto_provider_demo_encrypt_user_id"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="dominik@dominikschuermann.de"
|
||||||
|
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Message"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/child_scroll1"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/crypto_provider_demo_message"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:text="message"
|
||||||
|
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Ciphertext"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/child_scroll2"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/crypto_provider_demo_ciphertext"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="ciphertext"
|
||||||
|
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/crypto_provider_demo_sign"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Sign"
|
||||||
|
android:layout_gravity="center_vertical" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/crypto_provider_demo_encrypt"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Encrypt"
|
||||||
|
android:layout_gravity="center_vertical" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/crypto_provider_demo_sign_and_encrypt"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Sign and Encrypt"
|
||||||
|
android:layout_gravity="center_vertical" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/crypto_provider_demo_decrypt_and_verify"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Decrypt and Verify" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
@ -1,23 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<PreferenceCategory android:title="Intent" >
|
<PreferenceCategory android:title="OpenKeychain Intents">
|
||||||
<Preference
|
<Preference
|
||||||
android:key="intent_demo"
|
android:key="intent_demo"
|
||||||
android:title="Intent Demo" />
|
android:title="Intent Demo" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<!-- <PreferenceCategory android:title="AIDL" > -->
|
<PreferenceCategory android:title="OpenPGP Provider API">
|
||||||
<!-- <Preference -->
|
<org.openintents.openpgp.util.OpenPgpListPreference
|
||||||
<!-- android:key="aidl_demo2" -->
|
|
||||||
<!-- android:title="AIDL Demo (ACCESS_KEYS permission)" /> -->
|
|
||||||
<!-- </PreferenceCategory> -->
|
|
||||||
<PreferenceCategory android:title="OpenPGP Provider" >
|
|
||||||
<org.openintents.openpgp.OpenPgpListPreference
|
|
||||||
android:key="openpgp_provider_list"
|
android:key="openpgp_provider_list"
|
||||||
android:title="Select OpenPGP Provider!" />
|
android:title="Select OpenPGP Provider!" />
|
||||||
<Preference
|
<Preference
|
||||||
android:key="openpgp_provider_demo"
|
android:key="openpgp_provider_demo"
|
||||||
android:title="OpenPGP Provider" />
|
android:title="OpenPGP Provider Demo" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="Intents (org.sufficientlysecure.keychain.action.)">
|
||||||
|
<Preference
|
||||||
|
android:key="ENCRYPT"
|
||||||
|
android:title="ENCRYPT" />
|
||||||
|
<Preference
|
||||||
|
android:key="ENCRYPT_URI"
|
||||||
|
android:title="ENCRYPT with Uri" />
|
||||||
|
<Preference
|
||||||
|
android:key="DECRYPT"
|
||||||
|
android:title="DECRYPT" />
|
||||||
|
<Preference
|
||||||
|
android:key="IMPORT_KEY"
|
||||||
|
android:title="IMPORT_KEY" />
|
||||||
|
<Preference
|
||||||
|
android:key="IMPORT_KEY_FROM_KEYSERVER"
|
||||||
|
android:title="IMPORT_KEY_FROM_KEYSERVER" />
|
||||||
|
<Preference
|
||||||
|
android:key="IMPORT_KEY_FROM_QR_CODE"
|
||||||
|
android:title="IMPORT_KEY_FROM_QR_CODE" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="Special Intents">
|
||||||
|
<Preference
|
||||||
|
android:key="openpgp4fpr"
|
||||||
|
android:title="VIEW openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@ -1,6 +1,6 @@
|
|||||||
#Mon Jan 27 14:45:08 CET 2014
|
#Sun Feb 09 19:19:25 CET 2014
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-bin.zip
|
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
|
BIN
OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
OpenPGP-Keychain-API/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Fri Feb 14 01:26:40 CET 2014
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
|
164
OpenPGP-Keychain-API/gradlew
vendored
Executable file
@ -0,0 +1,164 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >&-
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >&-
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
OpenPGP-Keychain-API/gradlew.bat
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
29
OpenPGP-Keychain-API/libraries/keychain-api-library/.gitignore
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#Android specific
|
||||||
|
bin
|
||||||
|
gen
|
||||||
|
obj
|
||||||
|
lint.xml
|
||||||
|
local.properties
|
||||||
|
release.properties
|
||||||
|
ant.properties
|
||||||
|
*.class
|
||||||
|
*.apk
|
||||||
|
|
||||||
|
#Gradle
|
||||||
|
.gradle
|
||||||
|
build
|
||||||
|
gradle.properties
|
||||||
|
|
||||||
|
#Maven
|
||||||
|
target
|
||||||
|
pom.xml.*
|
||||||
|
|
||||||
|
#Eclipse
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings
|
||||||
|
.metadata
|
||||||
|
|
||||||
|
#IntelliJ IDEA
|
||||||
|
.idea
|
||||||
|
*.iml
|
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.sufficientlysecure.keychain.api"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0" >
|
||||||
|
|
||||||
|
<uses-sdk
|
||||||
|
android:minSdkVersion="9"
|
||||||
|
android:targetSdkVersion="19" />
|
||||||
|
|
||||||
|
<application/>
|
||||||
|
|
||||||
|
</manifest>
|
202
OpenPGP-Keychain-API/libraries/keychain-api-library/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
@ -0,0 +1,35 @@
|
|||||||
|
// please leave this here, so this library builds on its own
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:0.8.3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'android-library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 19
|
||||||
|
buildToolsVersion '19.0.1'
|
||||||
|
|
||||||
|
// NOTE: We are using the old folder structure to also support Eclipse
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
java.srcDirs = ['src']
|
||||||
|
resources.srcDirs = ['src']
|
||||||
|
aidl.srcDirs = ['src']
|
||||||
|
renderscript.srcDirs = ['src']
|
||||||
|
res.srcDirs = ['res']
|
||||||
|
assets.srcDirs = ['assets']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not abort build if lint finds errors
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system edit
|
||||||
|
# "ant.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
|
# Project target.
|
||||||
|
target=android-19
|
||||||
|
android.library=true
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="openpgp_list_preference_none">None</string>
|
||||||
|
|
||||||
|
</resources>
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
interface IOpenPgpService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General extras
|
||||||
|
* --------------
|
||||||
|
*
|
||||||
|
* Bundle params:
|
||||||
|
* int api_version (required)
|
||||||
|
* boolean ascii_armor (request ascii armor for ouput)
|
||||||
|
*
|
||||||
|
* returned Bundle:
|
||||||
|
* int result_code (0, 1, or 2 (see OpenPgpConstants))
|
||||||
|
* OpenPgpError error (if result_code == 0)
|
||||||
|
* Intent intent (if result_code == 2)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign only
|
||||||
|
*
|
||||||
|
* optional params:
|
||||||
|
* String passphrase (for key passphrase)
|
||||||
|
*/
|
||||||
|
Bundle sign(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt
|
||||||
|
*
|
||||||
|
* Bundle params:
|
||||||
|
* long[] key_ids
|
||||||
|
* or
|
||||||
|
* String[] user_ids (= emails of recipients) (if more than one key has this user_id, a PendingIntent is returned)
|
||||||
|
*
|
||||||
|
* optional params:
|
||||||
|
* String passphrase (for key passphrase)
|
||||||
|
*/
|
||||||
|
Bundle encrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign and encrypt
|
||||||
|
*
|
||||||
|
* Bundle params:
|
||||||
|
* same as in encrypt()
|
||||||
|
*/
|
||||||
|
Bundle signAndEncrypt(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
|
||||||
|
* and also signed-only input.
|
||||||
|
*
|
||||||
|
* returned Bundle:
|
||||||
|
* OpenPgpSignatureResult signature_result
|
||||||
|
*/
|
||||||
|
Bundle decryptAndVerify(in Bundle params, in ParcelFileDescriptor input, in ParcelFileDescriptor output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves key ids based on given user ids (=emails)
|
||||||
|
*
|
||||||
|
* Bundle params:
|
||||||
|
* String[] user_ids
|
||||||
|
*
|
||||||
|
* returned Bundle:
|
||||||
|
* long[] key_ids
|
||||||
|
*/
|
||||||
|
Bundle getKeyIds(in Bundle params);
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -20,10 +20,13 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
public class OpenPgpError implements Parcelable {
|
public class OpenPgpError implements Parcelable {
|
||||||
|
public static final int CLIENT_SIDE_ERROR = -1;
|
||||||
|
|
||||||
public static final int GENERIC_ERROR = 0;
|
public static final int GENERIC_ERROR = 0;
|
||||||
public static final int NO_OR_WRONG_PASSPHRASE = 1;
|
public static final int INCOMPATIBLE_API_VERSIONS = 1;
|
||||||
public static final int NO_USER_IDS = 2;
|
|
||||||
public static final int USER_INTERACTION_REQUIRED = 3;
|
public static final int NO_OR_WRONG_PASSPHRASE = 2;
|
||||||
|
public static final int NO_USER_IDS = 3;
|
||||||
|
|
||||||
int errorId;
|
int errorId;
|
||||||
String message;
|
String message;
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -22,14 +22,14 @@ import android.os.Parcelable;
|
|||||||
public class OpenPgpSignatureResult implements Parcelable {
|
public class OpenPgpSignatureResult implements Parcelable {
|
||||||
// generic error on signature verification
|
// generic error on signature verification
|
||||||
public static final int SIGNATURE_ERROR = 0;
|
public static final int SIGNATURE_ERROR = 0;
|
||||||
// successfully verified signature, with trusted public key
|
// successfully verified signature, with certified public key
|
||||||
public static final int SIGNATURE_SUCCESS_TRUSTED = 1;
|
public static final int SIGNATURE_SUCCESS_CERTIFIED = 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
|
// you can retrieve the key with
|
||||||
// getKeys(new String[] {String.valueOf(signatureResult.getKeyId)}, true, callback)
|
// 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 certified public key
|
||||||
public static final int SIGNATURE_SUCCESS_UNTRUSTED = 3;
|
public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3;
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
boolean signatureOnly;
|
boolean signatureOnly;
|
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
IOpenPgpService mService;
|
||||||
|
Context mContext;
|
||||||
|
|
||||||
|
private static final int OPERATION_SIGN = 0;
|
||||||
|
private static final int OPERATION_ENCRYPT = 1;
|
||||||
|
private static final int OPERATION_SIGN_ENCRYPT = 2;
|
||||||
|
private static final int OPERATION_DECRYPT_VERIFY = 3;
|
||||||
|
private static final int OPERATION_GET_KEY_IDS = 4;
|
||||||
|
|
||||||
|
public OpenPgpApi(Context context, IOpenPgpService service) {
|
||||||
|
this.mContext = context;
|
||||||
|
this.mService = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle sign(InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_SIGN, new Bundle(), is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle sign(Bundle params, InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_SIGN, params, is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sign(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||||
|
executeApiAsync(OPERATION_SIGN, params, is, os, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle encrypt(InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_ENCRYPT, new Bundle(), is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle encrypt(Bundle params, InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_ENCRYPT, params, is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||||
|
executeApiAsync(OPERATION_ENCRYPT, params, is, os, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle signAndEncrypt(InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_SIGN_ENCRYPT, new Bundle(), is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle signAndEncrypt(Bundle params, InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_SIGN_ENCRYPT, params, is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void signAndEncrypt(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||||
|
executeApiAsync(OPERATION_SIGN_ENCRYPT, params, is, os, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle decryptAndVerify(InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_DECRYPT_VERIFY, new Bundle(), is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle decryptAndVerify(Bundle params, InputStream is, final OutputStream os) {
|
||||||
|
return executeApi(OPERATION_DECRYPT_VERIFY, params, is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decryptAndVerify(Bundle params, InputStream is, final OutputStream os, IOpenPgpCallback callback) {
|
||||||
|
executeApiAsync(OPERATION_DECRYPT_VERIFY, params, is, os, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle getKeyIds(Bundle params) {
|
||||||
|
return executeApi(OPERATION_GET_KEY_IDS, params, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOpenPgpCallback {
|
||||||
|
void onReturn(final Bundle result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Bundle> {
|
||||||
|
int operationId;
|
||||||
|
Bundle params;
|
||||||
|
InputStream is;
|
||||||
|
OutputStream os;
|
||||||
|
IOpenPgpCallback callback;
|
||||||
|
|
||||||
|
private OpenPgpAsyncTask(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||||
|
this.operationId = operationId;
|
||||||
|
this.params = params;
|
||||||
|
this.is = is;
|
||||||
|
this.os = os;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Bundle doInBackground(Void... unused) {
|
||||||
|
return executeApi(operationId, params, is, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(Bundle result) {
|
||||||
|
callback.onReturn(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeApiAsync(int operationId, Bundle params, InputStream is, OutputStream os, IOpenPgpCallback callback) {
|
||||||
|
new OpenPgpAsyncTask(operationId, params, is, os, callback).execute((Void[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bundle executeApi(int operationId, Bundle params, InputStream is, OutputStream os) {
|
||||||
|
try {
|
||||||
|
params.putInt(OpenPgpConstants.PARAMS_API_VERSION, OpenPgpConstants.API_VERSION);
|
||||||
|
|
||||||
|
Bundle result = null;
|
||||||
|
|
||||||
|
if (operationId == OPERATION_GET_KEY_IDS) {
|
||||||
|
result = mService.getKeyIds(params);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// send the input and output pfds
|
||||||
|
ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is,
|
||||||
|
new ParcelFileDescriptorUtil.IThreadListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThreadFinished(Thread thread) {
|
||||||
|
Log.d(OpenPgpConstants.TAG, "Copy to service finished");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os,
|
||||||
|
new ParcelFileDescriptorUtil.IThreadListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThreadFinished(Thread thread) {
|
||||||
|
Log.d(OpenPgpConstants.TAG, "Service finished writing!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// blocks until result is ready
|
||||||
|
switch (operationId) {
|
||||||
|
case OPERATION_SIGN:
|
||||||
|
result = mService.sign(params, input, output);
|
||||||
|
break;
|
||||||
|
case OPERATION_ENCRYPT:
|
||||||
|
result = mService.encrypt(params, input, output);
|
||||||
|
break;
|
||||||
|
case OPERATION_SIGN_ENCRYPT:
|
||||||
|
result = mService.signAndEncrypt(params, input, output);
|
||||||
|
break;
|
||||||
|
case OPERATION_DECRYPT_VERIFY:
|
||||||
|
result = mService.decryptAndVerify(params, input, output);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// close() is required to halt the TransferThread
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
// set class loader to current context to allow unparcelling
|
||||||
|
// of OpenPgpError and OpenPgpSignatureResult
|
||||||
|
// http://stackoverflow.com/a/3806769
|
||||||
|
result.setClassLoader(mContext.getClassLoader());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(OpenPgpConstants.TAG, "Exception", e);
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||||
|
new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class OpenPgpConstants {
|
||||||
|
|
||||||
|
public static final String TAG = "OpenPgp API";
|
||||||
|
|
||||||
|
public static final int API_VERSION = 1;
|
||||||
|
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
||||||
|
|
||||||
|
|
||||||
|
/* Bundle params */
|
||||||
|
public static final String PARAMS_API_VERSION = "api_version";
|
||||||
|
// 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 PARAMS_REQUEST_ASCII_ARMOR = "ascii_armor";
|
||||||
|
// (for encrypt method)
|
||||||
|
public static final String PARAMS_USER_IDS = "user_ids";
|
||||||
|
public static final String PARAMS_KEY_IDS = "key_ids";
|
||||||
|
// optional parameter:
|
||||||
|
public static final String PARAMS_PASSPHRASE = "passphrase";
|
||||||
|
|
||||||
|
/* Service Bundle returns */
|
||||||
|
public static final String RESULT_CODE = "result_code";
|
||||||
|
public static final String RESULT_SIGNATURE = "signature";
|
||||||
|
public static final String RESULT_ERRORS = "error";
|
||||||
|
public static final String RESULT_INTENT = "intent";
|
||||||
|
|
||||||
|
// get actual error object from RESULT_ERRORS
|
||||||
|
public static final int RESULT_CODE_ERROR = 0;
|
||||||
|
// success!
|
||||||
|
public static final int RESULT_CODE_SUCCESS = 1;
|
||||||
|
// executeServiceMethod intent and do it again with params from intent
|
||||||
|
public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2;
|
||||||
|
|
||||||
|
/* PendingIntent returns */
|
||||||
|
public static final String PI_RESULT_PARAMS = "params";
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,18 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.openintents.openpgp;
|
package org.openintents.openpgp.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.app.AlertDialog.Builder;
|
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.content.res.TypedArray;
|
||||||
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;
|
||||||
@ -35,33 +31,21 @@ import android.widget.ArrayAdapter;
|
|||||||
import android.widget.ListAdapter;
|
import android.widget.ListAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.api.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does not extend ListPreference, but is very similar to it!
|
||||||
|
* http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source
|
||||||
|
*/
|
||||||
public class OpenPgpListPreference extends DialogPreference {
|
public class OpenPgpListPreference extends DialogPreference {
|
||||||
ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
|
private ArrayList<OpenPgpProviderEntry> mProviderList = new ArrayList<OpenPgpProviderEntry>();
|
||||||
private String mSelectedPackage;
|
private String mSelectedPackage;
|
||||||
|
|
||||||
public OpenPgpListPreference(Context context, AttributeSet attrs) {
|
public OpenPgpListPreference(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
|
||||||
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(
|
|
||||||
new Intent(OpenPgpConstants.SERVICE_INTENT), PackageManager.GET_META_DATA);
|
|
||||||
if (!resInfo.isEmpty()) {
|
|
||||||
for (ResolveInfo resolveInfo : resInfo) {
|
|
||||||
if (resolveInfo.serviceInfo == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String packageName = resolveInfo.serviceInfo.packageName;
|
|
||||||
String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(context
|
|
||||||
.getPackageManager()));
|
|
||||||
Drawable icon = resolveInfo.serviceInfo.loadIcon(context.getPackageManager());
|
|
||||||
|
|
||||||
// get api version
|
|
||||||
ServiceInfo si = resolveInfo.serviceInfo;
|
|
||||||
int apiVersion = si.metaData.getInt("api_version");
|
|
||||||
|
|
||||||
mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon,
|
|
||||||
apiVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpListPreference(Context context) {
|
public OpenPgpListPreference(Context context) {
|
||||||
@ -69,20 +53,42 @@ public class OpenPgpListPreference extends DialogPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can be used to add "no selection"
|
* Public method to add new entries for legacy applications
|
||||||
*
|
*
|
||||||
* @param packageName
|
* @param packageName
|
||||||
* @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) {
|
||||||
int apiVersion) {
|
mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||||
mProviderList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon,
|
|
||||||
apiVersion));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(Builder builder) {
|
protected void onPrepareDialogBuilder(Builder builder) {
|
||||||
|
|
||||||
|
// get providers
|
||||||
|
mProviderList.clear();
|
||||||
|
Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
|
||||||
|
List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0);
|
||||||
|
if (!resInfo.isEmpty()) {
|
||||||
|
for (ResolveInfo resolveInfo : resInfo) {
|
||||||
|
if (resolveInfo.serviceInfo == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String packageName = resolveInfo.serviceInfo.packageName;
|
||||||
|
String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext()
|
||||||
|
.getPackageManager()));
|
||||||
|
Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager());
|
||||||
|
|
||||||
|
mProviderList.add(new OpenPgpProviderEntry(packageName, simpleName, icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add "none"-entry
|
||||||
|
mProviderList.add(0, new OpenPgpProviderEntry("",
|
||||||
|
getContext().getString(R.string.openpgp_list_preference_none),
|
||||||
|
getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize)));
|
||||||
|
|
||||||
// Init ArrayAdapter with OpenPGP Providers
|
// Init ArrayAdapter with OpenPGP Providers
|
||||||
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
|
ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(),
|
||||||
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mProviderList) {
|
android.R.layout.select_dialog_singlechoice, android.R.id.text1, mProviderList) {
|
||||||
@ -99,15 +105,6 @@ public class OpenPgpListPreference extends DialogPreference {
|
|||||||
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
|
int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f);
|
||||||
tv.setCompoundDrawablePadding(dp10);
|
tv.setCompoundDrawablePadding(dp10);
|
||||||
|
|
||||||
// disable if it has the wrong api_version
|
|
||||||
if (mProviderList.get(position).apiVersion == OpenPgpConstants.REQUIRED_API_VERSION) {
|
|
||||||
tv.setEnabled(true);
|
|
||||||
} else {
|
|
||||||
tv.setEnabled(false);
|
|
||||||
tv.setText(tv.getText() + " (API v" + mProviderList.get(position).apiVersion
|
|
||||||
+ ", needs v" + OpenPgpConstants.REQUIRED_API_VERSION + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -169,6 +166,16 @@ public class OpenPgpListPreference extends DialogPreference {
|
|||||||
return getEntryByValue(mSelectedPackage);
|
return getEntryByValue(mSelectedPackage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object onGetDefaultValue(TypedArray a, int index) {
|
||||||
|
return a.getString(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
|
||||||
|
setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
public String getEntryByValue(String packageName) {
|
public String getEntryByValue(String packageName) {
|
||||||
for (OpenPgpProviderEntry app : mProviderList) {
|
for (OpenPgpProviderEntry app : mProviderList) {
|
||||||
if (app.packageName.equals(packageName)) {
|
if (app.packageName.equals(packageName)) {
|
||||||
@ -183,14 +190,11 @@ 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
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.openintents.openpgp;
|
package org.openintents.openpgp.util;
|
||||||
|
|
||||||
import org.openintents.openpgp.IOpenPgpService;
|
import org.openintents.openpgp.IOpenPgpService;
|
||||||
|
|
||||||
@ -23,18 +23,17 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class OpenPgpServiceConnection {
|
public class OpenPgpServiceConnection {
|
||||||
private Context mApplicationContext;
|
private Context mApplicationContext;
|
||||||
|
|
||||||
private IOpenPgpService mService;
|
|
||||||
private boolean mBound;
|
private boolean mBound;
|
||||||
private String mCryptoProviderPackageName;
|
private IOpenPgpService mService;
|
||||||
|
private String mProviderPackageName;
|
||||||
|
|
||||||
public OpenPgpServiceConnection(Context context, String cryptoProviderPackageName) {
|
public OpenPgpServiceConnection(Context context, String providerPackageName) {
|
||||||
this.mApplicationContext = context.getApplicationContext();
|
this.mApplicationContext = context.getApplicationContext();
|
||||||
this.mCryptoProviderPackageName = cryptoProviderPackageName;
|
this.mProviderPackageName = providerPackageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOpenPgpService getService() {
|
public IOpenPgpService getService() {
|
||||||
@ -45,49 +44,45 @@ public class OpenPgpServiceConnection {
|
|||||||
return mBound;
|
return mBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServiceConnection mCryptoServiceConnection = new ServiceConnection() {
|
private ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
mService = IOpenPgpService.Stub.asInterface(service);
|
mService = IOpenPgpService.Stub.asInterface(service);
|
||||||
Log.d(OpenPgpConstants.TAG, "connected to service");
|
|
||||||
mBound = true;
|
mBound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
mService = null;
|
mService = null;
|
||||||
Log.d(OpenPgpConstants.TAG, "disconnected from service");
|
|
||||||
mBound = false;
|
mBound = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If not already bound, bind!
|
* If not already bound, bind to service!
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean bindToService() {
|
public boolean bindToService() {
|
||||||
if (mService == null && !mBound) { // if not already connected
|
// if not already bound...
|
||||||
|
if (mService == null && !mBound) {
|
||||||
try {
|
try {
|
||||||
Log.d(OpenPgpConstants.TAG, "not bound yet");
|
|
||||||
|
|
||||||
Intent serviceIntent = new Intent();
|
Intent serviceIntent = new Intent();
|
||||||
serviceIntent.setAction(IOpenPgpService.class.getName());
|
serviceIntent.setAction(IOpenPgpService.class.getName());
|
||||||
serviceIntent.setPackage(mCryptoProviderPackageName);
|
// NOTE: setPackage is very important to restrict the intent to this provider only!
|
||||||
mApplicationContext.bindService(serviceIntent, mCryptoServiceConnection,
|
serviceIntent.setPackage(mProviderPackageName);
|
||||||
|
mApplicationContext.bindService(serviceIntent, mServiceConnection,
|
||||||
Context.BIND_AUTO_CREATE);
|
Context.BIND_AUTO_CREATE);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.d(OpenPgpConstants.TAG, "Exception on binding", e);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(OpenPgpConstants.TAG, "already bound");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbindFromService() {
|
public void unbindFromService() {
|
||||||
mApplicationContext.unbindService(mCryptoServiceConnection);
|
mApplicationContext.unbindService(mServiceConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
|
||||||
|
public class OpenPgpUtils {
|
||||||
|
|
||||||
|
public static final Pattern PGP_MESSAGE = Pattern.compile(
|
||||||
|
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
|
||||||
|
Pattern.DOTALL);
|
||||||
|
|
||||||
|
public static final Pattern PGP_SIGNED_MESSAGE = Pattern.compile(
|
||||||
|
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
||||||
|
Pattern.DOTALL);
|
||||||
|
|
||||||
|
public static final int PARSE_RESULT_NO_PGP = -1;
|
||||||
|
public static final int PARSE_RESULT_MESSAGE = 0;
|
||||||
|
public static final int PARSE_RESULT_SIGNED_MESSAGE = 1;
|
||||||
|
|
||||||
|
public static int parseMessage(String message) {
|
||||||
|
Matcher matcherSigned = PGP_SIGNED_MESSAGE.matcher(message);
|
||||||
|
Matcher matcherMessage = PGP_MESSAGE.matcher(message);
|
||||||
|
|
||||||
|
if (matcherMessage.matches()) {
|
||||||
|
return PARSE_RESULT_MESSAGE;
|
||||||
|
} else if (matcherSigned.matches()) {
|
||||||
|
return PARSE_RESULT_SIGNED_MESSAGE;
|
||||||
|
} else {
|
||||||
|
return PARSE_RESULT_NO_PGP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAvailable(Context context) {
|
||||||
|
Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
|
||||||
|
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
|
||||||
|
if (!resInfo.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
* 2013 Flow (http://stackoverflow.com/questions/18212152/transfer-inputstream-to-another-service-across-process-boundaries-with-parcelf)
|
||||||
|
*
|
||||||
|
* 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.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class ParcelFileDescriptorUtil {
|
||||||
|
|
||||||
|
public interface IThreadListener {
|
||||||
|
void onThreadFinished(final Thread thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener)
|
||||||
|
throws IOException {
|
||||||
|
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
|
||||||
|
ParcelFileDescriptor readSide = pipe[0];
|
||||||
|
ParcelFileDescriptor writeSide = pipe[1];
|
||||||
|
|
||||||
|
// start the transfer thread
|
||||||
|
new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide),
|
||||||
|
listener)
|
||||||
|
.start();
|
||||||
|
|
||||||
|
return readSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener)
|
||||||
|
throws IOException {
|
||||||
|
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
|
||||||
|
ParcelFileDescriptor readSide = pipe[0];
|
||||||
|
ParcelFileDescriptor writeSide = pipe[1];
|
||||||
|
|
||||||
|
// start the transfer thread
|
||||||
|
new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream,
|
||||||
|
listener)
|
||||||
|
.start();
|
||||||
|
|
||||||
|
return writeSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TransferThread extends Thread {
|
||||||
|
final InputStream mIn;
|
||||||
|
final OutputStream mOut;
|
||||||
|
final IThreadListener mListener;
|
||||||
|
|
||||||
|
TransferThread(InputStream in, OutputStream out, IThreadListener listener) {
|
||||||
|
super("ParcelFileDescriptor Transfer Thread");
|
||||||
|
mIn = in;
|
||||||
|
mOut = out;
|
||||||
|
mListener = listener;
|
||||||
|
setDaemon(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while ((len = mIn.read(buf)) > 0) {
|
||||||
|
mOut.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
mOut.flush(); // just to be safe
|
||||||
|
} catch (IOException e) {
|
||||||
|
//Log.e(OpenPgpConstants.TAG, "TransferThread" + getId() + ": writing failed", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
mIn.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mOut.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//Log.e(OpenPgpConstants.TAG, "TransferThread" + getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mListener != null) {
|
||||||
|
//Log.d(OpenPgpConstants.TAG, "TransferThread " + getId() + " finished!");
|
||||||
|
mListener.onThreadFinished(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.sufficientlysecure.keychain.api;
|
||||||
|
|
||||||
|
public class OpenKeychainIntents {
|
||||||
|
|
||||||
|
public static final String ENCRYPT = "org.sufficientlysecure.keychain.action.ENCRYPT";
|
||||||
|
public static final String ENCRYPT_EXTRA_TEXT = "text"; // String
|
||||||
|
public static final String ENCRYPT_ASCII_ARMOR = "ascii_armor"; // boolean
|
||||||
|
|
||||||
|
public static final String DECRYPT = "org.sufficientlysecure.keychain.action.DECRYPT";
|
||||||
|
public static final String DECRYPT_EXTRA_TEXT = "text"; // String
|
||||||
|
|
||||||
|
public static final String IMPORT_KEY = "org.sufficientlysecure.keychain.action.IMPORT_KEY";
|
||||||
|
public static final String IMPORT_KEY_EXTRA_KEY_BYTES = "key_bytes"; // byte[]
|
||||||
|
|
||||||
|
public static final String IMPORT_KEY_FROM_KEYSERVER = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER";
|
||||||
|
public static final String IMPORT_KEY_FROM_KEYSERVER_QUERY = "query"; // String
|
||||||
|
public static final String IMPORT_KEY_FROM_KEYSERVER_FINGERPRINT = "fingerprint"; // String
|
||||||
|
|
||||||
|
public static final String IMPORT_KEY_FROM_QR_CODE = "org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE";
|
||||||
|
|
||||||
|
}
|
2
OpenPGP-Keychain-API/settings.gradle
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
include ':example-app'
|
||||||
|
include ':libraries:keychain-api-library'
|
@ -3,6 +3,7 @@ apply plugin: 'android'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.android.support:support-v4:19.0.1'
|
compile 'com.android.support:support-v4:19.0.1'
|
||||||
compile 'com.android.support:appcompat-v7:19.0.1'
|
compile 'com.android.support:appcompat-v7:19.0.1'
|
||||||
|
compile project(':libraries:keychain-api-library')
|
||||||
compile project(':libraries:HtmlTextView')
|
compile project(':libraries:HtmlTextView')
|
||||||
compile project(':libraries:StickyListHeaders:library')
|
compile project(':libraries:StickyListHeaders:library')
|
||||||
compile project(':libraries:AndroidBootstrap')
|
compile project(':libraries:AndroidBootstrap')
|
||||||
@ -12,6 +13,7 @@ dependencies {
|
|||||||
compile project(':libraries:spongycastle:pg')
|
compile project(':libraries:spongycastle:pg')
|
||||||
compile project(':libraries:spongycastle:pkix')
|
compile project(':libraries:spongycastle:pkix')
|
||||||
compile project(':libraries:spongycastle:prov')
|
compile project(':libraries:spongycastle:prov')
|
||||||
|
compile project(':libraries:Android-AppMsg:library')
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -19,7 +21,7 @@ android {
|
|||||||
buildToolsVersion "19.0.1"
|
buildToolsVersion "19.0.1"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 8
|
minSdkVersion 9
|
||||||
targetSdkVersion 19
|
targetSdkVersion 19
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.sufficientlysecure.keychain"
|
package="org.sufficientlysecure.keychain"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="22001"
|
android:versionCode="23100"
|
||||||
android:versionName="2.2">
|
android:versionName="2.3.1">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
General remarks
|
General remarks
|
||||||
@ -30,7 +30,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="8"
|
android:minSdkVersion="9"
|
||||||
android:targetSdkVersion="19" />
|
android:targetSdkVersion="19" />
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
@ -153,12 +153,19 @@
|
|||||||
android:windowSoftInputMode="stateHidden">
|
android:windowSoftInputMode="stateHidden">
|
||||||
|
|
||||||
<!-- Keychain's own Actions -->
|
<!-- Keychain's own Actions -->
|
||||||
|
<!-- ENCRYPT with text as extra -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
|
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- ENCRYPT with data Uri -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.ENCRYPT" />
|
||||||
|
|
||||||
<data android:mimeType="*/*" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<!-- TODO: accept other schemes! -->
|
||||||
|
<data android:scheme="file" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<!-- Android's Send Action -->
|
<!-- Android's Send Action -->
|
||||||
<intent-filter android:label="@string/intent_send_encrypt">
|
<intent-filter android:label="@string/intent_send_encrypt">
|
||||||
@ -175,13 +182,40 @@
|
|||||||
android:label="@string/title_decrypt"
|
android:label="@string/title_decrypt"
|
||||||
android:windowSoftInputMode="stateHidden">
|
android:windowSoftInputMode="stateHidden">
|
||||||
|
|
||||||
|
<!--<!– VIEW with mimeType: TODO (from email app) –>-->
|
||||||
|
<!--<intent-filter android:label="@string/intent_import_key">-->
|
||||||
|
<!--<action android:name="android.intent.action.VIEW" />-->
|
||||||
|
|
||||||
|
<!--<category android:name="android.intent.category.BROWSABLE" />-->
|
||||||
|
<!--<category android:name="android.intent.category.DEFAULT" />-->
|
||||||
|
|
||||||
|
<!--<!– mime type as defined in http://tools.ietf.org/html/rfc3156 –>-->
|
||||||
|
<!--<data android:mimeType="application/pgp-signature" />-->
|
||||||
|
<!--</intent-filter>-->
|
||||||
|
<!--<!– VIEW with mimeType: TODO (from email app) –>-->
|
||||||
|
<!--<intent-filter android:label="@string/intent_import_key">-->
|
||||||
|
<!--<action android:name="android.intent.action.VIEW" />-->
|
||||||
|
|
||||||
|
<!--<category android:name="android.intent.category.BROWSABLE" />-->
|
||||||
|
<!--<category android:name="android.intent.category.DEFAULT" />-->
|
||||||
|
|
||||||
|
<!--<!– mime type as defined in http://tools.ietf.org/html/rfc3156 –>-->
|
||||||
|
<!--<data android:mimeType="application/pgp-encrypted" />-->
|
||||||
|
<!--</intent-filter>-->
|
||||||
<!-- Keychain's own Actions -->
|
<!-- Keychain's own Actions -->
|
||||||
|
<!-- DECRYPT with text as extra -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.sufficientlysecure.keychain.action.DECRYPT" />
|
<action android:name="org.sufficientlysecure.keychain.action.DECRYPT" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- DECRYPT with data Uri -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.DECRYPT" />
|
||||||
|
|
||||||
<data android:mimeType="*/*" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<!-- TODO: accept other schemes! -->
|
||||||
|
<data android:scheme="file" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<!-- Android's Send Action -->
|
<!-- Android's Send Action -->
|
||||||
<intent-filter android:label="@string/intent_send_decrypt">
|
<intent-filter android:label="@string/intent_send_decrypt">
|
||||||
@ -249,17 +283,19 @@
|
|||||||
android:label="@string/title_key_server_preference"
|
android:label="@string/title_key_server_preference"
|
||||||
android:windowSoftInputMode="stateHidden" />
|
android:windowSoftInputMode="stateHidden" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SignKeyActivity"
|
android:name=".ui.CertifyKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_sign_key" />
|
android:label="@string/title_certify_key" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ImportKeysActivity"
|
android:name=".ui.ImportKeysActivity"
|
||||||
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_import_keys"
|
android:label="@string/title_import_keys"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:windowSoftInputMode="stateHidden">
|
android:windowSoftInputMode="stateHidden">
|
||||||
|
|
||||||
<!-- Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
|
<!-- VIEW with fingerprint scheme:
|
||||||
<intent-filter>
|
Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
|
||||||
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
@ -272,31 +308,25 @@
|
|||||||
<data android:scheme="OpenPGP4Fpr" />
|
<data android:scheme="OpenPGP4Fpr" />
|
||||||
<data android:scheme="OpenPGP4fpr" />
|
<data android:scheme="OpenPGP4fpr" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<!-- Handle NFC tags detected from outside our application -->
|
<!-- VIEW with mimeType: Allows to import keys (attached to emails) from email apps -->
|
||||||
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->
|
||||||
|
<data android:mimeType="application/pgp-keys" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- NFC: Handle NFC tags detected from outside our application -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<!-- mime type as defined in http://tools.ietf.org/html/rfc3156, section 7 -->
|
<!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->
|
||||||
<data android:mimeType="application/pgp-keys" />
|
<data android:mimeType="application/pgp-keys" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<!-- Keychain's own Actions -->
|
<!-- VIEW with file endings: *.gpg (e.g. to import from OI File Manager) -->
|
||||||
<intent-filter android:label="@string/intent_import_key">
|
|
||||||
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
|
|
||||||
<data android:mimeType="*/*" />
|
|
||||||
</intent-filter>
|
|
||||||
<!-- IMPORT again without mimeType to also allow data only without filename -->
|
|
||||||
<intent-filter android:label="@string/intent_import_key">
|
|
||||||
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
|
|
||||||
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
|
|
||||||
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEY_SERVER" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
|
||||||
<!-- Linking "Import key" to file types -->
|
|
||||||
<intent-filter android:label="@string/intent_import_key">
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
@ -317,6 +347,7 @@
|
|||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<!-- VIEW with file endings: *.asc -->
|
||||||
<intent-filter android:label="@string/intent_import_key">
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
@ -338,6 +369,31 @@
|
|||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<!-- Keychain's own Actions -->
|
||||||
|
<!-- IMPORT_KEY with files TODO: does this work? -->
|
||||||
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- IMPORT_KEY with mimeType 'application/pgp-keys' -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<!-- mime type as defined in http://tools.ietf.org/html/rfc3156, section 7 -->
|
||||||
|
<data android:mimeType="application/pgp-keys" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- IMPORT_KEY without mimeType to allow import with extras Bundle -->
|
||||||
|
<intent-filter android:label="@string/intent_import_key">
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE" />
|
||||||
|
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.HelpActivity"
|
android:name=".ui.HelpActivity"
|
||||||
@ -361,10 +417,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
|
android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name" />
|
||||||
android:launchMode="singleTop"
|
<!--android:launchMode="singleTop"-->
|
||||||
android:process=":remote_api"
|
<!--android:process=":remote_api"-->
|
||||||
android:taskAffinity=":remote_api" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
|
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
@ -391,19 +446,19 @@
|
|||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Extended Remote API -->
|
<!-- Extended Remote API -->
|
||||||
<service
|
<!--<service-->
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.ExtendedApiService"
|
<!--android:name="org.sufficientlysecure.keychain.service.remote.ExtendedApiService"-->
|
||||||
android:enabled="true"
|
<!--android:enabled="true"-->
|
||||||
android:exported="true"
|
<!--android:exported="true"-->
|
||||||
android:process=":remote_api">
|
<!--android:process=":remote_api">-->
|
||||||
<intent-filter>
|
<!--<intent-filter>-->
|
||||||
<action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />
|
<!--<action android:name="org.sufficientlysecure.keychain.service.remote.IExtendedApiService" />-->
|
||||||
</intent-filter>
|
<!--</intent-filter>-->
|
||||||
|
|
||||||
<meta-data
|
<!--<meta-data-->
|
||||||
android:name="api_version"
|
<!--android:name="api_version"-->
|
||||||
android:value="1" />
|
<!--android:value="1" />-->
|
||||||
</service>
|
<!--</service>-->
|
||||||
|
|
||||||
<!-- TODO: authority! Make this API with content provider uris -->
|
<!-- TODO: authority! Make this API with content provider uris -->
|
||||||
<!-- <provider -->
|
<!-- <provider -->
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
|
|
||||||
interface IOpenPgpCallback {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* onSuccess returns on successful OpenPGP operations.
|
|
||||||
*
|
|
||||||
* @param output
|
|
||||||
* contains resulting output (decrypted content (when input was encrypted)
|
|
||||||
* or content without signature (when input was signed-only))
|
|
||||||
* @param signatureResult
|
|
||||||
* signatureResult is only non-null if decryptAndVerify() was called and the content
|
|
||||||
* was encrypted or signed-and-encrypted.
|
|
||||||
*/
|
|
||||||
oneway void onSuccess(in OpenPgpData output, in OpenPgpSignatureResult signatureResult);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.IOpenPgpCallback;
|
|
||||||
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All methods are oneway, which means they are asynchronous and non-blocking.
|
|
||||||
* Results are returned to the callback, which has to be implemented on client side.
|
|
||||||
*/
|
|
||||||
interface IOpenPgpService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign
|
|
||||||
*
|
|
||||||
* After successful signing, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void sign(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt
|
|
||||||
*
|
|
||||||
* After successful encryption, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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 keyIds
|
|
||||||
* Key Ids of recipients. Can be retrieved with getKeyIds()
|
|
||||||
* @param callback
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void encrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign then encrypt
|
|
||||||
*
|
|
||||||
* After successful signing and encryption, callback's onSuccess will contain the resulting output.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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 keyIds
|
|
||||||
* Key Ids of recipients. Can be retrieved with getKeyIds()
|
|
||||||
* @param callback
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void signAndEncrypt(in OpenPgpData input, in OpenPgpData output, in long[] keyIds, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts and verifies given input bytes. This methods handles encrypted-only, signed-and-encrypted,
|
|
||||||
* and also signed-only input.
|
|
||||||
*
|
|
||||||
* After successful decryption/verification, callback's onSuccess will contain the resulting output.
|
|
||||||
* The signatureResult in onSuccess is only non-null if signed-and-encrypted or signed-only inputBytes were given.
|
|
||||||
*
|
|
||||||
* @param input
|
|
||||||
* OpenPgpData object containing String, byte[], ParcelFileDescriptor, or Uri
|
|
||||||
* @param output
|
|
||||||
* 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
|
|
||||||
* Callback where to return results
|
|
||||||
*/
|
|
||||||
oneway void decryptAndVerify(in OpenPgpData input, in OpenPgpData output, in IOpenPgpCallback callback);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OpenPgpError so AIDL can find it and knows that it implements the parcelable protocol.
|
|
||||||
parcelable OpenPgpError;
|
|
@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 OpenPgpSignatureResult so AIDL can find it and knows that it implements the parcelable protocol.
|
|
||||||
parcelable OpenPgpSignatureResult;
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.openintents.openpgp;
|
|
||||||
|
|
||||||
public class OpenPgpConstants {
|
|
||||||
|
|
||||||
public static final String TAG = "OpenPgp API";
|
|
||||||
|
|
||||||
public static final int REQUIRED_API_VERSION = 1;
|
|
||||||
public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService";
|
|
||||||
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.writeInt(type);
|
|
||||||
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.type = source.readInt();
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
|
|
||||||
public class OpenPgpHelper {
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public static Pattern PGP_MESSAGE = Pattern.compile(
|
|
||||||
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
|
|
||||||
|
|
||||||
public static Pattern PGP_SIGNED_MESSAGE = Pattern
|
|
||||||
.compile(
|
|
||||||
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
|
||||||
Pattern.DOTALL);
|
|
||||||
|
|
||||||
public OpenPgpHelper(Context context) {
|
|
||||||
super();
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAvailable() {
|
|
||||||
Intent intent = new Intent(OpenPgpConstants.SERVICE_INTENT);
|
|
||||||
List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0);
|
|
||||||
if (!resInfo.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -78,7 +78,7 @@ public final class Id {
|
|||||||
public static final int filename = 0x00007003;
|
public static final int filename = 0x00007003;
|
||||||
// public static final int output_filename = 0x00007004;
|
// public static final int output_filename = 0x00007004;
|
||||||
public static final int key_server_preference = 0x00007005;
|
public static final int key_server_preference = 0x00007005;
|
||||||
public static final int look_up_key_id = 0x00007006;
|
// public static final int look_up_key_id = 0x00007006;
|
||||||
public static final int export_to_server = 0x00007007;
|
public static final int export_to_server = 0x00007007;
|
||||||
public static final int import_from_qr_code = 0x00007008;
|
public static final int import_from_qr_code = 0x00007008;
|
||||||
public static final int sign_key = 0x00007009;
|
public static final int sign_key = 0x00007009;
|
||||||
|
@ -0,0 +1,782 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.spongycastle.bcpg.ArmoredInputStream;
|
||||||
|
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||||
|
import org.spongycastle.openpgp.PGPCompressedData;
|
||||||
|
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||||
|
import org.spongycastle.openpgp.PGPEncryptedDataList;
|
||||||
|
import org.spongycastle.openpgp.PGPException;
|
||||||
|
import org.spongycastle.openpgp.PGPLiteralData;
|
||||||
|
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||||
|
import org.spongycastle.openpgp.PGPOnePassSignature;
|
||||||
|
import org.spongycastle.openpgp.PGPOnePassSignatureList;
|
||||||
|
import org.spongycastle.openpgp.PGPPBEEncryptedData;
|
||||||
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureList;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||||
|
import org.spongycastle.openpgp.PGPUtil;
|
||||||
|
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
|
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
|
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
|
||||||
|
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class uses a Builder pattern!
|
||||||
|
*/
|
||||||
|
public class PgpDecryptVerify {
|
||||||
|
private Context context;
|
||||||
|
private InputData data;
|
||||||
|
private OutputStream outStream;
|
||||||
|
|
||||||
|
private ProgressDialogUpdater progress;
|
||||||
|
boolean assumeSymmetric;
|
||||||
|
String passphrase;
|
||||||
|
|
||||||
|
private PgpDecryptVerify(Builder builder) {
|
||||||
|
// private Constructor can only be called from Builder
|
||||||
|
this.context = builder.context;
|
||||||
|
this.data = builder.data;
|
||||||
|
this.outStream = builder.outStream;
|
||||||
|
|
||||||
|
this.progress = builder.progress;
|
||||||
|
this.assumeSymmetric = builder.assumeSymmetric;
|
||||||
|
this.passphrase = builder.passphrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
// mandatory parameter
|
||||||
|
private Context context;
|
||||||
|
private InputData data;
|
||||||
|
private OutputStream outStream;
|
||||||
|
|
||||||
|
// optional
|
||||||
|
private ProgressDialogUpdater progress = null;
|
||||||
|
private boolean assumeSymmetric = false;
|
||||||
|
private String passphrase = "";
|
||||||
|
|
||||||
|
public Builder(Context context, InputData data, OutputStream outStream) {
|
||||||
|
this.context = context;
|
||||||
|
this.data = data;
|
||||||
|
this.outStream = outStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder progress(ProgressDialogUpdater progress) {
|
||||||
|
this.progress = progress;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder assumeSymmetric(boolean assumeSymmetric) {
|
||||||
|
this.assumeSymmetric = assumeSymmetric;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder passphrase(String passphrase) {
|
||||||
|
this.passphrase = passphrase;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PgpDecryptVerify build() {
|
||||||
|
return new PgpDecryptVerify(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateProgress(int message, int current, int total) {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.setProgress(message, current, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateProgress(int current, int total) {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.setProgress(current, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasSymmetricEncryption(Context context, InputStream inputStream)
|
||||||
|
throws PgpGeneralException, IOException {
|
||||||
|
InputStream in = PGPUtil.getDecoderStream(inputStream);
|
||||||
|
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
||||||
|
PGPEncryptedDataList enc;
|
||||||
|
Object o = pgpF.nextObject();
|
||||||
|
|
||||||
|
// the first object might be a PGP marker packet.
|
||||||
|
if (o instanceof PGPEncryptedDataList) {
|
||||||
|
enc = (PGPEncryptedDataList) o;
|
||||||
|
} else {
|
||||||
|
enc = (PGPEncryptedDataList) pgpF.nextObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<?> it = enc.getEncryptedDataObjects();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Object obj = it.next();
|
||||||
|
if (obj instanceof PGPPBEEncryptedData) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts and/or verifies data based on parameters of class
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PgpGeneralException
|
||||||
|
* @throws PGPException
|
||||||
|
* @throws SignatureException
|
||||||
|
*/
|
||||||
|
public Bundle execute()
|
||||||
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
|
|
||||||
|
// automatically works with ascii armor input and binary
|
||||||
|
InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
|
||||||
|
if (in instanceof ArmoredInputStream) {
|
||||||
|
ArmoredInputStream aIn = (ArmoredInputStream) in;
|
||||||
|
// it is ascii armored
|
||||||
|
Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine());
|
||||||
|
|
||||||
|
if (aIn.isClearText()) {
|
||||||
|
// a cleartext signature, verify it with the other method
|
||||||
|
return verifyCleartextSignature(aIn);
|
||||||
|
}
|
||||||
|
// else: ascii armored encryption! go on...
|
||||||
|
}
|
||||||
|
|
||||||
|
return decryptVerify(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt and/or verifies binary or ascii armored pgp
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PgpGeneralException
|
||||||
|
* @throws PGPException
|
||||||
|
* @throws SignatureException
|
||||||
|
*/
|
||||||
|
private Bundle decryptVerify(InputStream in)
|
||||||
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
|
Bundle returnData = new Bundle();
|
||||||
|
|
||||||
|
PGPObjectFactory pgpF = new PGPObjectFactory(in);
|
||||||
|
PGPEncryptedDataList enc;
|
||||||
|
Object o = pgpF.nextObject();
|
||||||
|
|
||||||
|
int currentProgress = 0;
|
||||||
|
updateProgress(R.string.progress_reading_data, currentProgress, 100);
|
||||||
|
|
||||||
|
if (o instanceof PGPEncryptedDataList) {
|
||||||
|
enc = (PGPEncryptedDataList) o;
|
||||||
|
} else {
|
||||||
|
enc = (PGPEncryptedDataList) pgpF.nextObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream clear;
|
||||||
|
PGPEncryptedData encryptedData;
|
||||||
|
|
||||||
|
currentProgress += 5;
|
||||||
|
|
||||||
|
// TODO: currently we always only look at the first known key or symmetric encryption,
|
||||||
|
// there might be more...
|
||||||
|
if (assumeSymmetric) {
|
||||||
|
PGPPBEEncryptedData pbe = null;
|
||||||
|
Iterator<?> it = enc.getEncryptedDataObjects();
|
||||||
|
// find secret key
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Object obj = it.next();
|
||||||
|
if (obj instanceof PGPPBEEncryptedData) {
|
||||||
|
pbe = (PGPPBEEncryptedData) obj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pbe == null) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
context.getString(R.string.error_no_symmetric_encryption_packet));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||||
|
|
||||||
|
PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
|
||||||
|
PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
|
||||||
|
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
|
passphrase.toCharArray());
|
||||||
|
|
||||||
|
clear = pbe.getDataStream(decryptorFactory);
|
||||||
|
|
||||||
|
encryptedData = pbe;
|
||||||
|
currentProgress += 5;
|
||||||
|
} else {
|
||||||
|
updateProgress(R.string.progress_finding_key, currentProgress, 100);
|
||||||
|
|
||||||
|
PGPPublicKeyEncryptedData pbe = null;
|
||||||
|
PGPSecretKey secretKey = null;
|
||||||
|
Iterator<?> it = enc.getEncryptedDataObjects();
|
||||||
|
// find secret key
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Object obj = it.next();
|
||||||
|
if (obj instanceof PGPPublicKeyEncryptedData) {
|
||||||
|
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
||||||
|
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
|
||||||
|
if (secretKey != null) {
|
||||||
|
pbe = encData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secretKey == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProgress += 5;
|
||||||
|
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
|
||||||
|
PGPPrivateKey privateKey = null;
|
||||||
|
try {
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
|
passphrase.toCharArray());
|
||||||
|
privateKey = secretKey.extractPrivateKey(keyDecryptor);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new PGPException(context.getString(R.string.error_wrong_passphrase));
|
||||||
|
}
|
||||||
|
if (privateKey == null) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
context.getString(R.string.error_could_not_extract_private_key));
|
||||||
|
}
|
||||||
|
currentProgress += 5;
|
||||||
|
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||||
|
|
||||||
|
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
|
||||||
|
|
||||||
|
clear = pbe.getDataStream(decryptorFactory);
|
||||||
|
|
||||||
|
encryptedData = pbe;
|
||||||
|
currentProgress += 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
|
||||||
|
Object dataChunk = plainFact.nextObject();
|
||||||
|
PGPOnePassSignature signature = null;
|
||||||
|
PGPPublicKey signatureKey = null;
|
||||||
|
int signatureIndex = -1;
|
||||||
|
|
||||||
|
if (dataChunk instanceof PGPCompressedData) {
|
||||||
|
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
|
||||||
|
|
||||||
|
PGPObjectFactory fact = new PGPObjectFactory(
|
||||||
|
((PGPCompressedData) dataChunk).getDataStream());
|
||||||
|
dataChunk = fact.nextObject();
|
||||||
|
plainFact = fact;
|
||||||
|
currentProgress += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
long signatureKeyId = 0;
|
||||||
|
if (dataChunk instanceof PGPOnePassSignatureList) {
|
||||||
|
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
|
||||||
|
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
|
||||||
|
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
|
||||||
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
|
signature = sigList.get(i);
|
||||||
|
signatureKey = ProviderHelper
|
||||||
|
.getPGPPublicKeyByKeyId(context, signature.getKeyID());
|
||||||
|
if (signatureKeyId == 0) {
|
||||||
|
signatureKeyId = signature.getKeyID();
|
||||||
|
}
|
||||||
|
if (signatureKey == null) {
|
||||||
|
signature = null;
|
||||||
|
} else {
|
||||||
|
signatureIndex = i;
|
||||||
|
signatureKeyId = signature.getKeyID();
|
||||||
|
String userId = null;
|
||||||
|
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
|
||||||
|
context, signatureKeyId);
|
||||||
|
if (signKeyRing != null) {
|
||||||
|
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||||
|
}
|
||||||
|
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
|
||||||
|
|
||||||
|
if (signature != null) {
|
||||||
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
|
signature.init(contentVerifierBuilderProvider, signatureKey);
|
||||||
|
} else {
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataChunk = plainFact.nextObject();
|
||||||
|
currentProgress += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataChunk instanceof PGPSignatureList) {
|
||||||
|
dataChunk = plainFact.nextObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataChunk instanceof PGPLiteralData) {
|
||||||
|
updateProgress(R.string.progress_decrypting, currentProgress, 100);
|
||||||
|
|
||||||
|
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1 << 16];
|
||||||
|
InputStream dataIn = literalData.getInputStream();
|
||||||
|
|
||||||
|
int startProgress = currentProgress;
|
||||||
|
int endProgress = 100;
|
||||||
|
if (signature != null) {
|
||||||
|
endProgress = 90;
|
||||||
|
} else if (encryptedData.isIntegrityProtected()) {
|
||||||
|
endProgress = 95;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n;
|
||||||
|
// TODO: progress calculation is broken here! Try to rework it based on commented code!
|
||||||
|
// int progress = 0;
|
||||||
|
long startPos = data.getStreamPosition();
|
||||||
|
while ((n = dataIn.read(buffer)) > 0) {
|
||||||
|
outStream.write(buffer, 0, n);
|
||||||
|
// progress += n;
|
||||||
|
if (signature != null) {
|
||||||
|
try {
|
||||||
|
signature.update(buffer, 0, n);
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
returnData
|
||||||
|
.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
|
||||||
|
signature = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: dead code?!
|
||||||
|
// unknown size, but try to at least have a moving, slowing down progress bar
|
||||||
|
// currentProgress = startProgress + (endProgress - startProgress) * progress
|
||||||
|
// / (progress + 100000);
|
||||||
|
if (data.getSize() - startPos == 0) {
|
||||||
|
currentProgress = endProgress;
|
||||||
|
} else {
|
||||||
|
currentProgress = (int) (startProgress + (endProgress - startProgress)
|
||||||
|
* (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
|
||||||
|
}
|
||||||
|
updateProgress(currentProgress, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signature != null) {
|
||||||
|
updateProgress(R.string.progress_verifying_signature, 90, 100);
|
||||||
|
|
||||||
|
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
|
||||||
|
PGPSignature messageSignature = signatureList.get(signatureIndex);
|
||||||
|
|
||||||
|
// these are not cleartext signatures!
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
|
||||||
|
|
||||||
|
//Now check binding signatures
|
||||||
|
boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey);
|
||||||
|
boolean sig_isok = signature.verify(messageSignature);
|
||||||
|
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test if this integrity really check works!
|
||||||
|
if (encryptedData.isIntegrityProtected()) {
|
||||||
|
updateProgress(R.string.progress_verifying_integrity, 95, 100);
|
||||||
|
|
||||||
|
if (encryptedData.verify()) {
|
||||||
|
// passed
|
||||||
|
Log.d(Constants.TAG, "Integrity verification: success!");
|
||||||
|
} else {
|
||||||
|
// failed
|
||||||
|
Log.d(Constants.TAG, "Integrity verification: failed!");
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_integrity_check_failed));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no integrity check
|
||||||
|
Log.e(Constants.TAG, "Encrypted data was not integrity protected!");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method verifies cleartext signatures
|
||||||
|
* as defined in http://tools.ietf.org/html/rfc4880#section-7
|
||||||
|
* <p/>
|
||||||
|
* The method is heavily based on
|
||||||
|
* pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PgpGeneralException
|
||||||
|
* @throws PGPException
|
||||||
|
* @throws SignatureException
|
||||||
|
*/
|
||||||
|
private Bundle verifyCleartextSignature(ArmoredInputStream aIn)
|
||||||
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
|
Bundle returnData = new Bundle();
|
||||||
|
// cleartext signatures are never encrypted ;)
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_done, 0, 100);
|
||||||
|
|
||||||
|
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
|
||||||
|
int lookAhead = readInputLine(lineOut, aIn);
|
||||||
|
byte[] lineSep = getLineSeparator();
|
||||||
|
|
||||||
|
byte[] line = lineOut.toByteArray();
|
||||||
|
out.write(line, 0, getLengthWithoutSeparator(line));
|
||||||
|
out.write(lineSep);
|
||||||
|
|
||||||
|
while (lookAhead != -1 && aIn.isClearText()) {
|
||||||
|
lookAhead = readInputLine(lineOut, lookAhead, aIn);
|
||||||
|
line = lineOut.toByteArray();
|
||||||
|
out.write(line, 0, getLengthWithoutSeparator(line));
|
||||||
|
out.write(lineSep);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
byte[] clearText = out.toByteArray();
|
||||||
|
outStream.write(clearText);
|
||||||
|
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true);
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_processing_signature, 60, 100);
|
||||||
|
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
||||||
|
|
||||||
|
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
|
||||||
|
if (sigList == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_corrupt_data));
|
||||||
|
}
|
||||||
|
PGPSignature signature = null;
|
||||||
|
long signatureKeyId = 0;
|
||||||
|
PGPPublicKey signatureKey = null;
|
||||||
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
|
signature = sigList.get(i);
|
||||||
|
signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID());
|
||||||
|
if (signatureKeyId == 0) {
|
||||||
|
signatureKeyId = signature.getKeyID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signatureKey == null) {
|
||||||
|
signature = null;
|
||||||
|
} else {
|
||||||
|
signatureKeyId = signature.getKeyID();
|
||||||
|
String userId = null;
|
||||||
|
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
|
||||||
|
signatureKeyId);
|
||||||
|
if (signKeyRing != null) {
|
||||||
|
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||||
|
}
|
||||||
|
returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId);
|
||||||
|
|
||||||
|
if (signature == null) {
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true);
|
||||||
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
|
signature.init(contentVerifierBuilderProvider, signatureKey);
|
||||||
|
|
||||||
|
InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
|
||||||
|
|
||||||
|
lookAhead = readInputLine(lineOut, sigIn);
|
||||||
|
|
||||||
|
processLine(signature, lineOut.toByteArray());
|
||||||
|
|
||||||
|
if (lookAhead != -1) {
|
||||||
|
do {
|
||||||
|
lookAhead = readInputLine(lineOut, lookAhead, sigIn);
|
||||||
|
|
||||||
|
signature.update((byte) '\r');
|
||||||
|
signature.update((byte) '\n');
|
||||||
|
|
||||||
|
processLine(signature, lineOut.toByteArray());
|
||||||
|
} while (lookAhead != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean sig_isok = signature.verify();
|
||||||
|
|
||||||
|
//Now check binding signatures
|
||||||
|
boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey);
|
||||||
|
|
||||||
|
returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok);
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
|
||||||
|
long signatureKeyId = signature.getKeyID();
|
||||||
|
boolean keyBinding_isok = false;
|
||||||
|
String userId = null;
|
||||||
|
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
|
||||||
|
signatureKeyId);
|
||||||
|
PGPPublicKey mKey = null;
|
||||||
|
if (signKeyRing != null) {
|
||||||
|
mKey = PgpKeyHelper.getMasterKey(signKeyRing);
|
||||||
|
}
|
||||||
|
if (signature.getKeyID() != mKey.getKeyID()) {
|
||||||
|
keyBinding_isok = verifyKeyBinding(mKey, signatureKey);
|
||||||
|
} else { //if the key used to make the signature was the master key, no need to check binding sigs
|
||||||
|
keyBinding_isok = true;
|
||||||
|
}
|
||||||
|
return keyBinding_isok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||||
|
boolean subkeyBinding_isok = false;
|
||||||
|
boolean tmp_subkeyBinding_isok = false;
|
||||||
|
boolean primkeyBinding_isok = false;
|
||||||
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
|
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
|
||||||
|
|
||||||
|
subkeyBinding_isok = false;
|
||||||
|
tmp_subkeyBinding_isok = false;
|
||||||
|
primkeyBinding_isok = false;
|
||||||
|
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
|
||||||
|
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
|
||||||
|
//about keys without subkey signing. Can't get it to import a slightly broken one
|
||||||
|
//either, so we will err on bad subkey binding here.
|
||||||
|
PGPSignature sig = itr.next();
|
||||||
|
if (sig.getKeyID() == masterPublicKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
|
||||||
|
//check and if ok, check primary key binding.
|
||||||
|
try {
|
||||||
|
sig.init(contentVerifierBuilderProvider, masterPublicKey);
|
||||||
|
tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||||
|
} catch (PGPException e) {
|
||||||
|
continue;
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp_subkeyBinding_isok)
|
||||||
|
subkeyBinding_isok = true;
|
||||||
|
if (tmp_subkeyBinding_isok) {
|
||||||
|
primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey);
|
||||||
|
if (primkeyBinding_isok)
|
||||||
|
break;
|
||||||
|
primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey);
|
||||||
|
if (primkeyBinding_isok)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (subkeyBinding_isok & primkeyBinding_isok);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||||
|
boolean primkeyBinding_isok = false;
|
||||||
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
PGPSignatureList eSigList;
|
||||||
|
|
||||||
|
if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
|
||||||
|
try {
|
||||||
|
eSigList = Pkts.getEmbeddedSignatures();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
} catch (PGPException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < eSigList.size(); ++j) {
|
||||||
|
PGPSignature emSig = eSigList.get(j);
|
||||||
|
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
|
||||||
|
try {
|
||||||
|
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
|
||||||
|
primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||||
|
if (primkeyBinding_isok)
|
||||||
|
break;
|
||||||
|
} catch (PGPException e) {
|
||||||
|
continue;
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return primkeyBinding_isok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
|
||||||
|
*
|
||||||
|
* @param sig
|
||||||
|
* @param line
|
||||||
|
* @throws SignatureException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private static void processLine(PGPSignature sig, byte[] line)
|
||||||
|
throws SignatureException, IOException {
|
||||||
|
int length = getLengthWithoutWhiteSpace(line);
|
||||||
|
if (length > 0) {
|
||||||
|
sig.update(line, 0, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
||||||
|
throws IOException {
|
||||||
|
bOut.reset();
|
||||||
|
|
||||||
|
int lookAhead = -1;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
while ((ch = fIn.read()) >= 0) {
|
||||||
|
bOut.write(ch);
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
lookAhead = readPassedEOL(bOut, ch, fIn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
|
||||||
|
throws IOException {
|
||||||
|
bOut.reset();
|
||||||
|
|
||||||
|
int ch = lookAhead;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bOut.write(ch);
|
||||||
|
if (ch == '\r' || ch == '\n') {
|
||||||
|
lookAhead = readPassedEOL(bOut, ch, fIn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ((ch = fIn.read()) >= 0);
|
||||||
|
|
||||||
|
if (ch < 0) {
|
||||||
|
lookAhead = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
|
||||||
|
throws IOException {
|
||||||
|
int lookAhead = fIn.read();
|
||||||
|
|
||||||
|
if (lastCh == '\r' && lookAhead == '\n') {
|
||||||
|
bOut.write(lookAhead);
|
||||||
|
lookAhead = fIn.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookAhead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getLengthWithoutSeparator(byte[] line) {
|
||||||
|
int end = line.length - 1;
|
||||||
|
|
||||||
|
while (end >= 0 && isLineEnding(line[end])) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isLineEnding(byte b) {
|
||||||
|
return b == '\r' || b == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getLengthWithoutWhiteSpace(byte[] line) {
|
||||||
|
int end = line.length - 1;
|
||||||
|
|
||||||
|
while (end >= 0 && isWhiteSpace(line[end])) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return end + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWhiteSpace(byte b) {
|
||||||
|
return b == '\r' || b == '\n' || b == '\t' || b == ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] getLineSeparator() {
|
||||||
|
String nl = System.getProperty("line.separator");
|
||||||
|
byte[] nlBytes = new byte[nl.length()];
|
||||||
|
|
||||||
|
for (int i = 0; i != nlBytes.length; i++) {
|
||||||
|
nlBytes[i] = (byte) nl.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nlBytes;
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,8 @@ import android.content.Context;
|
|||||||
|
|
||||||
public class PgpKeyHelper {
|
public class PgpKeyHelper {
|
||||||
|
|
||||||
|
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
|
||||||
|
|
||||||
public static Date getCreationDate(PGPPublicKey key) {
|
public static Date getCreationDate(PGPPublicKey key) {
|
||||||
return key.getCreationTime();
|
return key.getCreationTime();
|
||||||
}
|
}
|
||||||
@ -591,8 +593,7 @@ public class PgpKeyHelper {
|
|||||||
* "Max Mustermann (this is a comment)"
|
* "Max Mustermann (this is a comment)"
|
||||||
* "Max Mustermann [this is nothing]"
|
* "Max Mustermann [this is nothing]"
|
||||||
*/
|
*/
|
||||||
Pattern withComment = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
|
Matcher matcher = USER_ID_PATTERN.matcher(userId);
|
||||||
Matcher matcher = withComment.matcher(userId);
|
|
||||||
if (matcher.matches()) {
|
if (matcher.matches()) {
|
||||||
result[0] = matcher.group(1);
|
result[0] = matcher.group(1);
|
||||||
result[1] = matcher.group(3);
|
result[1] = matcher.group(3);
|
||||||
|
@ -504,7 +504,7 @@ public class PgpKeyOperation {
|
|||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPPublicKeyRing signKey(long masterKeyId, long pubKeyId, String passphrase)
|
public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, String passphrase)
|
||||||
throws PgpGeneralException, PGPException, SignatureException {
|
throws PgpGeneralException, PGPException, SignatureException {
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
throw new PgpGeneralException("Unable to obtain passphrase");
|
throw new PgpGeneralException("Unable to obtain passphrase");
|
||||||
@ -512,14 +512,14 @@ public class PgpKeyOperation {
|
|||||||
PGPPublicKeyRing pubring = ProviderHelper
|
PGPPublicKeyRing pubring = ProviderHelper
|
||||||
.getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
|
.getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
|
||||||
|
|
||||||
PGPSecretKey signingKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
|
PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
|
||||||
if (signingKey == null) {
|
if (certificationKey == null) {
|
||||||
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
||||||
}
|
}
|
||||||
|
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||||
PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
|
||||||
if (signaturePrivateKey == null) {
|
if (signaturePrivateKey == null) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
mContext.getString(R.string.error_could_not_extract_private_key));
|
mContext.getString(R.string.error_could_not_extract_private_key));
|
||||||
@ -527,7 +527,7 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
// TODO: SHA256 fixed?
|
// TODO: SHA256 fixed?
|
||||||
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
|
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
|
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
|
||||||
|
@ -0,0 +1,605 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||||
|
import org.spongycastle.bcpg.BCPGOutputStream;
|
||||||
|
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPException;
|
||||||
|
import org.spongycastle.openpgp.PGPLiteralData;
|
||||||
|
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
|
||||||
|
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
|
||||||
|
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class uses a Builder pattern!
|
||||||
|
*/
|
||||||
|
public class PgpSignEncrypt {
|
||||||
|
private Context context;
|
||||||
|
private InputData data;
|
||||||
|
private OutputStream outStream;
|
||||||
|
|
||||||
|
private ProgressDialogUpdater progress;
|
||||||
|
private boolean enableAsciiArmorOutput;
|
||||||
|
private int compressionId;
|
||||||
|
private long[] encryptionKeyIds;
|
||||||
|
private String encryptionPassphrase;
|
||||||
|
private int symmetricEncryptionAlgorithm;
|
||||||
|
private long signatureKeyId;
|
||||||
|
private int signatureHashAlgorithm;
|
||||||
|
private boolean signatureForceV3;
|
||||||
|
private String signaturePassphrase;
|
||||||
|
|
||||||
|
private PgpSignEncrypt(Builder builder) {
|
||||||
|
// private Constructor can only be called from Builder
|
||||||
|
this.context = builder.context;
|
||||||
|
this.data = builder.data;
|
||||||
|
this.outStream = builder.outStream;
|
||||||
|
|
||||||
|
this.progress = builder.progress;
|
||||||
|
this.enableAsciiArmorOutput = builder.enableAsciiArmorOutput;
|
||||||
|
this.compressionId = builder.compressionId;
|
||||||
|
this.encryptionKeyIds = builder.encryptionKeyIds;
|
||||||
|
this.encryptionPassphrase = builder.encryptionPassphrase;
|
||||||
|
this.symmetricEncryptionAlgorithm = builder.symmetricEncryptionAlgorithm;
|
||||||
|
this.signatureKeyId = builder.signatureKeyId;
|
||||||
|
this.signatureHashAlgorithm = builder.signatureHashAlgorithm;
|
||||||
|
this.signatureForceV3 = builder.signatureForceV3;
|
||||||
|
this.signaturePassphrase = builder.signaturePassphrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
// mandatory parameter
|
||||||
|
private Context context;
|
||||||
|
private InputData data;
|
||||||
|
private OutputStream outStream;
|
||||||
|
|
||||||
|
// optional
|
||||||
|
private ProgressDialogUpdater progress = null;
|
||||||
|
private boolean enableAsciiArmorOutput = false;
|
||||||
|
private int compressionId = Id.choice.compression.none;
|
||||||
|
private long[] encryptionKeyIds = new long[0];
|
||||||
|
private String encryptionPassphrase = null;
|
||||||
|
private int symmetricEncryptionAlgorithm = 0;
|
||||||
|
private long signatureKeyId = Id.key.none;
|
||||||
|
private int signatureHashAlgorithm = 0;
|
||||||
|
private boolean signatureForceV3 = false;
|
||||||
|
private String signaturePassphrase = null;
|
||||||
|
|
||||||
|
public Builder(Context context, InputData data, OutputStream outStream) {
|
||||||
|
this.context = context;
|
||||||
|
this.data = data;
|
||||||
|
this.outStream = outStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder progress(ProgressDialogUpdater progress) {
|
||||||
|
this.progress = progress;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder enableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
|
||||||
|
this.enableAsciiArmorOutput = enableAsciiArmorOutput;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder compressionId(int compressionId) {
|
||||||
|
this.compressionId = compressionId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder encryptionKeyIds(long[] encryptionKeyIds) {
|
||||||
|
this.encryptionKeyIds = encryptionKeyIds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder encryptionPassphrase(String encryptionPassphrase) {
|
||||||
|
this.encryptionPassphrase = encryptionPassphrase;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder symmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
|
||||||
|
this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder signatureKeyId(long signatureKeyId) {
|
||||||
|
this.signatureKeyId = signatureKeyId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder signatureHashAlgorithm(int signatureHashAlgorithm) {
|
||||||
|
this.signatureHashAlgorithm = signatureHashAlgorithm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder signatureForceV3(boolean signatureForceV3) {
|
||||||
|
this.signatureForceV3 = signatureForceV3;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder signaturePassphrase(String signaturePassphrase) {
|
||||||
|
this.signaturePassphrase = signaturePassphrase;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PgpSignEncrypt build() {
|
||||||
|
return new PgpSignEncrypt(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateProgress(int message, int current, int total) {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.setProgress(message, current, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateProgress(int current, int total) {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.setProgress(current, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs and/or encrypts data based on parameters of class
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
* @throws PgpGeneralException
|
||||||
|
* @throws PGPException
|
||||||
|
* @throws NoSuchProviderException
|
||||||
|
* @throws NoSuchAlgorithmException
|
||||||
|
* @throws SignatureException
|
||||||
|
*/
|
||||||
|
public void execute()
|
||||||
|
throws IOException, PgpGeneralException, PGPException, NoSuchProviderException,
|
||||||
|
NoSuchAlgorithmException, SignatureException {
|
||||||
|
|
||||||
|
boolean enableSignature = signatureKeyId != Id.key.none;
|
||||||
|
boolean enableEncryption = (encryptionKeyIds.length != 0 || encryptionPassphrase != null);
|
||||||
|
boolean enableCompression = (enableEncryption && compressionId != Id.choice.compression.none);
|
||||||
|
|
||||||
|
Log.d(Constants.TAG, "enableSignature:" + enableSignature
|
||||||
|
+ "\nenableEncryption:" + enableEncryption
|
||||||
|
+ "\nenableCompression:" + enableCompression
|
||||||
|
+ "\nenableAsciiArmorOutput:" + enableAsciiArmorOutput);
|
||||||
|
|
||||||
|
int signatureType;
|
||||||
|
if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
|
||||||
|
// for sign-only ascii text
|
||||||
|
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
|
||||||
|
} else {
|
||||||
|
signatureType = PGPSignature.BINARY_DOCUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArmoredOutputStream armorOut = null;
|
||||||
|
OutputStream out;
|
||||||
|
if (enableAsciiArmorOutput) {
|
||||||
|
armorOut = new ArmoredOutputStream(outStream);
|
||||||
|
armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
|
||||||
|
out = armorOut;
|
||||||
|
} else {
|
||||||
|
out = outStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get keys for signature generation for later usage */
|
||||||
|
PGPSecretKey signingKey = null;
|
||||||
|
PGPSecretKeyRing signingKeyRing = null;
|
||||||
|
PGPPrivateKey signaturePrivateKey = null;
|
||||||
|
if (enableSignature) {
|
||||||
|
signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
|
||||||
|
signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
|
||||||
|
if (signingKey == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signaturePassphrase == null) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
context.getString(R.string.error_no_signature_passphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
|
||||||
|
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
|
||||||
|
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
||||||
|
if (signaturePrivateKey == null) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
context.getString(R.string.error_could_not_extract_private_key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateProgress(R.string.progress_preparing_streams, 5, 100);
|
||||||
|
|
||||||
|
/* Initialize PGPEncryptedDataGenerator for later usage */
|
||||||
|
PGPEncryptedDataGenerator cPk = null;
|
||||||
|
if (enableEncryption) {
|
||||||
|
// has Integrity packet enabled!
|
||||||
|
JcePGPDataEncryptorBuilder encryptorBuilder =
|
||||||
|
new JcePGPDataEncryptorBuilder(symmetricEncryptionAlgorithm)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
|
||||||
|
.setWithIntegrityPacket(true);
|
||||||
|
|
||||||
|
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
|
||||||
|
|
||||||
|
if (encryptionKeyIds.length == 0) {
|
||||||
|
// Symmetric encryption
|
||||||
|
Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
|
||||||
|
|
||||||
|
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
|
||||||
|
new JcePBEKeyEncryptionMethodGenerator(encryptionPassphrase.toCharArray());
|
||||||
|
cPk.addMethod(symmetricEncryptionGenerator);
|
||||||
|
} else {
|
||||||
|
// Asymmetric encryption
|
||||||
|
for (long id : encryptionKeyIds) {
|
||||||
|
PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(context, id);
|
||||||
|
if (key != null) {
|
||||||
|
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
|
||||||
|
new JcePublicKeyKeyEncryptionMethodGenerator(key);
|
||||||
|
cPk.addMethod(pubKeyEncryptionGenerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize signature generator object for later usage */
|
||||||
|
PGPSignatureGenerator signatureGenerator = null;
|
||||||
|
PGPV3SignatureGenerator signatureV3Generator = null;
|
||||||
|
if (enableSignature) {
|
||||||
|
updateProgress(R.string.progress_preparing_signature, 10, 100);
|
||||||
|
|
||||||
|
// content signer based on signing key algorithm and chosen hash algorithm
|
||||||
|
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
|
||||||
|
signatureV3Generator.init(signatureType, signaturePrivateKey);
|
||||||
|
} else {
|
||||||
|
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
|
signatureGenerator.init(signatureType, signaturePrivateKey);
|
||||||
|
|
||||||
|
String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
|
||||||
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
spGen.setSignerUserID(false, userId);
|
||||||
|
signatureGenerator.setHashedSubpackets(spGen.generate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPCompressedDataGenerator compressGen = null;
|
||||||
|
OutputStream pOut;
|
||||||
|
OutputStream encryptionOut = null;
|
||||||
|
BCPGOutputStream bcpgOut;
|
||||||
|
if (enableEncryption) {
|
||||||
|
/* actual encryption */
|
||||||
|
|
||||||
|
encryptionOut = cPk.open(out, new byte[1 << 16]);
|
||||||
|
|
||||||
|
if (enableCompression) {
|
||||||
|
compressGen = new PGPCompressedDataGenerator(compressionId);
|
||||||
|
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
|
||||||
|
} else {
|
||||||
|
bcpgOut = new BCPGOutputStream(encryptionOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableSignature) {
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
|
||||||
|
} else {
|
||||||
|
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
|
||||||
|
// file name not needed, so empty string
|
||||||
|
pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
|
||||||
|
new byte[1 << 16]);
|
||||||
|
updateProgress(R.string.progress_encrypting, 20, 100);
|
||||||
|
|
||||||
|
long progress = 0;
|
||||||
|
int n;
|
||||||
|
byte[] buffer = new byte[1 << 16];
|
||||||
|
InputStream in = data.getInputStream();
|
||||||
|
while ((n = in.read(buffer)) > 0) {
|
||||||
|
pOut.write(buffer, 0, n);
|
||||||
|
|
||||||
|
// update signature buffer if signature is requested
|
||||||
|
if (enableSignature) {
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator.update(buffer, 0, n);
|
||||||
|
} else {
|
||||||
|
signatureGenerator.update(buffer, 0, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress += n;
|
||||||
|
if (data.getSize() != 0) {
|
||||||
|
updateProgress((int) (20 + (95 - 20) * progress / data.getSize()), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
literalGen.close();
|
||||||
|
} else if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
|
||||||
|
/* sign-only of ascii text */
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_signing, 40, 100);
|
||||||
|
|
||||||
|
// write directly on armor output stream
|
||||||
|
armorOut.beginClearText(signatureHashAlgorithm);
|
||||||
|
|
||||||
|
InputStream in = data.getInputStream();
|
||||||
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||||
|
|
||||||
|
final byte[] newline = "\r\n".getBytes("UTF-8");
|
||||||
|
|
||||||
|
if (signatureForceV3) {
|
||||||
|
processLine(reader.readLine(), armorOut, signatureV3Generator);
|
||||||
|
} else {
|
||||||
|
processLine(reader.readLine(), armorOut, signatureGenerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
|
||||||
|
if (line == null) {
|
||||||
|
armorOut.write(newline);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
armorOut.write(newline);
|
||||||
|
|
||||||
|
// update signature buffer with input line
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator.update(newline);
|
||||||
|
processLine(line, armorOut, signatureV3Generator);
|
||||||
|
} else {
|
||||||
|
signatureGenerator.update(newline);
|
||||||
|
processLine(line, armorOut, signatureGenerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
armorOut.endClearText();
|
||||||
|
|
||||||
|
pOut = new BCPGOutputStream(armorOut);
|
||||||
|
} else {
|
||||||
|
// TODO: implement sign-only for files!
|
||||||
|
pOut = null;
|
||||||
|
Log.e(Constants.TAG, "not supported!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableSignature) {
|
||||||
|
updateProgress(R.string.progress_generating_signature, 95, 100);
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator.generate().encode(pOut);
|
||||||
|
} else {
|
||||||
|
signatureGenerator.generate().encode(pOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing outputs
|
||||||
|
// NOTE: closing needs to be done in the correct order!
|
||||||
|
// TODO: closing bcpgOut and pOut???
|
||||||
|
if (enableEncryption) {
|
||||||
|
if (enableCompression) {
|
||||||
|
compressGen.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionOut.close();
|
||||||
|
}
|
||||||
|
if (enableAsciiArmorOutput) {
|
||||||
|
armorOut.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
outStream.close();
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: merge this into execute method!
|
||||||
|
// TODO: allow binary input for this class
|
||||||
|
public void generateSignature()
|
||||||
|
throws PgpGeneralException, PGPException, IOException, NoSuchAlgorithmException,
|
||||||
|
SignatureException {
|
||||||
|
|
||||||
|
OutputStream out;
|
||||||
|
if (enableAsciiArmorOutput) {
|
||||||
|
// Ascii Armor (Radix-64)
|
||||||
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
|
||||||
|
armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
|
||||||
|
out = armorOut;
|
||||||
|
} else {
|
||||||
|
out = outStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signatureKeyId == 0) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_no_signature_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPSecretKeyRing signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
|
||||||
|
PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
|
||||||
|
if (signingKey == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signaturePassphrase == null) {
|
||||||
|
throw new PgpGeneralException(context.getString(R.string.error_no_signature_passphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
|
||||||
|
PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
||||||
|
if (signaturePrivateKey == null) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
context.getString(R.string.error_could_not_extract_private_key));
|
||||||
|
}
|
||||||
|
updateProgress(R.string.progress_preparing_streams, 0, 100);
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_preparing_signature, 30, 100);
|
||||||
|
|
||||||
|
int type = PGPSignature.CANONICAL_TEXT_DOCUMENT;
|
||||||
|
// if (binary) {
|
||||||
|
// type = PGPSignature.BINARY_DOCUMENT;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// content signer based on signing key algorithm and chosen hash algorithm
|
||||||
|
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
|
||||||
|
.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
|
PGPSignatureGenerator signatureGenerator = null;
|
||||||
|
PGPV3SignatureGenerator signatureV3Generator = null;
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
|
||||||
|
signatureV3Generator.init(type, signaturePrivateKey);
|
||||||
|
} else {
|
||||||
|
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
|
signatureGenerator.init(type, signaturePrivateKey);
|
||||||
|
|
||||||
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
|
||||||
|
spGen.setSignerUserID(false, userId);
|
||||||
|
signatureGenerator.setHashedSubpackets(spGen.generate());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_signing, 40, 100);
|
||||||
|
|
||||||
|
InputStream inStream = data.getInputStream();
|
||||||
|
// if (binary) {
|
||||||
|
// byte[] buffer = new byte[1 << 16];
|
||||||
|
// int n = 0;
|
||||||
|
// while ((n = inStream.read(buffer)) > 0) {
|
||||||
|
// if (signatureForceV3) {
|
||||||
|
// signatureV3Generator.update(buffer, 0, n);
|
||||||
|
// } else {
|
||||||
|
// signatureGenerator.update(buffer, 0, n);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
|
||||||
|
final byte[] newline = "\r\n".getBytes("UTF-8");
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (signatureForceV3) {
|
||||||
|
processLine(line, null, signatureV3Generator);
|
||||||
|
signatureV3Generator.update(newline);
|
||||||
|
} else {
|
||||||
|
processLine(line, null, signatureGenerator);
|
||||||
|
signatureGenerator.update(newline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
BCPGOutputStream bOut = new BCPGOutputStream(out);
|
||||||
|
if (signatureForceV3) {
|
||||||
|
signatureV3Generator.generate().encode(bOut);
|
||||||
|
} else {
|
||||||
|
signatureGenerator.generate().encode(bOut);
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
outStream.close();
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
|
||||||
|
final PGPSignatureGenerator pSignatureGenerator)
|
||||||
|
throws IOException, SignatureException {
|
||||||
|
|
||||||
|
if (pLine == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char[] chars = pLine.toCharArray();
|
||||||
|
int len = chars.length;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
if (!Character.isWhitespace(chars[len - 1])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
|
||||||
|
|
||||||
|
if (pArmoredOutput != null) {
|
||||||
|
pArmoredOutput.write(data);
|
||||||
|
}
|
||||||
|
pSignatureGenerator.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
|
||||||
|
final PGPV3SignatureGenerator pSignatureGenerator)
|
||||||
|
throws IOException, SignatureException {
|
||||||
|
|
||||||
|
if (pLine == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char[] chars = pLine.toCharArray();
|
||||||
|
int len = chars.length;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
if (!Character.isWhitespace(chars[len - 1])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
|
||||||
|
|
||||||
|
if (pArmoredOutput != null) {
|
||||||
|
pArmoredOutput.write(data);
|
||||||
|
}
|
||||||
|
pSignatureGenerator.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -110,7 +110,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
|
Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
|
||||||
|
|
||||||
// Upgrade from oldVersion through all methods to newest one
|
// Upgrade from oldVersion through all cases to newest one
|
||||||
for (int version = oldVersion; version < newVersion; ++version) {
|
for (int version = oldVersion; version < newVersion; ++version) {
|
||||||
Log.w(Constants.TAG, "Upgrading database to version " + version);
|
Log.w(Constants.TAG, "Upgrading database to version " + version);
|
||||||
|
|
||||||
@ -123,14 +123,17 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
db.execSQL(CREATE_API_APPS);
|
db.execSQL(CREATE_API_APPS);
|
||||||
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
// new column: package_signature
|
// new column: package_signature
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
|
db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
|
||||||
db.execSQL(CREATE_API_APPS);
|
db.execSQL(CREATE_API_APPS);
|
||||||
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
// new column: fingerprint
|
// new column: fingerprint
|
||||||
db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
|
db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
|
||||||
+ " BLOB;");
|
+ " BLOB;");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -359,7 +359,9 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID);
|
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID);
|
||||||
// TODO: deprecated master key id
|
// TODO: deprecated master key id
|
||||||
//projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
|
//projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
|
||||||
|
|
||||||
projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
|
projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
|
||||||
|
projectionMap.put(KeysColumns.IS_REVOKED, Tables.KEYS + "." + KeysColumns.IS_REVOKED);
|
||||||
|
|
||||||
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import org.spongycastle.openpgp.PGPPublicKey;
|
|||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
@ -210,6 +211,13 @@ public class ProviderHelper {
|
|||||||
++userIdRank;
|
++userIdRank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (PGPSignature certification : new IterableIterator<PGPSignature>(masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) {
|
||||||
|
//TODO: how to do this?? we need to verify the signatures again and again when they are displayed...
|
||||||
|
// if (certification.verify
|
||||||
|
// operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
|
context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@ -562,6 +570,26 @@ public class ProviderHelper {
|
|||||||
return fingerprint;
|
return fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUserId(Context context, Uri queryUri) {
|
||||||
|
String[] projection = new String[]{UserIds.USER_ID};
|
||||||
|
Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
|
||||||
|
|
||||||
|
String userId = null;
|
||||||
|
try {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int col = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
|
||||||
|
|
||||||
|
userId = cursor.getString(col);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
|
public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
|
||||||
long[] masterKeyIds) {
|
long[] masterKeyIds) {
|
||||||
ArrayList<String> output = new ArrayList<String>();
|
ArrayList<String> output = new ArrayList<String>();
|
||||||
|
@ -43,10 +43,11 @@ import org.sufficientlysecure.keychain.helper.FileHelper;
|
|||||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
|
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpOperation;
|
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
@ -95,7 +96,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
|
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
|
||||||
public static final String ACTION_DOWNLOAD_AND_IMPORT_KEYS = Constants.INTENT_PREFIX + "QUERY_KEYRING";
|
public static final String ACTION_DOWNLOAD_AND_IMPORT_KEYS = Constants.INTENT_PREFIX + "QUERY_KEYRING";
|
||||||
|
|
||||||
public static final String ACTION_SIGN_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
|
public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
|
||||||
|
|
||||||
/* keys for data bundle */
|
/* keys for data bundle */
|
||||||
|
|
||||||
@ -119,11 +120,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
|
public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
|
||||||
|
|
||||||
// decrypt/verify
|
// decrypt/verify
|
||||||
public static final String DECRYPT_SIGNED_ONLY = "signed_only";
|
|
||||||
public static final String DECRYPT_RETURN_BYTES = "return_binary";
|
public static final String DECRYPT_RETURN_BYTES = "return_binary";
|
||||||
public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
|
public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
|
||||||
public static final String DECRYPT_ASSUME_SYMMETRIC = "assume_symmetric";
|
public static final String DECRYPT_ASSUME_SYMMETRIC = "assume_symmetric";
|
||||||
public static final String DECRYPT_LOOKUP_UNKNOWN_KEY = "lookup_unknownKey";
|
|
||||||
|
|
||||||
// save keyring
|
// save keyring
|
||||||
public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase";
|
public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase";
|
||||||
@ -166,8 +165,8 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
public static final String DOWNLOAD_KEY_LIST = "query_key_id";
|
public static final String DOWNLOAD_KEY_LIST = "query_key_id";
|
||||||
|
|
||||||
// sign key
|
// sign key
|
||||||
public static final String SIGN_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
|
public static final String CERTIFY_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
|
||||||
public static final String SIGN_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
|
public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* possible data keys as result send over messenger
|
* possible data keys as result send over messenger
|
||||||
@ -189,10 +188,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
public static final String RESULT_SIGNATURE = "signature";
|
public static final String RESULT_SIGNATURE = "signature";
|
||||||
public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id";
|
public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id";
|
||||||
public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id";
|
public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id";
|
||||||
|
public static final String RESULT_CLEARTEXT_SIGNATURE_ONLY = "signature_only";
|
||||||
|
|
||||||
public static final String RESULT_SIGNATURE_SUCCESS = "signature_success";
|
public static final String RESULT_SIGNATURE_SUCCESS = "signature_success";
|
||||||
public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown";
|
public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown";
|
||||||
public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookup_key";
|
|
||||||
|
|
||||||
// import
|
// import
|
||||||
public static final String RESULT_IMPORT_ADDED = "added";
|
public static final String RESULT_IMPORT_ADDED = "added";
|
||||||
@ -241,7 +240,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
|
||||||
// execute action from extra bundle
|
// executeServiceMethod action from extra bundle
|
||||||
if (ACTION_ENCRYPT_SIGN.equals(action)) {
|
if (ACTION_ENCRYPT_SIGN.equals(action)) {
|
||||||
try {
|
try {
|
||||||
/* Input */
|
/* Input */
|
||||||
@ -322,27 +321,41 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
PgpOperation operation = new PgpOperation(this, this, inputData, outStream);
|
PgpSignEncrypt.Builder builder =
|
||||||
|
new PgpSignEncrypt.Builder(this, inputData, outStream);
|
||||||
|
builder.progress(this);
|
||||||
|
|
||||||
if (generateSignature) {
|
if (generateSignature) {
|
||||||
Log.d(Constants.TAG, "generating signature...");
|
Log.d(Constants.TAG, "generating signature...");
|
||||||
operation.generateSignature(useAsciiArmor, false, secretKeyId,
|
builder.enableAsciiArmorOutput(useAsciiArmor)
|
||||||
PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
|
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
||||||
Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences
|
.signatureKeyId(secretKeyId)
|
||||||
.getPreferences(this).getForceV3Signatures());
|
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||||
|
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
|
builder.build().generateSignature();
|
||||||
} else if (signOnly) {
|
} else if (signOnly) {
|
||||||
Log.d(Constants.TAG, "sign only...");
|
Log.d(Constants.TAG, "sign only...");
|
||||||
operation.signText(secretKeyId, PassphraseCacheService.getCachedPassphrase(
|
builder.enableAsciiArmorOutput(useAsciiArmor)
|
||||||
this, secretKeyId), Preferences.getPreferences(this)
|
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
||||||
.getDefaultHashAlgorithm(), Preferences.getPreferences(this)
|
.signatureKeyId(secretKeyId)
|
||||||
.getForceV3Signatures());
|
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||||
|
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
|
builder.build().execute();
|
||||||
} else {
|
} else {
|
||||||
Log.d(Constants.TAG, "encrypt...");
|
Log.d(Constants.TAG, "encrypt...");
|
||||||
operation.signAndEncrypt(useAsciiArmor, compressionId, encryptionKeyIds,
|
builder.enableAsciiArmorOutput(useAsciiArmor)
|
||||||
encryptionPassphrase, Preferences.getPreferences(this)
|
.compressionId(compressionId)
|
||||||
.getDefaultEncryptionAlgorithm(), secretKeyId, Preferences
|
.symmetricEncryptionAlgorithm(Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
|
||||||
.getPreferences(this).getDefaultHashAlgorithm(), Preferences
|
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
||||||
.getPreferences(this).getForceV3Signatures(),
|
.encryptionKeyIds(encryptionKeyIds)
|
||||||
PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
.encryptionPassphrase(encryptionPassphrase)
|
||||||
|
.signatureKeyId(secretKeyId)
|
||||||
|
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||||
|
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
|
builder.build().execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
outStream.close();
|
outStream.close();
|
||||||
@ -395,12 +408,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
|
long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
|
||||||
byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
|
byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
|
||||||
boolean signedOnly = data.getBoolean(DECRYPT_SIGNED_ONLY);
|
|
||||||
boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES);
|
boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES);
|
||||||
boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC);
|
boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC);
|
||||||
|
|
||||||
boolean lookupUnknownKey = data.getBoolean(DECRYPT_LOOKUP_UNKNOWN_KEY);
|
|
||||||
|
|
||||||
InputStream inStream = null;
|
InputStream inStream = null;
|
||||||
long inLength = -1;
|
long inLength = -1;
|
||||||
InputData inputData = null;
|
InputData inputData = null;
|
||||||
@ -474,14 +484,13 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
// verifyText and decrypt returning additional resultData values for the
|
// verifyText and decrypt returning additional resultData values for the
|
||||||
// verification of signatures
|
// verification of signatures
|
||||||
PgpOperation operation = new PgpOperation(this, this, inputData, outStream);
|
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
|
||||||
if (signedOnly) {
|
builder.progress(this);
|
||||||
resultData = operation.verifyText(lookupUnknownKey);
|
|
||||||
} else {
|
builder.assumeSymmetric(assumeSymmetricEncryption)
|
||||||
resultData = operation.decryptAndVerify(
|
.passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
|
|
||||||
assumeSymmetricEncryption);
|
resultData = builder.build().execute();
|
||||||
}
|
|
||||||
|
|
||||||
outStream.close();
|
outStream.close();
|
||||||
|
|
||||||
@ -785,19 +794,19 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
sendErrorToHandler(e);
|
sendErrorToHandler(e);
|
||||||
}
|
}
|
||||||
} else if (ACTION_SIGN_KEYRING.equals(action)) {
|
} else if (ACTION_CERTIFY_KEYRING.equals(action)) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
/* Input */
|
/* Input */
|
||||||
long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID);
|
long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
|
||||||
long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID);
|
long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
|
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
|
||||||
masterKeyId);
|
masterKeyId);
|
||||||
|
|
||||||
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
|
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
|
||||||
PGPPublicKeyRing signedPubKeyRing = keyOperation.signKey(masterKeyId, pubKeyId,
|
PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
|
||||||
signaturePassPhrase);
|
signaturePassPhrase);
|
||||||
|
|
||||||
// store the signed key in our local cache
|
// store the signed key in our local cache
|
||||||
|
@ -25,6 +25,7 @@ import android.os.Bundle;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
public class KeychainIntentServiceHandler extends Handler {
|
public class KeychainIntentServiceHandler extends Handler {
|
||||||
@ -60,7 +61,14 @@ public class KeychainIntentServiceHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showProgressDialog(FragmentActivity activity) {
|
public void showProgressDialog(FragmentActivity activity) {
|
||||||
mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog");
|
// TODO: This is a hack!, see http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
|
||||||
|
final FragmentManager manager = activity.getSupportFragmentManager();
|
||||||
|
Handler handler = new Handler();
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mProgressDialogFragment.show(manager, "progressDialog");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package org.sufficientlysecure.keychain.service.exception;
|
|
||||||
|
|
||||||
public class NoUserIdsException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 7009311527126696207L;
|
|
||||||
|
|
||||||
public NoUserIdsException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.sufficientlysecure.keychain.service.exception;
|
|
||||||
|
|
||||||
public class UserInteractionRequiredException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -60128148603511936L;
|
|
||||||
|
|
||||||
public UserInteractionRequiredException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.sufficientlysecure.keychain.service.exception;
|
|
||||||
|
|
||||||
public class WrongPassphraseException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -5309689232853485740L;
|
|
||||||
|
|
||||||
public WrongPassphraseException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -109,6 +109,15 @@ public class AppSettingsFragment extends Fragment implements
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set error String on key selection
|
||||||
|
*
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
public void setErrorOnSelectKeyFragment(String error) {
|
||||||
|
mSelectKeyFragment.setError(error);
|
||||||
|
}
|
||||||
|
|
||||||
private void initView(View view) {
|
private void initView(View view) {
|
||||||
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
|
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
|
||||||
R.id.api_app_settings_select_key_fragment);
|
R.id.api_app_settings_select_key_fragment);
|
||||||
@ -182,7 +191,7 @@ public class AppSettingsFragment extends Fragment implements
|
|||||||
// TODO: Better: collapse/expand animation
|
// TODO: Better: collapse/expand animation
|
||||||
// final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
|
// final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
|
||||||
// Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
|
// Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
|
||||||
// Animation.RELATIVE_TO_SELF, 0.0f);
|
// Animation.RELATIVE_TO_SELF, 0.0f);u
|
||||||
// animation2.setDuration(150);
|
// animation2.setDuration(150);
|
||||||
|
|
||||||
mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
|
mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -17,99 +17,38 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.service.remote;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import android.app.PendingIntent;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.IOpenPgpCallback;
|
|
||||||
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
|
|
||||||
import org.openintents.openpgp.IOpenPgpService;
|
|
||||||
import org.openintents.openpgp.OpenPgpData;
|
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.spongycastle.util.Arrays;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpOperation;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
|
||||||
import org.sufficientlysecure.keychain.service.exception.NoUserIdsException;
|
|
||||||
import org.sufficientlysecure.keychain.service.exception.UserInteractionRequiredException;
|
|
||||||
import org.sufficientlysecure.keychain.service.exception.WrongPassphraseException;
|
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Message;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
|
||||||
|
import org.openintents.openpgp.IOpenPgpService;
|
||||||
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||||
|
import org.spongycastle.util.Arrays;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class OpenPgpService extends RemoteService {
|
public class OpenPgpService extends RemoteService {
|
||||||
|
|
||||||
private String getCachedPassphrase(long keyId, boolean allowUserInteraction)
|
private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
|
||||||
throws UserInteractionRequiredException {
|
private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
|
||||||
String passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
|
|
||||||
|
|
||||||
if (passphrase == null) {
|
|
||||||
if (!allowUserInteraction) {
|
|
||||||
throw new UserInteractionRequiredException(
|
|
||||||
"Passphrase not found in cache, please enter your passphrase!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(Constants.TAG, "No passphrase! Activity required!");
|
|
||||||
|
|
||||||
// start passphrase dialog
|
|
||||||
PassphraseActivityCallback callback = new PassphraseActivityCallback();
|
|
||||||
Bundle extras = new Bundle();
|
|
||||||
extras.putLong(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
|
||||||
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE, callback,
|
|
||||||
extras);
|
|
||||||
|
|
||||||
if (callback.isSuccess()) {
|
|
||||||
Log.d(Constants.TAG, "New passphrase entered!");
|
|
||||||
|
|
||||||
// get again after it was entered
|
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
|
|
||||||
} else {
|
|
||||||
Log.d(Constants.TAG, "Passphrase dialog canceled!");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return passphrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PassphraseActivityCallback extends UserInputCallback {
|
|
||||||
|
|
||||||
private boolean success = false;
|
|
||||||
|
|
||||||
public boolean isSuccess() {
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleUserInput(Message msg) {
|
|
||||||
if (msg.arg1 == OKAY) {
|
|
||||||
success = true;
|
|
||||||
} else {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search database for key ids based on emails.
|
* Search database for key ids based on emails.
|
||||||
@ -117,8 +56,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
* @param encryptionUserIds
|
* @param encryptionUserIds
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private long[] getKeyIdsFromEmails(String[] encryptionUserIds, boolean allowUserInteraction)
|
private Bundle getKeyIdsFromEmails(Bundle params, String[] encryptionUserIds) {
|
||||||
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>();
|
||||||
|
|
||||||
@ -152,96 +90,129 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow the user to verify pub key selection
|
// allow the user to verify pub key selection
|
||||||
if (allowUserInteraction && (missingUserIdsCheck || dublicateUserIdsCheck)) {
|
if (missingUserIdsCheck || dublicateUserIdsCheck) {
|
||||||
SelectPubKeysActivityCallback callback = new SelectPubKeysActivityCallback();
|
// build PendingIntent for passphrase input
|
||||||
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
|
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
|
||||||
|
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
|
|
||||||
Bundle extras = new Bundle();
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
|
||||||
extras.putLongArray(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
|
||||||
extras.putStringArrayList(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
|
||||||
extras.putStringArrayList(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS,
|
|
||||||
dublicateUserIds);
|
|
||||||
|
|
||||||
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS, callback,
|
// return PendingIntent to be executed by client
|
||||||
extras);
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||||
|
|
||||||
if (callback.isSuccess()) {
|
return result;
|
||||||
Log.d(Constants.TAG, "New selection of pub keys!");
|
|
||||||
keyIdsArray = callback.getPubKeyIds();
|
|
||||||
} else {
|
|
||||||
Log.d(Constants.TAG, "Pub key selection canceled!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no user interaction is allow throw exceptions on duplicate or missing pub keys
|
|
||||||
if (!allowUserInteraction) {
|
|
||||||
if (missingUserIdsCheck)
|
|
||||||
throw new UserInteractionRequiredException(
|
|
||||||
"Pub keys for these user ids are missing:" + missingUserIds.toString());
|
|
||||||
if (dublicateUserIdsCheck)
|
|
||||||
throw new UserInteractionRequiredException(
|
|
||||||
"More than one pub key with these user ids exist:"
|
|
||||||
+ dublicateUserIds.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyIdsArray.length == 0) {
|
if (keyIdsArray.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return keyIdsArray;
|
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||||
|
result.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS, keyIdsArray);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SelectPubKeysActivityCallback extends UserInputCallback {
|
private Bundle getPassphraseBundleIntent(Bundle params, long keyId) {
|
||||||
public static final String PUB_KEY_IDS = "pub_key_ids";
|
// build PendingIntent for passphrase input
|
||||||
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
|
intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
||||||
|
// pass params through to activity that it can be returned again later to repeat pgp operation
|
||||||
|
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
|
||||||
|
|
||||||
private boolean success = false;
|
// return PendingIntent to be executed by client
|
||||||
private long[] pubKeyIds;
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||||
|
|
||||||
public boolean isSuccess() {
|
return result;
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] getPubKeyIds() {
|
private Bundle signImpl(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output,
|
||||||
return pubKeyIds;
|
AppSettings appSettings) {
|
||||||
}
|
try {
|
||||||
|
boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
@Override
|
// get passphrase from cache, if key has "no" passphrase, this returns an empty String
|
||||||
public void handleUserInput(Message msg) {
|
String passphrase;
|
||||||
if (msg.arg1 == OKAY) {
|
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
|
||||||
success = true;
|
passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
|
||||||
pubKeyIds = msg.getData().getLongArray(PUB_KEY_IDS);
|
|
||||||
} else {
|
} else {
|
||||||
success = false;
|
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
|
||||||
}
|
}
|
||||||
|
if (passphrase == null) {
|
||||||
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
|
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
|
||||||
|
return passphraseBundle;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
private synchronized void getKeyIdsSafe(String[] userIds, boolean allowUserInteraction,
|
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||||
IOpenPgpKeyIdsCallback callback, AppSettings appSettings) {
|
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
||||||
|
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
|
||||||
try {
|
try {
|
||||||
long[] keyIds = getKeyIdsFromEmails(userIds, allowUserInteraction);
|
long inputLength = is.available();
|
||||||
if (keyIds == null) {
|
InputData inputData = new InputData(is, inputLength);
|
||||||
throw new NoUserIdsException("No user ids!");
|
|
||||||
|
// sign-only
|
||||||
|
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
|
||||||
|
builder.enableAsciiArmorOutput(asciiArmor)
|
||||||
|
.signatureHashAlgorithm(appSettings.getHashAlgorithm())
|
||||||
|
.signatureForceV3(false)
|
||||||
|
.signatureKeyId(appSettings.getKeyId())
|
||||||
|
.signaturePassphrase(passphrase);
|
||||||
|
builder.build().execute();
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
os.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.onSuccess(keyIds);
|
Bundle result = new Bundle();
|
||||||
} catch (UserInteractionRequiredException e) {
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||||
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
|
return result;
|
||||||
} catch (NoUserIdsException e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.NO_USER_IDS, e.getMessage());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||||
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void encryptAndSignSafe(OpenPgpData inputData,
|
private Bundle encryptAndSignImpl(Bundle params, ParcelFileDescriptor input,
|
||||||
final OpenPgpData outputData, long[] keyIds, boolean allowUserInteraction,
|
ParcelFileDescriptor output, AppSettings appSettings,
|
||||||
IOpenPgpCallback callback, AppSettings appSettings, boolean sign) {
|
boolean sign) {
|
||||||
try {
|
try {
|
||||||
// TODO: other options of OpenPgpData!
|
boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
|
||||||
byte[] inputBytes = getInput(inputData);
|
|
||||||
boolean asciiArmor = false;
|
long[] keyIds;
|
||||||
if (outputData.getType() == OpenPgpData.TYPE_STRING) {
|
if (params.containsKey(OpenPgpConstants.PARAMS_KEY_IDS)) {
|
||||||
asciiArmor = true;
|
keyIds = params.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
|
||||||
|
} else if (params.containsKey(OpenPgpConstants.PARAMS_USER_IDS)) {
|
||||||
|
// get key ids based on given user ids
|
||||||
|
String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
|
||||||
|
// give params through to activity...
|
||||||
|
Bundle result = getKeyIdsFromEmails(params, userIds);
|
||||||
|
|
||||||
|
if (result.getInt(OpenPgpConstants.RESULT_CODE, 0) == OpenPgpConstants.RESULT_CODE_SUCCESS) {
|
||||||
|
keyIds = result.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
|
||||||
|
} else {
|
||||||
|
// if not success -> result contains a PendingIntent for user interaction
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||||
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add own key for encryption
|
// add own key for encryption
|
||||||
@ -249,351 +220,339 @@ public class OpenPgpService extends RemoteService {
|
|||||||
keyIds[keyIds.length - 1] = appSettings.getKeyId();
|
keyIds[keyIds.length - 1] = appSettings.getKeyId();
|
||||||
|
|
||||||
// build InputData and write into OutputStream
|
// build InputData and write into OutputStream
|
||||||
InputStream inputStream = new ByteArrayInputStream(inputBytes);
|
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||||
long inputLength = inputBytes.length;
|
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
||||||
InputData inputDt = new InputData(inputStream, inputLength);
|
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
|
||||||
|
try {
|
||||||
|
long inputLength = is.available();
|
||||||
|
InputData inputData = new InputData(is, inputLength);
|
||||||
|
|
||||||
OutputStream outputStream = new ByteArrayOutputStream();
|
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
|
||||||
|
builder.enableAsciiArmorOutput(asciiArmor)
|
||||||
|
.compressionId(appSettings.getCompression())
|
||||||
|
.symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
|
||||||
|
.encryptionKeyIds(keyIds);
|
||||||
|
|
||||||
PgpOperation operation = new PgpOperation(getContext(), null, inputDt, outputStream);
|
|
||||||
if (sign) {
|
if (sign) {
|
||||||
String passphrase = getCachedPassphrase(appSettings.getKeyId(),
|
String passphrase;
|
||||||
allowUserInteraction);
|
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
|
||||||
if (passphrase == null) {
|
passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
|
||||||
throw new WrongPassphraseException("No or wrong passphrase!");
|
|
||||||
}
|
|
||||||
|
|
||||||
operation.signAndEncrypt(asciiArmor, appSettings.getCompression(), keyIds, null,
|
|
||||||
appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
|
|
||||||
appSettings.getHashAlgorithm(), true, passphrase);
|
|
||||||
} else {
|
} else {
|
||||||
operation.signAndEncrypt(asciiArmor, appSettings.getCompression(), keyIds, null,
|
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
|
||||||
appSettings.getEncryptionAlgorithm(), Id.key.none,
|
appSettings.getKeyId());
|
||||||
appSettings.getHashAlgorithm(), true, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.close();
|
|
||||||
|
|
||||||
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
|
|
||||||
callback.onSuccess(output, null);
|
|
||||||
} catch (UserInteractionRequiredException e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
|
|
||||||
} catch (WrongPassphraseException e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
|
|
||||||
} catch (Exception e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: asciiArmor?!
|
|
||||||
private void signSafe(byte[] inputBytes, boolean allowUserInteraction,
|
|
||||||
IOpenPgpCallback callback, AppSettings appSettings) {
|
|
||||||
try {
|
|
||||||
// build InputData and write into OutputStream
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(inputBytes);
|
|
||||||
long inputLength = inputBytes.length;
|
|
||||||
InputData inputData = new InputData(inputStream, inputLength);
|
|
||||||
|
|
||||||
OutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
String passphrase = getCachedPassphrase(appSettings.getKeyId(), allowUserInteraction);
|
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
throw new WrongPassphraseException("No or wrong passphrase!");
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
|
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
|
||||||
|
return passphraseBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream);
|
// sign and encrypt
|
||||||
operation.signText(appSettings.getKeyId(), passphrase, appSettings.getHashAlgorithm(),
|
builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
|
||||||
Preferences.getPreferences(this).getForceV3Signatures());
|
.signatureForceV3(false)
|
||||||
|
.signatureKeyId(appSettings.getKeyId())
|
||||||
|
.signaturePassphrase(passphrase);
|
||||||
|
} else {
|
||||||
|
// encrypt only
|
||||||
|
builder.signatureKeyId(Id.key.none);
|
||||||
|
}
|
||||||
|
// execute PGP operation!
|
||||||
|
builder.build().execute();
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
|
||||||
outputStream.close();
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||||
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
|
return result;
|
||||||
OpenPgpData output = new OpenPgpData(new String(outputBytes));
|
|
||||||
|
|
||||||
// return over handler on client side
|
|
||||||
callback.onSuccess(output, null);
|
|
||||||
} catch (UserInteractionRequiredException e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
|
|
||||||
} catch (WrongPassphraseException e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||||
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void decryptAndVerifySafe(byte[] inputBytes, boolean allowUserInteraction,
|
private Bundle decryptAndVerifyImpl(Bundle params, ParcelFileDescriptor input,
|
||||||
IOpenPgpCallback callback, AppSettings appSettings) {
|
ParcelFileDescriptor output, AppSettings appSettings) {
|
||||||
try {
|
try {
|
||||||
|
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||||
|
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
||||||
|
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
|
||||||
|
OpenPgpSignatureResult sigResult = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
// PGPUtil.getDecoderStream(is)
|
||||||
|
// TODOs API 2.0:
|
||||||
|
// implement verify-only!
|
||||||
|
// fix the mess: http://stackoverflow.com/questions/148130/how-do-i-peek-at-the-first-two-bytes-in-an-inputstream
|
||||||
|
// should we allow to decrypt everything under every key id or only the one set?
|
||||||
|
// TODO: instead of trying to get the passphrase before
|
||||||
|
// pause stream when passphrase is missing and then resume
|
||||||
|
|
||||||
|
|
||||||
// TODO: this is not really needed
|
// TODO: this is not really needed
|
||||||
// checked if it is text with BEGIN and END tags
|
// checked if it is text with BEGIN and END tags
|
||||||
String message = new String(inputBytes);
|
// String message = new String(inputBytes);
|
||||||
Log.d(Constants.TAG, "in: " + message);
|
// Log.d(Constants.TAG, "in: " + message);
|
||||||
boolean signedOnly = false;
|
// boolean signedOnly = false;
|
||||||
Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
|
// Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
|
||||||
if (matcher.matches()) {
|
// if (matcher.matches()) {
|
||||||
Log.d(Constants.TAG, "PGP_MESSAGE matched");
|
// Log.d(Constants.TAG, "PGP_MESSAGE matched");
|
||||||
message = matcher.group(1);
|
// message = matcher.group(1);
|
||||||
// replace non breakable spaces
|
// // replace non breakable spaces
|
||||||
message = message.replaceAll("\\xa0", " ");
|
// message = message.replaceAll("\\xa0", " ");
|
||||||
|
//
|
||||||
// overwrite inputBytes
|
// // overwrite inputBytes
|
||||||
inputBytes = message.getBytes();
|
// inputBytes = message.getBytes();
|
||||||
} else {
|
// } else {
|
||||||
matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
|
// matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
|
||||||
if (matcher.matches()) {
|
// if (matcher.matches()) {
|
||||||
signedOnly = true;
|
// signedOnly = true;
|
||||||
Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
|
// Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
|
||||||
message = matcher.group(1);
|
// message = matcher.group(1);
|
||||||
// replace non breakable spaces
|
// // replace non breakable spaces
|
||||||
message = message.replaceAll("\\xa0", " ");
|
// message = message.replaceAll("\\xa0", " ");
|
||||||
|
//
|
||||||
// overwrite inputBytes
|
// // overwrite inputBytes
|
||||||
inputBytes = message.getBytes();
|
// inputBytes = message.getBytes();
|
||||||
} else {
|
// } else {
|
||||||
Log.d(Constants.TAG, "Nothing matched! Binary?");
|
// Log.d(Constants.TAG, "Nothing matched! Binary?");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// END TODO
|
// END TODO
|
||||||
|
|
||||||
Log.d(Constants.TAG, "in: " + new String(inputBytes));
|
// Log.d(Constants.TAG, "in: " + new String(inputBytes));
|
||||||
|
|
||||||
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
|
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
|
||||||
// app, Fix this?
|
// app, Fix this?
|
||||||
|
|
||||||
String passphrase = null;
|
// String passphrase = null;
|
||||||
if (!signedOnly) {
|
// if (!signedOnly) {
|
||||||
// BEGIN Get key
|
// // BEGIN Get key
|
||||||
// TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
|
// // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
|
||||||
// better!
|
// // better!
|
||||||
InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
|
// InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
|
||||||
|
//
|
||||||
|
// // TODO: duplicates functions from DecryptActivity!
|
||||||
|
// long secretKeyId;
|
||||||
|
// try {
|
||||||
|
// if (inputStream2.markSupported()) {
|
||||||
|
// // should probably set this to the max size of two
|
||||||
|
// // pgpF objects, if it even needs to be anything other
|
||||||
|
// // than 0.
|
||||||
|
// inputStream2.mark(200);
|
||||||
|
// }
|
||||||
|
// secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
|
||||||
|
// if (secretKeyId == Id.key.none) {
|
||||||
|
// throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
|
||||||
|
// }
|
||||||
|
// } catch (NoAsymmetricEncryptionException e) {
|
||||||
|
// if (inputStream2.markSupported()) {
|
||||||
|
// inputStream2.reset();
|
||||||
|
// }
|
||||||
|
// secretKeyId = Id.key.symmetric;
|
||||||
|
// if (!PgpDecryptVerify.hasSymmetricEncryption(this, inputStream2)) {
|
||||||
|
// throw new PgpGeneralException(
|
||||||
|
// getString(R.string.error_no_known_encryption_found));
|
||||||
|
// }
|
||||||
|
// // we do not support symmetric decryption from the API!
|
||||||
|
// throw new Exception("Symmetric decryption is not supported!");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
|
||||||
|
|
||||||
// TODO: duplicates functions from DecryptActivity!
|
// NOTE: currently this only gets the passphrase for the key set for this client
|
||||||
long secretKeyId;
|
String passphrase;
|
||||||
try {
|
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
|
||||||
if (inputStream2.markSupported()) {
|
passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
|
||||||
// should probably set this to the max size of two
|
} else {
|
||||||
// pgpF objects, if it even needs to be anything other
|
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
|
||||||
// than 0.
|
|
||||||
inputStream2.mark(200);
|
|
||||||
}
|
}
|
||||||
secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
|
|
||||||
if (secretKeyId == Id.key.none) {
|
|
||||||
throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
|
|
||||||
}
|
|
||||||
} catch (NoAsymmetricEncryptionException e) {
|
|
||||||
if (inputStream2.markSupported()) {
|
|
||||||
inputStream2.reset();
|
|
||||||
}
|
|
||||||
secretKeyId = Id.key.symmetric;
|
|
||||||
if (!PgpOperation.hasSymmetricEncryption(this, inputStream2)) {
|
|
||||||
throw new PgpGeneralException(
|
|
||||||
getString(R.string.error_no_known_encryption_found));
|
|
||||||
}
|
|
||||||
// we do not support symmetric decryption from the API!
|
|
||||||
throw new Exception("Symmetric decryption is not supported!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
|
|
||||||
|
|
||||||
passphrase = getCachedPassphrase(secretKeyId, allowUserInteraction);
|
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
throw new WrongPassphraseException("No or wrong passphrase!");
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
}
|
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
|
||||||
|
return passphraseBundle;
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
// build InputData and write into OutputStream
|
// build InputData and write into OutputStream
|
||||||
InputStream inputStream = new ByteArrayInputStream(inputBytes);
|
long inputLength = is.available();
|
||||||
long inputLength = inputBytes.length;
|
InputData inputData = new InputData(is, inputLength);
|
||||||
InputData inputData = new InputData(inputStream, inputLength);
|
|
||||||
|
|
||||||
OutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
Bundle outputBundle;
|
Bundle outputBundle;
|
||||||
PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream);
|
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
|
||||||
if (signedOnly) {
|
|
||||||
// TODO: download missing keys from keyserver?
|
|
||||||
outputBundle = operation.verifyText(false);
|
|
||||||
} else {
|
|
||||||
outputBundle = operation.decryptAndVerify(passphrase, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
outputStream.close();
|
// if (signedOnly) {
|
||||||
|
// outputBundle = builder.build().verifyText();
|
||||||
|
// } else {
|
||||||
|
builder.assumeSymmetric(false)
|
||||||
|
.passphrase(passphrase);
|
||||||
|
|
||||||
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
|
// Do we want to do this: instead of trying to get the passphrase before
|
||||||
|
// pause stream when passphrase is missing and then resume???
|
||||||
|
|
||||||
|
// TODO: this also decrypts with other secret keys without passphrase!!!
|
||||||
|
outputBundle = builder.build().execute();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// outputStream.close();
|
||||||
|
|
||||||
|
// byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
|
||||||
|
|
||||||
// get signature informations from bundle
|
// get signature informations from bundle
|
||||||
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE);
|
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false);
|
||||||
|
|
||||||
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, 0);
|
||||||
String signatureUserId = outputBundle
|
String signatureUserId = outputBundle
|
||||||
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
|
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
|
||||||
boolean signatureSuccess = outputBundle
|
boolean signatureSuccess = outputBundle
|
||||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS);
|
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
|
||||||
boolean signatureUnknown = outputBundle
|
boolean signatureUnknown = outputBundle
|
||||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN);
|
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, false);
|
||||||
|
boolean signatureOnly = outputBundle
|
||||||
|
.getBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
|
||||||
|
|
||||||
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
|
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
|
||||||
if (signatureSuccess) {
|
if (signatureSuccess) {
|
||||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_TRUSTED;
|
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED;
|
||||||
} else if (signatureUnknown) {
|
} else if (signatureUnknown) {
|
||||||
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
|
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
|
sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
|
||||||
signedOnly, signatureKeyId);
|
signatureOnly, signatureKeyId);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
is.close();
|
||||||
|
os.close();
|
||||||
}
|
}
|
||||||
OpenPgpData output = new OpenPgpData(new String(outputBytes));
|
|
||||||
|
|
||||||
// return over handler on client side
|
Bundle result = new Bundle();
|
||||||
callback.onSuccess(output, sigResult);
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
|
||||||
} catch (UserInteractionRequiredException e) {
|
result.putParcelable(OpenPgpConstants.RESULT_SIGNATURE, sigResult);
|
||||||
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
|
return result;
|
||||||
} catch (WrongPassphraseException e) {
|
|
||||||
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||||
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Bundle getKeyIdsImpl(Bundle params) {
|
||||||
|
// get key ids based on given user ids
|
||||||
|
String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
|
||||||
|
Bundle result = getKeyIdsFromEmails(params, userIds);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns error to IOpenPgpCallback
|
* Check requirements:
|
||||||
|
* - params != null
|
||||||
|
* - has supported API version
|
||||||
|
* - is allowed to call the service (access has been granted)
|
||||||
*
|
*
|
||||||
* @param callback
|
* @param params
|
||||||
* @param errorId
|
* @return null if everything is okay, or a Bundle with an error/PendingIntent
|
||||||
* @param message
|
|
||||||
*/
|
*/
|
||||||
private void callbackOpenPgpError(IOpenPgpCallback callback, int errorId, String message) {
|
private Bundle checkRequirements(Bundle params) {
|
||||||
try {
|
// params Bundle is required!
|
||||||
callback.onError(new OpenPgpError(0, message));
|
if (params == null) {
|
||||||
} catch (Exception t) {
|
Bundle result = new Bundle();
|
||||||
Log.e(Constants.TAG,
|
OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!");
|
||||||
"Exception while returning OpenPgpError to client via callback.onError()", t);
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
|
||||||
}
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callbackOpenPgpError(IOpenPgpKeyIdsCallback callback, int errorId, String message) {
|
// version code is required and needs to correspond to version code of service!
|
||||||
try {
|
if (params.getInt(OpenPgpConstants.PARAMS_API_VERSION) != OpenPgpConstants.API_VERSION) {
|
||||||
callback.onError(new OpenPgpError(0, message));
|
Bundle result = new Bundle();
|
||||||
} catch (Exception t) {
|
OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
|
||||||
Log.e(Constants.TAG,
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
|
||||||
"Exception while returning OpenPgpError to client via callback.onError()", t);
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
}
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if caller is allowed to access openpgp keychain
|
||||||
|
Bundle result = isAllowed(params);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: multi-threading
|
||||||
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
|
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encrypt(final OpenPgpData input, final OpenPgpData output, final long[] keyIds,
|
public Bundle sign(Bundle params, final ParcelFileDescriptor input, final ParcelFileDescriptor output) {
|
||||||
final IOpenPgpCallback callback) throws RemoteException {
|
final AppSettings appSettings = getAppSettings();
|
||||||
final AppSettings settings = getAppSettings();
|
|
||||||
|
|
||||||
Runnable r = new Runnable() {
|
Bundle errorResult = checkRequirements(params);
|
||||||
@Override
|
if (errorResult != null) {
|
||||||
public void run() {
|
return errorResult;
|
||||||
encryptAndSignSafe(input, output, keyIds, true, callback, settings, false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
checkAndEnqueue(r);
|
return signImpl(params, input, output, appSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void signAndEncrypt(final OpenPgpData input, final OpenPgpData output,
|
public Bundle encrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||||
final long[] keyIds, final IOpenPgpCallback callback) throws RemoteException {
|
final AppSettings appSettings = getAppSettings();
|
||||||
final AppSettings settings = getAppSettings();
|
|
||||||
|
|
||||||
Runnable r = new Runnable() {
|
Bundle errorResult = checkRequirements(params);
|
||||||
@Override
|
if (errorResult != null) {
|
||||||
public void run() {
|
return errorResult;
|
||||||
encryptAndSignSafe(input, output, keyIds, true, callback, settings, true);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
checkAndEnqueue(r);
|
return encryptAndSignImpl(params, input, output, appSettings, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sign(final OpenPgpData input, final OpenPgpData output,
|
public Bundle signAndEncrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||||
final IOpenPgpCallback callback) throws RemoteException {
|
final AppSettings appSettings = getAppSettings();
|
||||||
final AppSettings settings = getAppSettings();
|
|
||||||
|
|
||||||
Runnable r = new Runnable() {
|
Bundle errorResult = checkRequirements(params);
|
||||||
@Override
|
if (errorResult != null) {
|
||||||
public void run() {
|
return errorResult;
|
||||||
signSafe(getInput(input), true, callback, settings);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
checkAndEnqueue(r);
|
return encryptAndSignImpl(params, input, output, appSettings, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decryptAndVerify(final OpenPgpData input, final OpenPgpData output,
|
public Bundle decryptAndVerify(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||||
final IOpenPgpCallback callback) throws RemoteException {
|
final AppSettings appSettings = getAppSettings();
|
||||||
|
|
||||||
final AppSettings settings = getAppSettings();
|
Bundle errorResult = checkRequirements(params);
|
||||||
|
if (errorResult != null) {
|
||||||
Runnable r = new Runnable() {
|
return errorResult;
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
decryptAndVerifySafe(getInput(input), true, callback, settings);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
checkAndEnqueue(r);
|
return decryptAndVerifyImpl(params, input, output, appSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getKeyIds(final String[] userIds, final boolean allowUserInteraction,
|
public Bundle getKeyIds(Bundle params) {
|
||||||
final IOpenPgpKeyIdsCallback callback) throws RemoteException {
|
Bundle errorResult = checkRequirements(params);
|
||||||
|
if (errorResult != null) {
|
||||||
final AppSettings settings = getAppSettings();
|
return errorResult;
|
||||||
|
|
||||||
Runnable r = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
getKeyIdsSafe(userIds, allowUserInteraction, callback, settings);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
checkAndEnqueue(r);
|
return getKeyIdsImpl(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,17 +19,16 @@ package org.sufficientlysecure.keychain.service.remote;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.exception.WrongPackageSignatureException;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor;
|
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -50,66 +49,19 @@ import android.os.Messenger;
|
|||||||
public abstract class RemoteService extends Service {
|
public abstract class RemoteService extends Service {
|
||||||
Context mContext;
|
Context mContext;
|
||||||
|
|
||||||
private final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
|
private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
|
||||||
// TODO: Are these parameters okay?
|
private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
|
||||||
private PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
|
|
||||||
TimeUnit.SECONDS, mPoolQueue);
|
|
||||||
|
|
||||||
private final Object userInputLock = new Object();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override handleUserInput() to handle OKAY (1) and CANCEL (0). After handling the waiting
|
|
||||||
* threads will be notified and the queue resumed
|
|
||||||
*/
|
|
||||||
protected class UserInputCallback extends BaseCallback {
|
|
||||||
|
|
||||||
public void handleUserInput(Message msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleMessage(Message msg) {
|
|
||||||
handleUserInput(msg);
|
|
||||||
|
|
||||||
// resume
|
|
||||||
synchronized (userInputLock) {
|
|
||||||
userInputLock.notifyAll();
|
|
||||||
}
|
|
||||||
mThreadPool.resume();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extends Handler.Callback with OKAY (1), CANCEL (0) variables
|
|
||||||
*/
|
|
||||||
private class BaseCallback implements Handler.Callback {
|
|
||||||
public static final int OKAY = 1;
|
|
||||||
public static final int CANCEL = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleMessage(Message msg) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return mContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected Bundle isAllowed(Bundle params) {
|
||||||
* Should be used from Stub implementations of AIDL interfaces to enqueue a runnable for
|
|
||||||
* execution
|
|
||||||
*
|
|
||||||
* @param r
|
|
||||||
*/
|
|
||||||
protected void checkAndEnqueue(Runnable r) {
|
|
||||||
try {
|
try {
|
||||||
if (isCallerAllowed(false)) {
|
if (isCallerAllowed(false)) {
|
||||||
mThreadPool.execute(r);
|
|
||||||
|
|
||||||
Log.d(Constants.TAG, "Enqueued runnable…");
|
return null;
|
||||||
} else {
|
} else {
|
||||||
String[] callingPackages = getPackageManager().getPackagesForUid(
|
String[] callingPackages = getPackageManager().getPackagesForUid(
|
||||||
Binder.getCallingUid());
|
Binder.getCallingUid());
|
||||||
@ -121,32 +73,46 @@ public abstract class RemoteService extends Service {
|
|||||||
packageSignature = getPackageSignature(packageName);
|
packageSignature = getPackageSignature(packageName);
|
||||||
} catch (NameNotFoundException e) {
|
} catch (NameNotFoundException e) {
|
||||||
Log.e(Constants.TAG, "Should not happen, returning!", e);
|
Log.e(Constants.TAG, "Should not happen, returning!", e);
|
||||||
return;
|
// return error
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
|
||||||
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
Log.e(Constants.TAG,
|
Log.e(Constants.TAG, "Not allowed to use service! return PendingIntent for registration!");
|
||||||
"Not allowed to use service! Starting activity for registration!");
|
|
||||||
Bundle extras = new Bundle();
|
|
||||||
extras.putString(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
|
|
||||||
extras.putByteArray(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
|
|
||||||
RegisterActivityCallback callback = new RegisterActivityCallback();
|
|
||||||
|
|
||||||
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_REGISTER, callback,
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
extras);
|
intent.setAction(RemoteServiceActivity.ACTION_REGISTER);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
|
||||||
|
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
|
|
||||||
if (callback.isAllowed()) {
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
|
||||||
mThreadPool.execute(r);
|
|
||||||
Log.d(Constants.TAG, "Enqueued runnable…");
|
// return PendingIntent to be executed by client
|
||||||
} else {
|
Bundle result = new Bundle();
|
||||||
Log.d(Constants.TAG, "User disallowed app!");
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
}
|
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
} catch (WrongPackageSignatureException e) {
|
} catch (WrongPackageSignatureException e) {
|
||||||
Log.e(Constants.TAG, e.getMessage());
|
Log.e(Constants.TAG, "wrong signature!", e);
|
||||||
|
|
||||||
Bundle extras = new Bundle();
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
extras.putString(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
|
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
|
||||||
getString(R.string.api_error_wrong_signature));
|
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, getString(R.string.api_error_wrong_signature));
|
||||||
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_ERROR_MESSAGE, null, extras);
|
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
|
|
||||||
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0);
|
||||||
|
|
||||||
|
// return PendingIntent to be executed by client
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
|
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,38 +126,6 @@ public abstract class RemoteService extends Service {
|
|||||||
return packageSignature;
|
return packageSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Locks current thread and pauses execution of runnables and starts activity for user input
|
|
||||||
*
|
|
||||||
* @param action
|
|
||||||
* @param messenger
|
|
||||||
* @param extras
|
|
||||||
*/
|
|
||||||
protected void pauseAndStartUserInteraction(String action, BaseCallback callback, Bundle extras) {
|
|
||||||
synchronized (userInputLock) {
|
|
||||||
mThreadPool.pause();
|
|
||||||
|
|
||||||
Log.d(Constants.TAG, "starting activity...");
|
|
||||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
intent.setAction(action);
|
|
||||||
|
|
||||||
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
|
|
||||||
|
|
||||||
extras.putParcelable(RemoteServiceActivity.EXTRA_MESSENGER, messenger);
|
|
||||||
intent.putExtras(extras);
|
|
||||||
|
|
||||||
startActivity(intent);
|
|
||||||
|
|
||||||
// lock current thread for user input
|
|
||||||
try {
|
|
||||||
userInputLock.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoService", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves AppSettings from database for the application calling this remote service
|
* Retrieves AppSettings from database for the application calling this remote service
|
||||||
*
|
*
|
||||||
@ -215,66 +149,11 @@ public abstract class RemoteService extends Service {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterActivityCallback extends BaseCallback {
|
|
||||||
public static final String PACKAGE_NAME = "package_name";
|
|
||||||
|
|
||||||
private boolean allowed = false;
|
|
||||||
private String packageName;
|
|
||||||
|
|
||||||
public boolean isAllowed() {
|
|
||||||
return allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPackageName() {
|
|
||||||
return packageName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleMessage(Message msg) {
|
|
||||||
if (msg.arg1 == OKAY) {
|
|
||||||
allowed = true;
|
|
||||||
packageName = msg.getData().getString(PACKAGE_NAME);
|
|
||||||
|
|
||||||
// resume threads
|
|
||||||
try {
|
|
||||||
if (isPackageAllowed(packageName)) {
|
|
||||||
synchronized (userInputLock) {
|
|
||||||
userInputLock.notifyAll();
|
|
||||||
}
|
|
||||||
mThreadPool.resume();
|
|
||||||
} else {
|
|
||||||
// Should not happen!
|
|
||||||
Log.e(Constants.TAG, "Should not happen! Emergency shutdown!");
|
|
||||||
mThreadPool.shutdownNow();
|
|
||||||
}
|
|
||||||
} catch (WrongPackageSignatureException e) {
|
|
||||||
Log.e(Constants.TAG, e.getMessage());
|
|
||||||
|
|
||||||
Bundle extras = new Bundle();
|
|
||||||
extras.putString(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
|
|
||||||
getString(R.string.api_error_wrong_signature));
|
|
||||||
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_ERROR_MESSAGE, null,
|
|
||||||
extras);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allowed = false;
|
|
||||||
|
|
||||||
synchronized (userInputLock) {
|
|
||||||
userInputLock.notifyAll();
|
|
||||||
}
|
|
||||||
mThreadPool.resume();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if process that binds to this service (i.e. the package name corresponding to the
|
* Checks if process that binds to this service (i.e. the package name corresponding to the
|
||||||
* process) is in the list of allowed package names.
|
* process) is in the list of allowed package names.
|
||||||
*
|
*
|
||||||
* @param allowOnlySelf
|
* @param allowOnlySelf allow only Keychain app itself
|
||||||
* allow only Keychain app itself
|
|
||||||
* @return true if process is allowed to use this service
|
* @return true if process is allowed to use this service
|
||||||
* @throws WrongPackageSignatureException
|
* @throws WrongPackageSignatureException
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -17,8 +17,15 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.service.remote;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.openintents.openpgp.util.OpenPgpConstants;
|
||||||
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
@ -30,15 +37,7 @@ import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
|
|||||||
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import android.content.Intent;
|
import java.util.ArrayList;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.support.v7.app.ActionBarActivity;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
public class RemoteServiceActivity extends ActionBarActivity {
|
public class RemoteServiceActivity extends ActionBarActivity {
|
||||||
|
|
||||||
@ -64,17 +63,11 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
// error message
|
// error message
|
||||||
public static final String EXTRA_ERROR_MESSAGE = "error_message";
|
public static final String EXTRA_ERROR_MESSAGE = "error_message";
|
||||||
|
|
||||||
private Messenger mMessenger;
|
|
||||||
|
|
||||||
// register view
|
// register view
|
||||||
private AppSettingsFragment mSettingsFragment;
|
private AppSettingsFragment mSettingsFragment;
|
||||||
// select pub keys view
|
// select pub keys view
|
||||||
private SelectPublicKeyFragment mSelectFragment;
|
private SelectPublicKeyFragment mSelectFragment;
|
||||||
|
|
||||||
// has the user clicked one of the buttons
|
|
||||||
// or do we need to handle the callback in onStop()
|
|
||||||
private boolean finishHandled;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -82,36 +75,12 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
handleActions(getIntent(), savedInstanceState);
|
handleActions(getIntent(), savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
|
|
||||||
if (!finishHandled) {
|
|
||||||
Message msg = Message.obtain();
|
|
||||||
msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
|
|
||||||
try {
|
|
||||||
mMessenger.send(msg);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void handleActions(Intent intent, Bundle savedInstanceState) {
|
protected void handleActions(Intent intent, Bundle savedInstanceState) {
|
||||||
finishHandled = false;
|
|
||||||
|
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
Bundle extras = intent.getExtras();
|
final Bundle extras = intent.getExtras();
|
||||||
|
|
||||||
if (extras == null) {
|
|
||||||
extras = new Bundle();
|
|
||||||
}
|
|
||||||
|
|
||||||
mMessenger = extras.getParcelable(EXTRA_MESSENGER);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* com.android.crypto actions
|
|
||||||
*/
|
|
||||||
if (ACTION_REGISTER.equals(action)) {
|
if (ACTION_REGISTER.equals(action)) {
|
||||||
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
|
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
|
||||||
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
|
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
|
||||||
@ -125,44 +94,27 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
// user needs to select a key!
|
// user needs to select a key!
|
||||||
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
|
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
|
||||||
Toast.makeText(RemoteServiceActivity.this,
|
mSettingsFragment.setErrorOnSelectKeyFragment(
|
||||||
R.string.api_register_error_select_key, Toast.LENGTH_LONG)
|
getString(R.string.api_register_error_select_key));
|
||||||
.show();
|
|
||||||
} else {
|
} else {
|
||||||
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
|
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
|
||||||
mSettingsFragment.getAppSettings());
|
mSettingsFragment.getAppSettings());
|
||||||
|
|
||||||
Message msg = Message.obtain();
|
// give params through for new service call
|
||||||
msg.arg1 = RemoteService.RegisterActivityCallback.OKAY;
|
Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||||
Bundle data = new Bundle();
|
|
||||||
data.putString(RemoteService.RegisterActivityCallback.PACKAGE_NAME,
|
|
||||||
packageName);
|
|
||||||
msg.setData(data);
|
|
||||||
try {
|
|
||||||
mMessenger.send(msg);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
finishHandled = true;
|
Intent finishIntent = new Intent();
|
||||||
finish();
|
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, oldParams);
|
||||||
|
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
|
||||||
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, R.string.api_register_disallow, new View.OnClickListener() {
|
}, R.string.api_register_disallow, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// Disallow
|
// Disallow
|
||||||
|
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
|
||||||
Message msg = Message.obtain();
|
RemoteServiceActivity.this.finish();
|
||||||
msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
|
|
||||||
try {
|
|
||||||
mMessenger.send(msg);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
finishHandled = true;
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -176,8 +128,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
mSettingsFragment.setAppSettings(settings);
|
mSettingsFragment.setAppSettings(settings);
|
||||||
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
|
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
|
||||||
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
|
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
|
||||||
|
Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||||
|
|
||||||
showPassphraseDialog(secretKeyId);
|
showPassphraseDialog(oldParams, secretKeyId);
|
||||||
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
|
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
|
||||||
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
|
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
|
||||||
ArrayList<String> missingUserIds = intent
|
ArrayList<String> missingUserIds = intent
|
||||||
@ -185,8 +138,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
ArrayList<String> dublicateUserIds = intent
|
ArrayList<String> dublicateUserIds = intent
|
||||||
.getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS);
|
.getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS);
|
||||||
|
|
||||||
String text = new String();
|
// TODO: do this with spannable instead of HTML to prevent parsing failures with weird user ids
|
||||||
text += "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
|
String text = "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
|
||||||
text += "<br/><br/>";
|
text += "<br/><br/>";
|
||||||
if (missingUserIds != null && missingUserIds.size() > 0) {
|
if (missingUserIds != null && missingUserIds.size() > 0) {
|
||||||
text += getString(R.string.api_select_pub_keys_missing_text);
|
text += getString(R.string.api_select_pub_keys_missing_text);
|
||||||
@ -213,40 +166,22 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// ok
|
// add key ids to params Bundle for new request
|
||||||
|
Bundle params = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
|
||||||
Message msg = Message.obtain();
|
params.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS,
|
||||||
msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.OKAY;
|
|
||||||
Bundle data = new Bundle();
|
|
||||||
data.putLongArray(
|
|
||||||
OpenPgpService.SelectPubKeysActivityCallback.PUB_KEY_IDS,
|
|
||||||
mSelectFragment.getSelectedMasterKeyIds());
|
mSelectFragment.getSelectedMasterKeyIds());
|
||||||
msg.setData(data);
|
|
||||||
try {
|
|
||||||
mMessenger.send(msg);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
finishHandled = true;
|
Intent finishIntent = new Intent();
|
||||||
finish();
|
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
|
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
|
||||||
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
}
|
||||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// cancel
|
// cancel
|
||||||
|
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
|
||||||
Message msg = Message.obtain();
|
RemoteServiceActivity.this.finish();
|
||||||
msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.CANCEL;
|
|
||||||
;
|
|
||||||
try {
|
|
||||||
mMessenger.send(msg);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
finishHandled = true;
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -279,8 +214,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
} else if (ACTION_ERROR_MESSAGE.equals(action)) {
|
} else if (ACTION_ERROR_MESSAGE.equals(action)) {
|
||||||
String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
|
String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
|
||||||
|
|
||||||
String text = new String();
|
String text = "<font color=\"red\">" + errorMessage + "</font>";
|
||||||
text += "<font color=\"red\">" + errorMessage + "</font>";
|
|
||||||
|
|
||||||
// Inflate a "Done" custom action bar view
|
// Inflate a "Done" custom action bar view
|
||||||
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
|
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
|
||||||
@ -288,7 +222,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
finish();
|
RemoteServiceActivity.this.setResult(RESULT_OK);
|
||||||
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -298,7 +233,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
|
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
|
||||||
textView.setHtmlFromString(text);
|
textView.setHtmlFromString(text);
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG, "Wrong action!");
|
Log.e(Constants.TAG, "Action does not exist!");
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -308,31 +244,21 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
|
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
|
||||||
* for a symmetric passphrase
|
* for a symmetric passphrase
|
||||||
*/
|
*/
|
||||||
private void showPassphraseDialog(long secretKeyId) {
|
private void showPassphraseDialog(final Bundle params, long secretKeyId) {
|
||||||
// Message is received after passphrase is cached
|
// Message is received after passphrase is cached
|
||||||
Handler returnHandler = new Handler() {
|
Handler returnHandler = new Handler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
|
||||||
Message msg = Message.obtain();
|
// return given params again, for calling the service method again
|
||||||
msg.arg1 = OpenPgpService.PassphraseActivityCallback.OKAY;
|
Intent finishIntent = new Intent();
|
||||||
try {
|
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
mMessenger.send(msg);
|
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Message msg = Message.obtain();
|
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
|
||||||
msg.arg1 = OpenPgpService.PassphraseActivityCallback.CANCEL;
|
|
||||||
try {
|
|
||||||
mMessenger.send(msg);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(Constants.TAG, "CryptoServiceActivity", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finishHandled = true;
|
RemoteServiceActivity.this.finish();
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -345,9 +271,12 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
|
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
|
Log.d(Constants.TAG, "No passphrase for this secret key, do pgp operation directly!");
|
||||||
// send message to handler to start encryption directly
|
// return given params again, for calling the service method again
|
||||||
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
|
Intent finishIntent = new Intent();
|
||||||
|
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
|
||||||
|
setResult(RESULT_OK, finishIntent);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.sufficientlysecure.keychain.service.exception;
|
package org.sufficientlysecure.keychain.service.remote;
|
||||||
|
|
||||||
public class WrongPackageSignatureException extends Exception {
|
public class WrongPackageSignatureException extends Exception {
|
||||||
|
|
@ -56,7 +56,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
|
|||||||
/**
|
/**
|
||||||
* Signs the specified public key with the specified secret master key
|
* Signs the specified public key with the specified secret master key
|
||||||
*/
|
*/
|
||||||
public class SignKeyActivity extends ActionBarActivity implements
|
public class CertifyKeyActivity extends ActionBarActivity implements
|
||||||
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
|
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
|
||||||
private BootstrapButton mSignButton;
|
private BootstrapButton mSignButton;
|
||||||
private CheckBox mUploadKeyCheckbox;
|
private CheckBox mUploadKeyCheckbox;
|
||||||
@ -72,7 +72,7 @@ public class SignKeyActivity extends ActionBarActivity implements
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.sign_key_activity);
|
setContentView(R.layout.certify_key_activity);
|
||||||
|
|
||||||
final ActionBar actionBar = getSupportActionBar();
|
final ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
@ -164,8 +164,8 @@ public class SignKeyActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
|
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
|
Log.d(Constants.TAG, "No passphrase for this secret key!");
|
||||||
// send message to handler to start encryption directly
|
// send message to handler to start certification directly
|
||||||
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
|
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,8 +196,8 @@ public class SignKeyActivity extends ActionBarActivity implements
|
|||||||
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
|
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
showPassphraseDialog(mMasterKeyId);
|
showPassphraseDialog(mMasterKeyId);
|
||||||
return; // bail out; need to wait until the user has entered the passphrase
|
// bail out; need to wait until the user has entered the passphrase before trying again
|
||||||
// before trying again
|
return;
|
||||||
} else {
|
} else {
|
||||||
startSigning();
|
startSigning();
|
||||||
}
|
}
|
||||||
@ -218,13 +218,13 @@ public class SignKeyActivity extends ActionBarActivity implements
|
|||||||
// Send all information needed to service to sign key in other thread
|
// Send all information needed to service to sign key in other thread
|
||||||
Intent intent = new Intent(this, KeychainIntentService.class);
|
Intent intent = new Intent(this, KeychainIntentService.class);
|
||||||
|
|
||||||
intent.setAction(KeychainIntentService.ACTION_SIGN_KEYRING);
|
intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING);
|
||||||
|
|
||||||
// fill values for this action
|
// fill values for this action
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
|
|
||||||
data.putLong(KeychainIntentService.SIGN_KEY_MASTER_KEY_ID, mMasterKeyId);
|
data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId);
|
||||||
data.putLong(KeychainIntentService.SIGN_KEY_PUB_KEY_ID, mPubKeyId);
|
data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId);
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
@ -237,14 +237,12 @@ public class SignKeyActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
|
|
||||||
Toast.makeText(SignKeyActivity.this, R.string.key_sign_success,
|
Toast.makeText(CertifyKeyActivity.this, R.string.key_sign_success,
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
// check if we need to send the key to the server or not
|
// check if we need to send the key to the server or not
|
||||||
if (mUploadKeyCheckbox.isChecked()) {
|
if (mUploadKeyCheckbox.isChecked()) {
|
||||||
/*
|
// upload the newly signed key to the keyserver
|
||||||
* upload the newly signed key to the key server
|
|
||||||
*/
|
|
||||||
uploadKey();
|
uploadKey();
|
||||||
} else {
|
} else {
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
@ -291,7 +289,7 @@ public class SignKeyActivity extends ActionBarActivity implements
|
|||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
Toast.makeText(SignKeyActivity.this, R.string.key_send_success,
|
Toast.makeText(CertifyKeyActivity.this, R.string.key_send_success,
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
|||||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpOperation;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
|
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
@ -43,7 +43,6 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
|||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.LookupUnknownKeyDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ import android.view.animation.AnimationUtils;
|
|||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.widget.ViewFlipper;
|
import android.widget.ViewFlipper;
|
||||||
@ -78,14 +77,20 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
/* EXTRA keys for input */
|
/* EXTRA keys for input */
|
||||||
public static final String EXTRA_TEXT = "text";
|
public static final String EXTRA_TEXT = "text";
|
||||||
|
|
||||||
|
private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
|
||||||
|
private static final int RESULT_CODE_FILE = 0x00007003;
|
||||||
|
|
||||||
private long mSignatureKeyId = 0;
|
private long mSignatureKeyId = 0;
|
||||||
|
|
||||||
private boolean mReturnResult = false;
|
private boolean mReturnResult = false;
|
||||||
|
|
||||||
|
// TODO: replace signed only checks with something more intelligent
|
||||||
|
// PgpDecryptVerify should handle all automatically!!!
|
||||||
private boolean mSignedOnly = false;
|
private boolean mSignedOnly = false;
|
||||||
private boolean mAssumeSymmetricEncryption = false;
|
private boolean mAssumeSymmetricEncryption = false;
|
||||||
|
|
||||||
private EditText mMessage = null;
|
private EditText mMessage = null;
|
||||||
private LinearLayout mSignatureLayout = null;
|
private RelativeLayout mSignatureLayout = null;
|
||||||
private ImageView mSignatureStatusImage = null;
|
private ImageView mSignatureStatusImage = null;
|
||||||
private TextView mUserId = null;
|
private TextView mUserId = null;
|
||||||
private TextView mUserIdRest = null;
|
private TextView mUserIdRest = null;
|
||||||
@ -100,6 +105,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
private EditText mFilename = null;
|
private EditText mFilename = null;
|
||||||
private CheckBox mDeleteAfter = null;
|
private CheckBox mDeleteAfter = null;
|
||||||
private BootstrapButton mBrowse = null;
|
private BootstrapButton mBrowse = null;
|
||||||
|
private BootstrapButton mLookupKey = null;
|
||||||
|
|
||||||
private String mInputFilename = null;
|
private String mInputFilename = null;
|
||||||
private String mOutputFilename = null;
|
private String mOutputFilename = null;
|
||||||
@ -107,14 +113,10 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
private Uri mContentUri = null;
|
private Uri mContentUri = null;
|
||||||
private boolean mReturnBinary = false;
|
private boolean mReturnBinary = false;
|
||||||
|
|
||||||
private long mUnknownSignatureKeyId = 0;
|
|
||||||
|
|
||||||
private long mSecretKeyId = Id.key.none;
|
private long mSecretKeyId = Id.key.none;
|
||||||
|
|
||||||
private FileDialogFragment mFileDialog;
|
private FileDialogFragment mFileDialog;
|
||||||
|
|
||||||
private boolean mLookupUnknownKey = true;
|
|
||||||
|
|
||||||
private boolean mDecryptImmediately = false;
|
private boolean mDecryptImmediately = false;
|
||||||
|
|
||||||
private BootstrapButton mDecryptButton;
|
private BootstrapButton mDecryptButton;
|
||||||
@ -154,7 +156,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
mSourceLabel.setOnClickListener(nextSourceClickListener);
|
mSourceLabel.setOnClickListener(nextSourceClickListener);
|
||||||
|
|
||||||
mMessage = (EditText) findViewById(R.id.message);
|
mMessage = (EditText) findViewById(R.id.message);
|
||||||
mSignatureLayout = (LinearLayout) findViewById(R.id.signature);
|
mSignatureLayout = (RelativeLayout) findViewById(R.id.signature);
|
||||||
mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status);
|
mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status);
|
||||||
mUserId = (TextView) findViewById(R.id.mainUserId);
|
mUserId = (TextView) findViewById(R.id.mainUserId);
|
||||||
mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
|
mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
|
||||||
@ -171,7 +173,15 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
mBrowse.setOnClickListener(new View.OnClickListener() {
|
mBrowse.setOnClickListener(new View.OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
|
FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
|
||||||
Id.request.filename);
|
RESULT_CODE_FILE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mLookupKey = (BootstrapButton) findViewById(R.id.lookup_key);
|
||||||
|
mLookupKey.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
lookupUnknownKey(mSignatureKeyId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -239,7 +249,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
DecryptActivity.this, mSignatureKeyId);
|
DecryptActivity.this, mSignatureKeyId);
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
Intent intent = new Intent(DecryptActivity.this, ImportKeysActivity.class);
|
Intent intent = new Intent(DecryptActivity.this, ImportKeysActivity.class);
|
||||||
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEY_SERVER);
|
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
|
||||||
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, mSignatureKeyId);
|
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, mSignatureKeyId);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
@ -287,13 +297,13 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
* Android's Action
|
* Android's Action
|
||||||
*/
|
*/
|
||||||
if (Intent.ACTION_SEND.equals(action) && type != null) {
|
if (Intent.ACTION_SEND.equals(action) && type != null) {
|
||||||
// When sending to Keychain Encrypt via share menu
|
// When sending to Keychain Decrypt via share menu
|
||||||
if ("text/plain".equals(type)) {
|
if ("text/plain".equals(type)) {
|
||||||
// Plain text
|
// Plain text
|
||||||
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
if (sharedText != null) {
|
if (sharedText != null) {
|
||||||
// handle like normal text decryption, override action and extras to later
|
// handle like normal text decryption, override action and extras to later
|
||||||
// execute ACTION_DECRYPT in main actions
|
// executeServiceMethod ACTION_DECRYPT in main actions
|
||||||
extras.putString(EXTRA_TEXT, sharedText);
|
extras.putString(EXTRA_TEXT, sharedText);
|
||||||
action = ACTION_DECRYPT;
|
action = ACTION_DECRYPT;
|
||||||
}
|
}
|
||||||
@ -449,7 +459,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
} else {
|
} else {
|
||||||
if (mDecryptTarget == Id.target.file) {
|
if (mDecryptTarget == Id.target.file) {
|
||||||
askForOutputFilename();
|
askForOutputFilename();
|
||||||
} else {
|
} else { // mDecryptTarget == Id.target.message
|
||||||
decryptStart();
|
decryptStart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,7 +549,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
inStream.reset();
|
inStream.reset();
|
||||||
}
|
}
|
||||||
mSecretKeyId = Id.key.symmetric;
|
mSecretKeyId = Id.key.symmetric;
|
||||||
if (!PgpOperation.hasSymmetricEncryption(this, inStream)) {
|
if (!PgpDecryptVerify.hasSymmetricEncryption(this, inStream)) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
getString(R.string.error_no_known_encryption_found));
|
getString(R.string.error_no_known_encryption_found));
|
||||||
}
|
}
|
||||||
@ -587,28 +597,10 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void lookupUnknownKey(long unknownKeyId) {
|
private void lookupUnknownKey(long unknownKeyId) {
|
||||||
// Message is received after passphrase is cached
|
Intent intent = new Intent(this, ImportKeysActivity.class);
|
||||||
Handler returnHandler = new Handler() {
|
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
|
||||||
@Override
|
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId);
|
||||||
public void handleMessage(Message message) {
|
startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY);
|
||||||
if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_OKAY) {
|
|
||||||
// the result is handled by onActivityResult() as LookupUnknownKeyDialogFragment
|
|
||||||
// starts a new Intent which then returns data
|
|
||||||
} else if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_CANCEL) {
|
|
||||||
// decrypt again, but don't lookup unknown keys!
|
|
||||||
mLookupUnknownKey = false;
|
|
||||||
decryptStart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
|
||||||
Messenger messenger = new Messenger(returnHandler);
|
|
||||||
|
|
||||||
LookupUnknownKeyDialogFragment lookupKeyDialog = LookupUnknownKeyDialogFragment
|
|
||||||
.newInstance(messenger, unknownKeyId);
|
|
||||||
|
|
||||||
lookupKeyDialog.show(getSupportFragmentManager(), "unknownKeyDialog");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decryptStart() {
|
private void decryptStart() {
|
||||||
@ -644,8 +636,6 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId);
|
data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId);
|
||||||
|
|
||||||
data.putBoolean(KeychainIntentService.DECRYPT_SIGNED_ONLY, mSignedOnly);
|
|
||||||
data.putBoolean(KeychainIntentService.DECRYPT_LOOKUP_UNKNOWN_KEY, mLookupUnknownKey);
|
|
||||||
data.putBoolean(KeychainIntentService.DECRYPT_RETURN_BYTES, mReturnBinary);
|
data.putBoolean(KeychainIntentService.DECRYPT_RETURN_BYTES, mReturnBinary);
|
||||||
data.putBoolean(KeychainIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption);
|
data.putBoolean(KeychainIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption);
|
||||||
|
|
||||||
@ -662,15 +652,6 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
// get returned data bundle
|
// get returned data bundle
|
||||||
Bundle returnData = message.getData();
|
Bundle returnData = message.getData();
|
||||||
|
|
||||||
// if key is unknown show lookup dialog
|
|
||||||
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY)
|
|
||||||
&& mLookupUnknownKey) {
|
|
||||||
mUnknownSignatureKeyId = returnData
|
|
||||||
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
|
|
||||||
lookupUnknownKey(mUnknownSignatureKeyId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSignatureKeyId = 0;
|
mSignatureKeyId = 0;
|
||||||
mSignatureLayout.setVisibility(View.GONE);
|
mSignatureLayout.setVisibility(View.GONE);
|
||||||
|
|
||||||
@ -727,19 +708,24 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) {
|
if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) {
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
|
||||||
|
mLookupKey.setVisibility(View.GONE);
|
||||||
} else if (returnData
|
} else if (returnData
|
||||||
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
|
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||||
|
mLookupKey.setVisibility(View.VISIBLE);
|
||||||
Toast.makeText(DecryptActivity.this,
|
Toast.makeText(DecryptActivity.this,
|
||||||
R.string.unknown_signature_key_touch_to_look_up,
|
R.string.unknown_signature,
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
|
||||||
|
mLookupKey.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
mSignatureLayout.setVisibility(View.VISIBLE);
|
mSignatureLayout.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -756,7 +742,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case Id.request.filename: {
|
case RESULT_CODE_FILE: {
|
||||||
if (resultCode == RESULT_OK && data != null) {
|
if (resultCode == RESULT_OK && data != null) {
|
||||||
try {
|
try {
|
||||||
String path = FileHelper.getPath(this, data.getData());
|
String path = FileHelper.getPath(this, data.getData());
|
||||||
@ -772,20 +758,21 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
// this request is returned after LookupUnknownKeyDialogFragment started
|
// this request is returned after LookupUnknownKeyDialogFragment started
|
||||||
// ImportKeysActivity and user looked uo key
|
// ImportKeysActivity and user looked uo key
|
||||||
case Id.request.look_up_key_id: {
|
case RESULT_CODE_LOOKUP_KEY: {
|
||||||
Log.d(Constants.TAG, "Returning from Lookup Key...");
|
Log.d(Constants.TAG, "Returning from Lookup Key...");
|
||||||
// decrypt again without lookup
|
if (resultCode == RESULT_OK) {
|
||||||
mLookupUnknownKey = false;
|
// decrypt again
|
||||||
decryptStart();
|
decryptStart();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
if (sharedText != null) {
|
if (sharedText != null) {
|
||||||
// handle like normal text encryption, override action and extras to later
|
// handle like normal text encryption, override action and extras to later
|
||||||
// execute ACTION_ENCRYPT in main actions
|
// executeServiceMethod ACTION_ENCRYPT in main actions
|
||||||
extras.putString(EXTRA_TEXT, sharedText);
|
extras.putString(EXTRA_TEXT, sharedText);
|
||||||
extras.putBoolean(EXTRA_ASCII_ARMOR, true);
|
extras.putBoolean(EXTRA_ASCII_ARMOR, true);
|
||||||
action = ACTION_ENCRYPT;
|
action = ACTION_ENCRYPT;
|
||||||
|
@ -33,18 +33,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
||||||
public class HelpFragmentAbout extends Fragment {
|
public class HelpAboutFragment extends Fragment {
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround for Android Bug. See
|
|
||||||
* http://stackoverflow.com/questions/8748064/starting-activity-from
|
|
||||||
* -fragment-causes-nullpointerexception
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
setUserVisibleHint(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|