merge master
5
.gitignore
vendored
@ -31,3 +31,8 @@ pom.xml.*
|
|||||||
|
|
||||||
#OS Specific
|
#OS Specific
|
||||||
[Tt]humbs.db
|
[Tt]humbs.db
|
||||||
|
|
||||||
|
|
||||||
|
#Lint output
|
||||||
|
OpenPGP-Keychain/lint-report.html
|
||||||
|
OpenPGP-Keychain/lint-report_files/*
|
@ -3,7 +3,7 @@ jdk: oraclejdk7
|
|||||||
before_install:
|
before_install:
|
||||||
# Install base Android SDK
|
# Install base Android SDK
|
||||||
- sudo apt-get update -qq
|
- sudo apt-get update -qq
|
||||||
- if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm ia32-libs; fi
|
- if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm lib32z1 lib32stdc++6; fi
|
||||||
- wget http://dl.google.com/android/android-sdk_r22.3-linux.tgz
|
- wget http://dl.google.com/android/android-sdk_r22.3-linux.tgz
|
||||||
- tar xzf android-sdk_r22.3-linux.tgz
|
- tar xzf android-sdk_r22.3-linux.tgz
|
||||||
- export ANDROID_HOME=$PWD/android-sdk-linux
|
- export ANDROID_HOME=$PWD/android-sdk-linux
|
||||||
|
16
CHANGELOG
@ -1,3 +1,19 @@
|
|||||||
|
2.4
|
||||||
|
Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
|
||||||
|
Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
|
||||||
|
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.
|
||||||
|
* new unified key list
|
||||||
|
* colorized key fingerprint
|
||||||
|
* support for keyserver ports
|
||||||
|
* deactivate possibility to generate weak keys
|
||||||
|
* much more internal work on the API
|
||||||
|
* certify user ids
|
||||||
|
* keyserver query based on machine-readable output
|
||||||
|
* lock navigation drawer on tablets
|
||||||
|
* suggestions for emails on creation of keys
|
||||||
|
* search in public key lists
|
||||||
|
* and much more improvements and fixes…
|
||||||
|
|
||||||
2.3.1
|
2.3.1
|
||||||
* hotfix for crash when upgrading from old versions
|
* hotfix for crash when upgrading from old versions
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ apply plugin: 'android'
|
|||||||
|
|
||||||
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')
|
compile project(':libraries:openpgp-api-library')
|
||||||
|
compile project(':libraries:openkeychain-api-library')
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -48,6 +48,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
private Button mEncrypt;
|
private Button mEncrypt;
|
||||||
private Button mSignAndEncrypt;
|
private Button mSignAndEncrypt;
|
||||||
private Button mDecryptAndVerify;
|
private Button mDecryptAndVerify;
|
||||||
|
private EditText mAccount;
|
||||||
|
|
||||||
private OpenPgpServiceConnection mServiceConnection;
|
private OpenPgpServiceConnection mServiceConnection;
|
||||||
|
|
||||||
@ -68,6 +69,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt);
|
mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt);
|
||||||
mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt);
|
mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt);
|
||||||
mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify);
|
mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify);
|
||||||
|
mAccount = (EditText) findViewById(R.id.crypto_provider_demo_account);
|
||||||
|
|
||||||
mSign.setOnClickListener(new View.OnClickListener() {
|
mSign.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -142,7 +144,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
private InputStream getInputstream(boolean ciphertext) {
|
private InputStream getInputstream(boolean ciphertext) {
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
try {
|
try {
|
||||||
String inputStr = null;
|
String inputStr;
|
||||||
if (ciphertext) {
|
if (ciphertext) {
|
||||||
inputStr = mCiphertext.getText().toString();
|
inputStr = mCiphertext.getText().toString();
|
||||||
} else {
|
} else {
|
||||||
@ -213,6 +215,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
public void sign(Intent data) {
|
public void sign(Intent data) {
|
||||||
data.setAction(OpenPgpApi.ACTION_SIGN);
|
data.setAction(OpenPgpApi.ACTION_SIGN);
|
||||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||||
|
|
||||||
InputStream is = getInputstream(false);
|
InputStream is = getInputstream(false);
|
||||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
@ -225,6 +228,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
data.setAction(OpenPgpApi.ACTION_ENCRYPT);
|
data.setAction(OpenPgpApi.ACTION_ENCRYPT);
|
||||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||||
|
|
||||||
InputStream is = getInputstream(false);
|
InputStream is = getInputstream(false);
|
||||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
@ -237,6 +241,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
|
data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT);
|
||||||
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(","));
|
||||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||||
|
|
||||||
InputStream is = getInputstream(false);
|
InputStream is = getInputstream(false);
|
||||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
@ -248,6 +253,7 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
public void decryptAndVerify(Intent data) {
|
public void decryptAndVerify(Intent data) {
|
||||||
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||||
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString());
|
||||||
|
|
||||||
InputStream is = getInputstream(true);
|
InputStream is = getInputstream(true);
|
||||||
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
final ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
@ -264,13 +270,11 @@ public class OpenPgpProviderActivity extends Activity {
|
|||||||
// try again after user interaction
|
// try again after user interaction
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
/*
|
/*
|
||||||
* The data originally given to the pgp method are are again
|
* The data originally given to one of the methods above, is again
|
||||||
* returned here to be used when calling again after user interaction.
|
* returned here to be used when calling the method again after user
|
||||||
*
|
* interaction. The Intent now also contains results from the user
|
||||||
* They also contain results from the user interaction which happened,
|
* interaction, for example selected key ids.
|
||||||
* for example selected key ids.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case REQUEST_CODE_SIGN: {
|
case REQUEST_CODE_SIGN: {
|
||||||
sign(data);
|
sign(data);
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
android:scrollHorizontally="true"
|
android:scrollHorizontally="true"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
android:text="message"
|
android:text="message"
|
||||||
|
android:hint="cleartext message"
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
@ -66,6 +67,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:text="ciphertext"
|
android:text="ciphertext"
|
||||||
|
android:hint="ciphertext"
|
||||||
android:textAppearance="@android:style/TextAppearance.Small" />
|
android:textAppearance="@android:style/TextAppearance.Small" />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
@ -104,5 +106,18 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Decrypt and Verify" />
|
android:text="Decrypt and Verify" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Account ID:"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:id="@+id/textView" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Alice <alice@example.com>"
|
||||||
|
android:id="@+id/crypto_provider_demo_account" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
29
OpenPGP-Keychain-API/libraries/openpgp-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.openintents.openpgp"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0" >
|
||||||
|
|
||||||
|
<uses-sdk
|
||||||
|
android:minSdkVersion="9"
|
||||||
|
android:targetSdkVersion="19" />
|
||||||
|
|
||||||
|
<application/>
|
||||||
|
|
||||||
|
</manifest>
|
202
OpenPGP-Keychain-API/libraries/openpgp-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.
|
@ -5,7 +5,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:0.8.3'
|
classpath 'com.android.tools.build:gradle:0.9.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
92
OpenPGP-Keychain-API/libraries/openpgp-api-library/build.xml
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="keychain-api-library" default="help">
|
||||||
|
|
||||||
|
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||||
|
It contains the path to the SDK. It should *NOT* be checked into
|
||||||
|
Version Control Systems. -->
|
||||||
|
<property file="local.properties" />
|
||||||
|
|
||||||
|
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||||
|
'android' tool to add properties to it.
|
||||||
|
This is the place to change some Ant specific build properties.
|
||||||
|
Here are some properties you may want to change/update:
|
||||||
|
|
||||||
|
source.dir
|
||||||
|
The name of the source directory. Default is 'src'.
|
||||||
|
out.dir
|
||||||
|
The name of the output directory. Default is 'bin'.
|
||||||
|
|
||||||
|
For other overridable properties, look at the beginning of the rules
|
||||||
|
files in the SDK, at tools/ant/build.xml
|
||||||
|
|
||||||
|
Properties related to the SDK location or the project target should
|
||||||
|
be updated using the 'android' tool with the 'update' action.
|
||||||
|
|
||||||
|
This file is an integral part of the build system for your
|
||||||
|
application and should be checked into Version Control Systems.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<property file="ant.properties" />
|
||||||
|
|
||||||
|
<!-- if sdk.dir was not set from one of the property file, then
|
||||||
|
get it from the ANDROID_HOME env var.
|
||||||
|
This must be done before we load project.properties since
|
||||||
|
the proguard config can use sdk.dir -->
|
||||||
|
<property environment="env" />
|
||||||
|
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||||
|
<isset property="env.ANDROID_HOME" />
|
||||||
|
</condition>
|
||||||
|
|
||||||
|
<!-- The project.properties file is created and updated by the 'android'
|
||||||
|
tool, as well as ADT.
|
||||||
|
|
||||||
|
This contains project specific properties such as project target, and library
|
||||||
|
dependencies. Lower level build properties are stored in ant.properties
|
||||||
|
(or in .classpath for Eclipse projects).
|
||||||
|
|
||||||
|
This file is an integral part of the build system for your
|
||||||
|
application and should be checked into Version Control Systems. -->
|
||||||
|
<loadproperties srcFile="project.properties" />
|
||||||
|
|
||||||
|
<!-- quick check on sdk.dir -->
|
||||||
|
<fail
|
||||||
|
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||||
|
unless="sdk.dir"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Import per project custom build rules if present at the root of the project.
|
||||||
|
This is the place to put custom intermediary targets such as:
|
||||||
|
-pre-build
|
||||||
|
-pre-compile
|
||||||
|
-post-compile (This is typically used for code obfuscation.
|
||||||
|
Compiled code location: ${out.classes.absolute.dir}
|
||||||
|
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||||
|
-post-package
|
||||||
|
-post-build
|
||||||
|
-pre-clean
|
||||||
|
-->
|
||||||
|
<import file="custom_rules.xml" optional="true" />
|
||||||
|
|
||||||
|
<!-- Import the actual build file.
|
||||||
|
|
||||||
|
To customize existing targets, there are two options:
|
||||||
|
- Customize only one target:
|
||||||
|
- copy/paste the target into this file, *before* the
|
||||||
|
<import> task.
|
||||||
|
- customize it to your needs.
|
||||||
|
- Customize the whole content of build.xml
|
||||||
|
- copy/paste the content of the rules files (minus the top node)
|
||||||
|
into this file, replacing the <import> task.
|
||||||
|
- customize to your needs.
|
||||||
|
|
||||||
|
***********************
|
||||||
|
****** IMPORTANT ******
|
||||||
|
***********************
|
||||||
|
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||||
|
in order to avoid having your file be overridden by tools such as "android update project"
|
||||||
|
-->
|
||||||
|
<!-- version-tag: 1 -->
|
||||||
|
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,20 @@
|
|||||||
|
# To enable ProGuard in your project, edit project.properties
|
||||||
|
# to define the proguard.config property as described in that file.
|
||||||
|
#
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the ProGuard
|
||||||
|
# include property in project.properties.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
@ -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
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@ -126,6 +126,8 @@ public class OpenPgpApi {
|
|||||||
/* Intent extras */
|
/* Intent extras */
|
||||||
public static final String EXTRA_API_VERSION = "api_version";
|
public static final String EXTRA_API_VERSION = "api_version";
|
||||||
|
|
||||||
|
public static final String EXTRA_ACCOUNT_NAME = "account_name";
|
||||||
|
|
||||||
// SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
|
// SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY
|
||||||
// request ASCII Armor for output
|
// request ASCII Armor for output
|
||||||
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
|
// OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53)
|
@ -31,7 +31,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.ListAdapter;
|
import android.widget.ListAdapter;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import org.sufficientlysecure.keychain.api.R;
|
import org.openintents.openpgp.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
@ -1,2 +1,3 @@
|
|||||||
include ':example-app'
|
include ':example-app'
|
||||||
include ':libraries:keychain-api-library'
|
include ':libraries:openpgp-api-library'
|
||||||
|
include ':libraries:openkeychain-api-library'
|
@ -3,7 +3,8 @@ 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(':OpenPGP-Keychain-API:libraries:keychain-api-library')
|
compile project(':OpenPGP-Keychain-API:libraries:openpgp-api-library')
|
||||||
|
compile project(':OpenPGP-Keychain-API:libraries:openkeychain-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')
|
||||||
@ -56,5 +57,7 @@ android {
|
|||||||
// Do not abort build if lint finds errors
|
// Do not abort build if lint finds errors
|
||||||
lintOptions {
|
lintOptions {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
|
htmlReport true
|
||||||
|
htmlOutput file("lint-report.html")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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="23103"
|
android:versionCode="24000"
|
||||||
android:versionName="2.3.1 beta3">
|
android:versionName="2.4">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
General remarks
|
General remarks
|
||||||
@ -50,6 +50,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
|
|
||||||
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
|
||||||
<application
|
<application
|
||||||
@ -60,7 +61,7 @@
|
|||||||
android:theme="@style/KeychainTheme"
|
android:theme="@style/KeychainTheme"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.KeyListPublicActivity"
|
android:name=".ui.KeyListActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
@ -70,12 +71,6 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
|
||||||
android:name=".ui.KeyListSecretActivity"
|
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
|
||||||
android:label="@string/title_manage_secret_keys"
|
|
||||||
android:launchMode="singleTop">
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.EditKeyActivity"
|
android:name=".ui.EditKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
@ -85,32 +80,30 @@
|
|||||||
android:name=".ui.ViewKeyActivity"
|
android:name=".ui.ViewKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_key_details"
|
android:label="@string/title_key_details"
|
||||||
android:parentActivityName=".ui.KeyListPublicActivity">
|
android:parentActivityName=".ui.KeyListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".ui.KeyListPublicActivity" />
|
android:value=".ui.KeyListActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ViewKeyActivityJB"
|
android:name=".ui.ViewKeyActivityJB"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_key_details"
|
android:label="@string/title_key_details"
|
||||||
android:parentActivityName=".ui.KeyListPublicActivity">
|
android:parentActivityName=".ui.KeyListActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".ui.KeyListPublicActivity" />
|
android:value=".ui.KeyListActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SelectPublicKeyActivity"
|
android:name=".ui.SelectPublicKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_select_recipients"
|
android:label="@string/title_select_recipients"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop"></activity>
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SelectSecretKeyActivity"
|
android:name=".ui.SelectSecretKeyActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_select_secret_key"
|
android:label="@string/title_select_secret_key"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop"></activity>
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.EncryptActivity"
|
android:name=".ui.EncryptActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
@ -149,23 +142,23 @@
|
|||||||
|
|
||||||
<!--<!– VIEW with mimeType: TODO (from email app) –>-->
|
<!--<!– VIEW with mimeType: TODO (from email app) –>-->
|
||||||
<!--<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" />-->
|
||||||
|
|
||||||
<!--<category android:name="android.intent.category.BROWSABLE" />-->
|
<!--<category android:name="android.intent.category.BROWSABLE" />-->
|
||||||
<!--<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 –>-->
|
<!--<!– mime type as defined in http://tools.ietf.org/html/rfc3156 –>-->
|
||||||
<!--<data android:mimeType="application/pgp-signature" />-->
|
<!--<data android:mimeType="application/pgp-signature" />-->
|
||||||
<!--</intent-filter>-->
|
<!--</intent-filter>-->
|
||||||
<!--<!– VIEW with mimeType: TODO (from email app) –>-->
|
<!--<!– VIEW with mimeType: TODO (from email app) –>-->
|
||||||
<!--<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" />-->
|
||||||
|
|
||||||
<!--<category android:name="android.intent.category.BROWSABLE" />-->
|
<!--<category android:name="android.intent.category.BROWSABLE" />-->
|
||||||
<!--<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 –>-->
|
<!--<!– mime type as defined in http://tools.ietf.org/html/rfc3156 –>-->
|
||||||
<!--<data android:mimeType="application/pgp-encrypted" />-->
|
<!--<data android:mimeType="application/pgp-encrypted" />-->
|
||||||
<!--</intent-filter>-->
|
<!--</intent-filter>-->
|
||||||
<!-- Keychain's own Actions -->
|
<!-- Keychain's own Actions -->
|
||||||
<!-- DECRYPT with text as extra -->
|
<!-- DECRYPT with text as extra -->
|
||||||
@ -241,7 +234,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.PreferencesActivity"
|
android:name=".ui.PreferencesActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:label="@string/title_preferences" >
|
android:label="@string/title_preferences">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.sufficientlysecure.keychain.ui.PREFS_GEN" />
|
<action android:name="org.sufficientlysecure.keychain.ui.PREFS_GEN" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
@ -379,44 +372,49 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:process=":passphrase_cache" />
|
android:process=":passphrase_cache" />
|
||||||
<service
|
<service
|
||||||
android:name="org.sufficientlysecure.keychain.service.KeychainIntentService"
|
android:name=".service.KeychainIntentService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="org.sufficientlysecure.keychain.provider.KeychainProvider"
|
android:name=".provider.KeychainProvider"
|
||||||
android:authorities="org.sufficientlysecure.keychain.provider"
|
android:authorities="org.sufficientlysecure.keychain.provider"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- Internal classes of the remote APIs (not exported) -->
|
<!-- Internal classes of the remote APIs (not exported) -->
|
||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
|
android:name=".remote.ui.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" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
|
android:name=".remote.ui.AppsListActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/title_api_registered_apps" />
|
android:label="@string/title_api_registered_apps" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsActivity"
|
android:name=".remote.ui.AppSettingsActivity"
|
||||||
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
|
android:exported="false">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".remote.ui.AppsListActivity" />
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".remote.ui.AccountSettingsActivity"
|
||||||
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- OpenPGP Remote API -->
|
<!-- OpenPGP Remote API -->
|
||||||
<service
|
<service
|
||||||
android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpService"
|
android:name=".remote.OpenPgpService"
|
||||||
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.openintents.openpgp.IOpenPgpService" />
|
<action android:name="org.openintents.openpgp.IOpenPgpService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="api_version"
|
|
||||||
android:value="1" />
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Extended Remote API -->
|
<!-- Extended Remote API -->
|
||||||
|
@ -16,10 +16,15 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain;
|
package org.sufficientlysecure.keychain;
|
||||||
|
|
||||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
|
||||||
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
||||||
|
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.sufficientlysecure.keychain.remote.ui.AppsListActivity;
|
||||||
|
import org.sufficientlysecure.keychain.ui.DecryptActivity;
|
||||||
|
import org.sufficientlysecure.keychain.ui.EncryptActivity;
|
||||||
|
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
|
||||||
|
import org.sufficientlysecure.keychain.ui.KeyListActivity;
|
||||||
|
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
|
|
||||||
public static final boolean DEBUG = BuildConfig.DEBUG;
|
public static final boolean DEBUG = BuildConfig.DEBUG;
|
||||||
@ -40,12 +45,14 @@ public final class Constants {
|
|||||||
|
|
||||||
public static final String INTENT_PREFIX = PACKAGE_NAME + ".action.";
|
public static final String INTENT_PREFIX = PACKAGE_NAME + ".action.";
|
||||||
|
|
||||||
public static final class path {
|
public static final class Path {
|
||||||
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
public static final String APP_DIR = Environment.getExternalStorageDirectory()
|
||||||
+ "/OpenPGP-Keychain";
|
+ "/OpenPGP-Keychain";
|
||||||
|
public static final String APP_DIR_FILE_SEC = APP_DIR + "/secexport.asc";
|
||||||
|
public static final String APP_DIR_FILE_PUB = APP_DIR + "/pubexport.asc";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class pref {
|
public static final class Pref {
|
||||||
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm";
|
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm";
|
||||||
public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm";
|
public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm";
|
||||||
public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
|
public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
|
||||||
@ -57,8 +64,17 @@ public final class Constants {
|
|||||||
public static final String KEY_SERVERS = "keyServers";
|
public static final String KEY_SERVERS = "keyServers";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class defaults {
|
public static final class Defaults {
|
||||||
public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
|
public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class DrawerItems {
|
||||||
|
public static final Class KEY_LIST = KeyListActivity.class;
|
||||||
|
public static final Class ENCRYPT = EncryptActivity.class;
|
||||||
|
public static final Class DECRYPT = DecryptActivity.class;
|
||||||
|
public static final Class IMPORT_KEYS = ImportKeysActivity.class;
|
||||||
|
public static final Class REGISTERED_APPS_LIST = AppsListActivity.class;
|
||||||
|
public static final Class[] ARRAY = new Class[]{KEY_LIST, ENCRYPT, DECRYPT,
|
||||||
|
IMPORT_KEYS, REGISTERED_APPS_LIST};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,7 @@ public final class Id {
|
|||||||
public static final int secret_key = 0x21070002;
|
public static final int secret_key = 0x21070002;
|
||||||
public static final int user_id = 0x21070003;
|
public static final int user_id = 0x21070003;
|
||||||
public static final int key = 0x21070004;
|
public static final int key = 0x21070004;
|
||||||
|
public static final int public_secret_key = 0x21070005;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class choice {
|
public static final class choice {
|
||||||
|
@ -64,7 +64,7 @@ public class KeychainApplication extends Application {
|
|||||||
|
|
||||||
// Create APG directory on sdcard if not existing
|
// Create APG directory on sdcard if not existing
|
||||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
File dir = new File(Constants.path.APP_DIR);
|
File dir = new File(Constants.Path.APP_DIR);
|
||||||
if (!dir.exists() && !dir.mkdirs()) {
|
if (!dir.exists() && !dir.mkdirs()) {
|
||||||
// ignore this for now, it's not crucial
|
// ignore this for now, it's not crucial
|
||||||
// that the directory doesn't exist at this point
|
// that the directory doesn't exist at this point
|
||||||
|
@ -59,7 +59,6 @@ public class ClipboardReflection {
|
|||||||
* Wrapper around ClipboardManager based on Android version using Reflection API
|
* Wrapper around ClipboardManager based on Android version using Reflection API
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param text
|
|
||||||
*/
|
*/
|
||||||
public static CharSequence getClipboardText(Context context) {
|
public static CharSequence getClipboardText(Context context) {
|
||||||
Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
|
Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
@ -37,8 +37,9 @@ import android.os.Handler;
|
|||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public class DialogFragmentWorkaround {
|
public class DialogFragmentWorkaround {
|
||||||
public static final SDKLevel17Interface INTERFACE = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ? new SDKLevel17Impl()
|
public static final SDKLevel17Interface INTERFACE =
|
||||||
: new SDKLevelPriorLevel17Impl());
|
((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ? new SDKLevel17Impl()
|
||||||
|
: new SDKLevelPriorLevel17Impl());
|
||||||
|
|
||||||
private static final int RUNNABLE_DELAY = 300;
|
private static final int RUNNABLE_DELAY = 300;
|
||||||
|
|
||||||
|
@ -17,18 +17,17 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.helper;
|
package org.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public class ActionBarHelper {
|
public class ActionBarHelper {
|
||||||
|
|
||||||
@ -38,7 +37,6 @@ public class ActionBarHelper {
|
|||||||
* @param activity
|
* @param activity
|
||||||
*/
|
*/
|
||||||
public static void setBackButton(ActionBarActivity activity) {
|
public static void setBackButton(ActionBarActivity activity) {
|
||||||
// set actionbar without home button if called from another app
|
|
||||||
final ActionBar actionBar = activity.getSupportActionBar();
|
final ActionBar actionBar = activity.getSupportActionBar();
|
||||||
Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
|
Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
|
||||||
+ activity.getCallingPackage());
|
+ activity.getCallingPackage());
|
||||||
@ -56,28 +54,33 @@ public class ActionBarHelper {
|
|||||||
* Sets custom view on ActionBar for Done/Cancel activities
|
* Sets custom view on ActionBar for Done/Cancel activities
|
||||||
*
|
*
|
||||||
* @param actionBar
|
* @param actionBar
|
||||||
* @param doneText
|
* @param firstText
|
||||||
* @param doneOnClickListener
|
* @param firstDrawableId
|
||||||
* @param cancelText
|
* @param firstOnClickListener
|
||||||
* @param cancelOnClickListener
|
* @param secondText
|
||||||
|
* @param secondDrawableId
|
||||||
|
* @param secondOnClickListener
|
||||||
*/
|
*/
|
||||||
public static void setDoneCancelView(ActionBar actionBar, int doneText,
|
public static void setTwoButtonView(ActionBar actionBar,
|
||||||
OnClickListener doneOnClickListener, int cancelText,
|
int firstText, int firstDrawableId, OnClickListener firstOnClickListener,
|
||||||
OnClickListener cancelOnClickListener) {
|
int secondText, int secondDrawableId, OnClickListener secondOnClickListener) {
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate the custom action bar view
|
||||||
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
||||||
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||||
final View customActionBarView = inflater.inflate(
|
final View customActionBarView = inflater.inflate(
|
||||||
R.layout.actionbar_custom_view_done_cancel, null);
|
R.layout.actionbar_custom_view_done_cancel, null);
|
||||||
|
|
||||||
((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
|
TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
|
||||||
|
firstTextView.setText(firstText);
|
||||||
|
firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
|
||||||
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
||||||
doneOnClickListener);
|
firstOnClickListener);
|
||||||
((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text))
|
TextView secondTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_cancel_text));
|
||||||
.setText(cancelText);
|
secondTextView.setText(secondText);
|
||||||
|
secondTextView.setCompoundDrawablesWithIntrinsicBounds(secondDrawableId, 0, 0, 0);
|
||||||
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
|
customActionBarView.findViewById(R.id.actionbar_cancel).setOnClickListener(
|
||||||
cancelOnClickListener);
|
secondOnClickListener);
|
||||||
|
|
||||||
// Show the custom action bar view and hide the normal Home icon and title.
|
// Show the custom action bar view and hide the normal Home icon and title.
|
||||||
actionBar.setDisplayShowTitleEnabled(false);
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
@ -91,20 +94,22 @@ public class ActionBarHelper {
|
|||||||
* Sets custom view on ActionBar for Done activities
|
* Sets custom view on ActionBar for Done activities
|
||||||
*
|
*
|
||||||
* @param actionBar
|
* @param actionBar
|
||||||
* @param doneText
|
* @param firstText
|
||||||
* @param doneOnClickListener
|
* @param firstOnClickListener
|
||||||
*/
|
*/
|
||||||
public static void setDoneView(ActionBar actionBar, int doneText,
|
public static void setOneButtonView(ActionBar actionBar, int firstText, int firstDrawableId,
|
||||||
OnClickListener doneOnClickListener) {
|
OnClickListener firstOnClickListener) {
|
||||||
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
|
// Inflate a "Done" custom action bar view to serve as the "Up" affordance.
|
||||||
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
final LayoutInflater inflater = (LayoutInflater) actionBar.getThemedContext()
|
||||||
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
||||||
final View customActionBarView = inflater
|
final View customActionBarView = inflater
|
||||||
.inflate(R.layout.actionbar_custom_view_done, null);
|
.inflate(R.layout.actionbar_custom_view_done, null);
|
||||||
|
|
||||||
((TextView) customActionBarView.findViewById(R.id.actionbar_done_text)).setText(doneText);
|
TextView firstTextView = ((TextView) customActionBarView.findViewById(R.id.actionbar_done_text));
|
||||||
|
firstTextView.setText(firstText);
|
||||||
|
firstTextView.setCompoundDrawablesWithIntrinsicBounds(firstDrawableId, 0, 0, 0);
|
||||||
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(
|
||||||
doneOnClickListener);
|
firstOnClickListener);
|
||||||
|
|
||||||
// Show the custom action bar view and hide the normal Home icon and title.
|
// Show the custom action bar view and hide the normal Home icon and title.
|
||||||
actionBar.setDisplayShowTitleEnabled(false);
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
@ -112,5 +117,4 @@ public class ActionBarHelper {
|
|||||||
actionBar.setDisplayShowCustomEnabled(true);
|
actionBar.setDisplayShowCustomEnabled(true);
|
||||||
actionBar.setCustomView(customActionBarView);
|
actionBar.setCustomView(customActionBarView);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.helper;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Patterns;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ContactHelper {
|
||||||
|
|
||||||
|
public static final List<String> getMailAccounts(Context context) {
|
||||||
|
final Account[] accounts = AccountManager.get(context).getAccounts();
|
||||||
|
final Set<String> emailSet = new HashSet<String>();
|
||||||
|
for (Account account : accounts) {
|
||||||
|
if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
|
||||||
|
emailSet.add(account.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<String>(emailSet);
|
||||||
|
}
|
||||||
|
}
|
@ -16,18 +16,8 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.helper;
|
package org.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -36,35 +26,50 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class ExportHelper {
|
public class ExportHelper {
|
||||||
protected FileDialogFragment mFileDialog;
|
protected FileDialogFragment mFileDialog;
|
||||||
protected String mExportFilename;
|
protected String mExportFilename;
|
||||||
|
|
||||||
ActionBarActivity activity;
|
ActionBarActivity mActivity;
|
||||||
|
|
||||||
public ExportHelper(ActionBarActivity activity) {
|
public ExportHelper(ActionBarActivity activity) {
|
||||||
super();
|
super();
|
||||||
this.activity = activity;
|
this.mActivity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
|
public void deleteKey(Uri dataUri, Handler deleteHandler) {
|
||||||
long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
|
long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
Messenger messenger = new Messenger(deleteHandler);
|
Messenger messenger = new Messenger(deleteHandler);
|
||||||
|
|
||||||
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
||||||
new long[] { keyRingRowId }, keyType);
|
new long[]{keyRingRowId});
|
||||||
|
|
||||||
deleteKeyDialog.show(activity.getSupportFragmentManager(), "deleteKeyDialog");
|
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show dialog where to export keys
|
* Show dialog where to export keys
|
||||||
*/
|
*/
|
||||||
public void showExportKeysDialog(final Uri dataUri, final int keyType,
|
public void showExportKeysDialog(final long[] masterKeyIds, final int keyType,
|
||||||
final String exportFilename) {
|
final String exportFilename, final String checkboxString) {
|
||||||
mExportFilename = exportFilename;
|
mExportFilename = exportFilename;
|
||||||
|
|
||||||
// Message is received after file is selected
|
// Message is received after file is selected
|
||||||
@ -73,9 +78,14 @@ public class ExportHelper {
|
|||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
|
if (message.what == FileDialogFragment.MESSAGE_OKAY) {
|
||||||
Bundle data = message.getData();
|
Bundle data = message.getData();
|
||||||
|
int type = keyType;
|
||||||
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
|
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
|
||||||
|
|
||||||
exportKeys(dataUri, keyType);
|
if( data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED) ) {
|
||||||
|
type = Id.type.public_secret_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportKeys(masterKeyIds, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -86,25 +96,20 @@ public class ExportHelper {
|
|||||||
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
String title = null;
|
String title = null;
|
||||||
if (dataUri == null) {
|
if (masterKeyIds == null) {
|
||||||
// export all keys
|
// export all keys
|
||||||
title = activity.getString(R.string.title_export_keys);
|
title = mActivity.getString(R.string.title_export_keys);
|
||||||
} else {
|
} else {
|
||||||
// export only key specified at data uri
|
// export only key specified at data uri
|
||||||
title = activity.getString(R.string.title_export_key);
|
title = mActivity.getString(R.string.title_export_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
String message = null;
|
String message = mActivity.getString(R.string.specify_file_to_export_to);
|
||||||
if (keyType == Id.type.public_key) {
|
|
||||||
message = activity.getString(R.string.specify_file_to_export_to);
|
|
||||||
} else {
|
|
||||||
message = activity.getString(R.string.specify_file_to_export_secret_keys_to);
|
|
||||||
}
|
|
||||||
|
|
||||||
mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
|
mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
|
||||||
exportFilename, null);
|
exportFilename, checkboxString);
|
||||||
|
|
||||||
mFileDialog.show(activity.getSupportFragmentManager(), "fileDialog");
|
mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -112,11 +117,11 @@ public class ExportHelper {
|
|||||||
/**
|
/**
|
||||||
* Export keys
|
* Export keys
|
||||||
*/
|
*/
|
||||||
public void exportKeys(Uri dataUri, int keyType) {
|
public void exportKeys(long[] masterKeyIds, int keyType) {
|
||||||
Log.d(Constants.TAG, "exportKeys started");
|
Log.d(Constants.TAG, "exportKeys started");
|
||||||
|
|
||||||
// Send all information needed to service to export key in other thread
|
// Send all information needed to service to export key in other thread
|
||||||
Intent intent = new Intent(activity, KeychainIntentService.class);
|
final Intent intent = new Intent(mActivity, KeychainIntentService.class);
|
||||||
|
|
||||||
intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
|
intent.setAction(KeychainIntentService.ACTION_EXPORT_KEYRING);
|
||||||
|
|
||||||
@ -126,22 +131,27 @@ public class ExportHelper {
|
|||||||
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
|
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
|
||||||
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
|
data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
|
||||||
|
|
||||||
if (dataUri == null) {
|
if (masterKeyIds == null) {
|
||||||
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
|
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
|
||||||
} else {
|
} else {
|
||||||
// TODO: put data uri into service???
|
data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
|
||||||
long keyRingMasterKeyId = ProviderHelper.getMasterKeyId(activity, dataUri);
|
|
||||||
|
|
||||||
data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after exporting is done in ApgService
|
// Message is received after exporting is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(activity,
|
KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(mActivity,
|
||||||
R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
|
mActivity.getString(R.string.progress_exporting),
|
||||||
|
ProgressDialog.STYLE_HORIZONTAL,
|
||||||
|
true,
|
||||||
|
new DialogInterface.OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel(DialogInterface dialogInterface) {
|
||||||
|
mActivity.stopService(intent);
|
||||||
|
}
|
||||||
|
}) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -151,16 +161,16 @@ public class ExportHelper {
|
|||||||
int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
|
int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT);
|
||||||
String toastMessage;
|
String toastMessage;
|
||||||
if (exported == 1) {
|
if (exported == 1) {
|
||||||
toastMessage = activity.getString(R.string.key_exported);
|
toastMessage = mActivity.getString(R.string.key_exported);
|
||||||
} else if (exported > 0) {
|
} else if (exported > 0) {
|
||||||
toastMessage = activity.getString(R.string.keys_exported, exported);
|
toastMessage = mActivity.getString(R.string.keys_exported, exported);
|
||||||
} else {
|
} else {
|
||||||
toastMessage = activity.getString(R.string.no_keys_exported);
|
toastMessage = mActivity.getString(R.string.no_keys_exported);
|
||||||
}
|
}
|
||||||
Toast.makeText(activity, toastMessage, Toast.LENGTH_SHORT).show();
|
Toast.makeText(mActivity, toastMessage, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -168,10 +178,10 @@ public class ExportHelper {
|
|||||||
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
|
intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
|
||||||
|
|
||||||
// show progress dialog
|
// show progress dialog
|
||||||
exportHandler.showProgressDialog(activity);
|
exportHandler.showProgressDialog(mActivity);
|
||||||
|
|
||||||
// start service with intent
|
// start service with intent
|
||||||
activity.startService(intent);
|
mActivity.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.helper;
|
package org.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -30,6 +26,9 @@ import android.net.Uri;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public class FileHelper {
|
public class FileHelper {
|
||||||
|
|
||||||
@ -54,13 +53,10 @@ public class FileHelper {
|
|||||||
* installed.
|
* installed.
|
||||||
*
|
*
|
||||||
* @param activity
|
* @param activity
|
||||||
* @param filename
|
* @param filename default selected file, not supported by all file managers
|
||||||
* default selected file, not supported by all file managers
|
* @param mimeType can be text/plain for example
|
||||||
* @param mimeType
|
* @param requestCode requestCode used to identify the result coming back from file manager to
|
||||||
* can be text/plain for example
|
* onActivityResult() in your activity
|
||||||
* @param requestCode
|
|
||||||
* requestCode used to identify the result coming back from file manager to
|
|
||||||
* onActivityResult() in your activity
|
|
||||||
*/
|
*/
|
||||||
public static void openFile(Activity activity, String filename, String mimeType, int requestCode) {
|
public static void openFile(Activity activity, String filename, String mimeType, int requestCode) {
|
||||||
Intent intent = buildFileIntent(filename, mimeType);
|
Intent intent = buildFileIntent(filename, mimeType);
|
||||||
@ -97,14 +93,13 @@ public class FileHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a file path from a Uri.
|
* Get a file path from a Uri.
|
||||||
*
|
* <p/>
|
||||||
* from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/
|
* from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/
|
||||||
* afilechooser/utils/FileUtils.java
|
* afilechooser/utils/FileUtils.java
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param uri
|
* @param uri
|
||||||
* @return
|
* @return
|
||||||
*
|
|
||||||
* @author paulburke
|
* @author paulburke
|
||||||
*/
|
*/
|
||||||
public static String getPath(Context context, Uri uri) {
|
public static String getPath(Context context, Uri uri) {
|
||||||
@ -115,21 +110,19 @@ public class FileHelper {
|
|||||||
+ uri.getPathSegments().toString());
|
+ uri.getPathSegments().toString());
|
||||||
|
|
||||||
if ("content".equalsIgnoreCase(uri.getScheme())) {
|
if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
String[] projection = { "_data" };
|
String[] projection = {"_data"};
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||||
int column_index = cursor.getColumnIndexOrThrow("_data");
|
int columnIndex = cursor.getColumnIndexOrThrow("_data");
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
return cursor.getString(column_index);
|
return cursor.getString(columnIndex);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Eat it
|
// Eat it
|
||||||
}
|
}
|
||||||
}
|
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
|
||||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
|
||||||
return uri.getPath();
|
return uri.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,15 +17,16 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.helper;
|
package org.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import android.os.Bundle;
|
||||||
import java.util.GregorianCalendar;
|
import android.text.SpannableStringBuilder;
|
||||||
import java.util.Iterator;
|
import android.text.Spanned;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
import android.text.style.StrikethroughSpan;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class OtherHelper {
|
public class OtherHelper {
|
||||||
|
|
||||||
@ -60,4 +61,10 @@ public class OtherHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SpannableStringBuilder strikeOutText(CharSequence text) {
|
||||||
|
SpannableStringBuilder sb = new SpannableStringBuilder(text);
|
||||||
|
sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,13 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.helper;
|
package org.sufficientlysecure.keychain.helper;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,8 +37,8 @@ public class Preferences {
|
|||||||
return getPreferences(context, false);
|
return getPreferences(context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized Preferences getPreferences(Context context, boolean force_new) {
|
public static synchronized Preferences getPreferences(Context context, boolean forceNew) {
|
||||||
if (mPreferences == null || force_new) {
|
if (mPreferences == null || forceNew) {
|
||||||
mPreferences = new Preferences(context);
|
mPreferences = new Preferences(context);
|
||||||
}
|
}
|
||||||
return mPreferences;
|
return mPreferences;
|
||||||
@ -50,17 +49,17 @@ public class Preferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getLanguage() {
|
public String getLanguage() {
|
||||||
return mSharedPreferences.getString(Constants.pref.LANGUAGE, "");
|
return mSharedPreferences.getString(Constants.Pref.LANGUAGE, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLanguage(String value) {
|
public void setLanguage(String value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putString(Constants.pref.LANGUAGE, value);
|
editor.putString(Constants.Pref.LANGUAGE, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPassPhraseCacheTtl() {
|
public long getPassPhraseCacheTtl() {
|
||||||
int ttl = mSharedPreferences.getInt(Constants.pref.PASS_PHRASE_CACHE_TTL, 180);
|
int ttl = mSharedPreferences.getInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, 180);
|
||||||
// fix the value if it was set to "never" in previous versions, which currently is not
|
// fix the value if it was set to "never" in previous versions, which currently is not
|
||||||
// supported
|
// supported
|
||||||
if (ttl == 0) {
|
if (ttl == 0) {
|
||||||
@ -71,81 +70,81 @@ public class Preferences {
|
|||||||
|
|
||||||
public void setPassPhraseCacheTtl(int value) {
|
public void setPassPhraseCacheTtl(int value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putInt(Constants.pref.PASS_PHRASE_CACHE_TTL, value);
|
editor.putInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDefaultEncryptionAlgorithm() {
|
public int getDefaultEncryptionAlgorithm() {
|
||||||
return mSharedPreferences.getInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM,
|
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM,
|
||||||
PGPEncryptedData.AES_256);
|
PGPEncryptedData.AES_256);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultEncryptionAlgorithm(int value) {
|
public void setDefaultEncryptionAlgorithm(int value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
|
editor.putInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDefaultHashAlgorithm() {
|
public int getDefaultHashAlgorithm() {
|
||||||
return mSharedPreferences.getInt(Constants.pref.DEFAULT_HASH_ALGORITHM,
|
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM,
|
||||||
HashAlgorithmTags.SHA512);
|
HashAlgorithmTags.SHA512);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultHashAlgorithm(int value) {
|
public void setDefaultHashAlgorithm(int value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putInt(Constants.pref.DEFAULT_HASH_ALGORITHM, value);
|
editor.putInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDefaultMessageCompression() {
|
public int getDefaultMessageCompression() {
|
||||||
return mSharedPreferences.getInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION,
|
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION,
|
||||||
Id.choice.compression.zlib);
|
Id.choice.compression.zlib);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultMessageCompression(int value) {
|
public void setDefaultMessageCompression(int value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, value);
|
editor.putInt(Constants.Pref.DEFAULT_MESSAGE_COMPRESSION, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDefaultFileCompression() {
|
public int getDefaultFileCompression() {
|
||||||
return mSharedPreferences.getInt(Constants.pref.DEFAULT_FILE_COMPRESSION,
|
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION,
|
||||||
Id.choice.compression.none);
|
Id.choice.compression.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultFileCompression(int value) {
|
public void setDefaultFileCompression(int value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putInt(Constants.pref.DEFAULT_FILE_COMPRESSION, value);
|
editor.putInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getDefaultAsciiArmour() {
|
public boolean getDefaultAsciiArmour() {
|
||||||
return mSharedPreferences.getBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, false);
|
return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultAsciiArmour(boolean value) {
|
public void setDefaultAsciiArmour(boolean value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, value);
|
editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getForceV3Signatures() {
|
public boolean getForceV3Signatures() {
|
||||||
return mSharedPreferences.getBoolean(Constants.pref.FORCE_V3_SIGNATURES, false);
|
return mSharedPreferences.getBoolean(Constants.Pref.FORCE_V3_SIGNATURES, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForceV3Signatures(boolean value) {
|
public void setForceV3Signatures(boolean value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
editor.putBoolean(Constants.pref.FORCE_V3_SIGNATURES, value);
|
editor.putBoolean(Constants.Pref.FORCE_V3_SIGNATURES, value);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getKeyServers() {
|
public String[] getKeyServers() {
|
||||||
String rawData = mSharedPreferences.getString(Constants.pref.KEY_SERVERS,
|
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
|
||||||
Constants.defaults.KEY_SERVERS);
|
Constants.Defaults.KEY_SERVERS);
|
||||||
Vector<String> servers = new Vector<String>();
|
Vector<String> servers = new Vector<String>();
|
||||||
String chunks[] = rawData.split(",");
|
String chunks[] = rawData.split(",");
|
||||||
for (int i = 0; i < chunks.length; ++i) {
|
for (String c : chunks) {
|
||||||
String tmp = chunks[i].trim();
|
String tmp = c.trim();
|
||||||
if (tmp.length() > 0) {
|
if (tmp.length() > 0) {
|
||||||
servers.add(tmp);
|
servers.add(tmp);
|
||||||
}
|
}
|
||||||
@ -156,8 +155,8 @@ public class Preferences {
|
|||||||
public void setKeyServers(String[] value) {
|
public void setKeyServers(String[] value) {
|
||||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||||
String rawData = "";
|
String rawData = "";
|
||||||
for (int i = 0; i < value.length; ++i) {
|
for (String v : value) {
|
||||||
String tmp = value[i].trim();
|
String tmp = v.trim();
|
||||||
if (tmp.length() == 0) {
|
if (tmp.length() == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -166,7 +165,7 @@ public class Preferences {
|
|||||||
}
|
}
|
||||||
rawData += tmp;
|
rawData += tmp;
|
||||||
}
|
}
|
||||||
editor.putString(Constants.pref.KEY_SERVERS, rawData);
|
editor.putString(Constants.Pref.KEY_SERVERS, rawData);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
import org.spongycastle.openpgp.PGPKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
@ -29,6 +24,11 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
public class PgpConversionHelper {
|
public class PgpConversionHelper {
|
||||||
|
|
||||||
@ -90,10 +90,10 @@ public class PgpConversionHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert from byte[] to PGPSecretKey
|
* Convert from byte[] to PGPSecretKey
|
||||||
*
|
* <p/>
|
||||||
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
|
* Singles keys are encoded as keyRings with one single key in it by Bouncy Castle
|
||||||
*
|
*
|
||||||
* @param keysBytes
|
* @param keyBytes
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
|
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
|
||||||
@ -105,13 +105,13 @@ public class PgpConversionHelper {
|
|||||||
Log.e(Constants.TAG, "Error while converting to PGPSecretKey!", e);
|
Log.e(Constants.TAG, "Error while converting to PGPSecretKey!", e);
|
||||||
}
|
}
|
||||||
PGPSecretKey secKey = null;
|
PGPSecretKey secKey = null;
|
||||||
if(obj instanceof PGPSecretKey) {
|
if (obj instanceof PGPSecretKey) {
|
||||||
if ((secKey = (PGPSecretKey)obj ) == null) {
|
if ((secKey = (PGPSecretKey) obj) == null) {
|
||||||
Log.e(Constants.TAG, "No keys given!");
|
Log.e(Constants.TAG, "No keys given!");
|
||||||
}
|
}
|
||||||
} else if(obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
|
} else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
|
||||||
PGPSecretKeyRing keyRing = null;
|
PGPSecretKeyRing keyRing = null;
|
||||||
if ((keyRing = (PGPSecretKeyRing)obj) == null) {
|
if ((keyRing = (PGPSecretKeyRing) obj) == null) {
|
||||||
Log.e(Constants.TAG, "No keys given!");
|
Log.e(Constants.TAG, "No keys given!");
|
||||||
}
|
}
|
||||||
secKey = keyRing.getSecretKey();
|
secKey = keyRing.getSecretKey();
|
||||||
@ -142,7 +142,7 @@ public class PgpConversionHelper {
|
|||||||
/**
|
/**
|
||||||
* Convert from PGPSecretKey to byte[]
|
* Convert from PGPSecretKey to byte[]
|
||||||
*
|
*
|
||||||
* @param keysBytes
|
* @param key
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
|
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
|
||||||
@ -158,7 +158,7 @@ public class PgpConversionHelper {
|
|||||||
/**
|
/**
|
||||||
* Convert from PGPSecretKeyRing to byte[]
|
* Convert from PGPSecretKeyRing to byte[]
|
||||||
*
|
*
|
||||||
* @param keysBytes
|
* @param keyRing
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
|
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
|
||||||
|
@ -18,38 +18,16 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.spongycastle.bcpg.ArmoredInputStream;
|
import org.spongycastle.bcpg.ArmoredInputStream;
|
||||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||||
import org.spongycastle.openpgp.PGPCompressedData;
|
import org.spongycastle.openpgp.*;
|
||||||
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.PGPSecretKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
|
||||||
import org.spongycastle.openpgp.PGPSignatureList;
|
|
||||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
import org.spongycastle.openpgp.PGPUtil;
|
||||||
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
|
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
|
||||||
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
import org.spongycastle.openpgp.operator.jcajce.*;
|
||||||
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.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
@ -59,12 +37,7 @@ import org.sufficientlysecure.keychain.util.InputData;
|
|||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.*;
|
||||||
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.security.SignatureException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
@ -72,57 +45,57 @@ import java.util.Iterator;
|
|||||||
* This class uses a Builder pattern!
|
* This class uses a Builder pattern!
|
||||||
*/
|
*/
|
||||||
public class PgpDecryptVerify {
|
public class PgpDecryptVerify {
|
||||||
private Context context;
|
private Context mContext;
|
||||||
private InputData data;
|
private InputData mData;
|
||||||
private OutputStream outStream;
|
private OutputStream mOutStream;
|
||||||
|
|
||||||
private ProgressDialogUpdater progressDialogUpdater;
|
private ProgressDialogUpdater mProgressDialogUpdater;
|
||||||
private boolean assumeSymmetric;
|
private boolean mAssumeSymmetric;
|
||||||
private String passphrase;
|
private String mPassphrase;
|
||||||
private long enforcedKeyId;
|
private long mEnforcedKeyId;
|
||||||
|
|
||||||
private PgpDecryptVerify(Builder builder) {
|
private PgpDecryptVerify(Builder builder) {
|
||||||
// private Constructor can only be called from Builder
|
// private Constructor can only be called from Builder
|
||||||
this.context = builder.context;
|
this.mContext = builder.mContext;
|
||||||
this.data = builder.data;
|
this.mData = builder.mData;
|
||||||
this.outStream = builder.outStream;
|
this.mOutStream = builder.mOutStream;
|
||||||
|
|
||||||
this.progressDialogUpdater = builder.progressDialogUpdater;
|
this.mProgressDialogUpdater = builder.mProgressDialogUpdater;
|
||||||
this.assumeSymmetric = builder.assumeSymmetric;
|
this.mAssumeSymmetric = builder.mAssumeSymmetric;
|
||||||
this.passphrase = builder.passphrase;
|
this.mPassphrase = builder.mPassphrase;
|
||||||
this.enforcedKeyId = builder.enforcedKeyId;
|
this.mEnforcedKeyId = builder.mEnforcedKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
// mandatory parameter
|
// mandatory parameter
|
||||||
private Context context;
|
private Context mContext;
|
||||||
private InputData data;
|
private InputData mData;
|
||||||
private OutputStream outStream;
|
private OutputStream mOutStream;
|
||||||
|
|
||||||
// optional
|
// optional
|
||||||
private ProgressDialogUpdater progressDialogUpdater = null;
|
private ProgressDialogUpdater mProgressDialogUpdater = null;
|
||||||
private boolean assumeSymmetric = false;
|
private boolean mAssumeSymmetric = false;
|
||||||
private String passphrase = "";
|
private String mPassphrase = "";
|
||||||
private long enforcedKeyId = 0;
|
private long mEnforcedKeyId = 0;
|
||||||
|
|
||||||
public Builder(Context context, InputData data, OutputStream outStream) {
|
public Builder(Context context, InputData data, OutputStream outStream) {
|
||||||
this.context = context;
|
this.mContext = context;
|
||||||
this.data = data;
|
this.mData = data;
|
||||||
this.outStream = outStream;
|
this.mOutStream = outStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
|
public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) {
|
||||||
this.progressDialogUpdater = progressDialogUpdater;
|
this.mProgressDialogUpdater = progressDialogUpdater;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder assumeSymmetric(boolean assumeSymmetric) {
|
public Builder assumeSymmetric(boolean assumeSymmetric) {
|
||||||
this.assumeSymmetric = assumeSymmetric;
|
this.mAssumeSymmetric = assumeSymmetric;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder passphrase(String passphrase) {
|
public Builder passphrase(String passphrase) {
|
||||||
this.passphrase = passphrase;
|
this.mPassphrase = passphrase;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +107,7 @@ public class PgpDecryptVerify {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Builder enforcedKeyId(long enforcedKeyId) {
|
public Builder enforcedKeyId(long enforcedKeyId) {
|
||||||
this.enforcedKeyId = enforcedKeyId;
|
this.mEnforcedKeyId = enforcedKeyId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,14 +117,14 @@ public class PgpDecryptVerify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgress(int message, int current, int total) {
|
public void updateProgress(int message, int current, int total) {
|
||||||
if (progressDialogUpdater != null) {
|
if (mProgressDialogUpdater != null) {
|
||||||
progressDialogUpdater.setProgress(message, current, total);
|
mProgressDialogUpdater.setProgress(message, current, total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgress(int current, int total) {
|
public void updateProgress(int current, int total) {
|
||||||
if (progressDialogUpdater != null) {
|
if (mProgressDialogUpdater != null) {
|
||||||
progressDialogUpdater.setProgress(current, total);
|
mProgressDialogUpdater.setProgress(current, total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +169,7 @@ public class PgpDecryptVerify {
|
|||||||
public PgpDecryptVerifyResult execute()
|
public PgpDecryptVerifyResult execute()
|
||||||
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
throws IOException, PgpGeneralException, PGPException, SignatureException {
|
||||||
// automatically works with ascii armor input and binary
|
// automatically works with ascii armor input and binary
|
||||||
InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
|
InputStream in = PGPUtil.getDecoderStream(mData.getInputStream());
|
||||||
if (in instanceof ArmoredInputStream) {
|
if (in instanceof ArmoredInputStream) {
|
||||||
ArmoredInputStream aIn = (ArmoredInputStream) in;
|
ArmoredInputStream aIn = (ArmoredInputStream) in;
|
||||||
// it is ascii armored
|
// it is ascii armored
|
||||||
@ -240,7 +213,7 @@ public class PgpDecryptVerify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enc == null) {
|
if (enc == null) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
|
throw new PgpGeneralException(mContext.getString(R.string.error_invalid_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream clear;
|
InputStream clear;
|
||||||
@ -250,7 +223,7 @@ public class PgpDecryptVerify {
|
|||||||
|
|
||||||
// TODO: currently we always only look at the first known key or symmetric encryption,
|
// TODO: currently we always only look at the first known key or symmetric encryption,
|
||||||
// there might be more...
|
// there might be more...
|
||||||
if (assumeSymmetric) {
|
if (mAssumeSymmetric) {
|
||||||
PGPPBEEncryptedData pbe = null;
|
PGPPBEEncryptedData pbe = null;
|
||||||
Iterator<?> it = enc.getEncryptedDataObjects();
|
Iterator<?> it = enc.getEncryptedDataObjects();
|
||||||
// find secret key
|
// find secret key
|
||||||
@ -264,7 +237,7 @@ public class PgpDecryptVerify {
|
|||||||
|
|
||||||
if (pbe == null) {
|
if (pbe == null) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
context.getString(R.string.error_no_symmetric_encryption_packet));
|
mContext.getString(R.string.error_no_symmetric_encryption_packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||||
@ -273,7 +246,7 @@ public class PgpDecryptVerify {
|
|||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
|
||||||
PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
|
PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
|
||||||
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
passphrase.toCharArray());
|
mPassphrase.toCharArray());
|
||||||
|
|
||||||
clear = pbe.getDataStream(decryptorFactory);
|
clear = pbe.getDataStream(decryptorFactory);
|
||||||
|
|
||||||
@ -290,33 +263,37 @@ public class PgpDecryptVerify {
|
|||||||
Object obj = it.next();
|
Object obj = it.next();
|
||||||
if (obj instanceof PGPPublicKeyEncryptedData) {
|
if (obj instanceof PGPPublicKeyEncryptedData) {
|
||||||
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
|
||||||
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID());
|
secretKey = ProviderHelper.getPGPSecretKeyByKeyId(mContext, encData.getKeyID());
|
||||||
if (secretKey != null) {
|
if (secretKey != null) {
|
||||||
// secret key exists in database
|
// secret key exists in database
|
||||||
|
|
||||||
// allow only a specific key for decryption?
|
// allow only a specific key for decryption?
|
||||||
if (enforcedKeyId != 0) {
|
if (mEnforcedKeyId != 0) {
|
||||||
// TODO: improve this code! get master key directly!
|
// TODO: improve this code! get master key directly!
|
||||||
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, encData.getKeyID());
|
PGPSecretKeyRing secretKeyRing =
|
||||||
|
ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, encData.getKeyID());
|
||||||
long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
|
long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
|
||||||
Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
|
Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
|
||||||
Log.d(Constants.TAG, "enforcedKeyId: " + enforcedKeyId);
|
Log.d(Constants.TAG, "enforcedKeyId: " + mEnforcedKeyId);
|
||||||
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
|
Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
|
||||||
|
|
||||||
if (enforcedKeyId != masterKeyId) {
|
if (mEnforcedKeyId != masterKeyId) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
|
throw new PgpGeneralException(
|
||||||
|
mContext.getString(R.string.error_no_secret_key_found));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pbe = encData;
|
pbe = encData;
|
||||||
|
|
||||||
// if no passphrase was explicitly set try to get it from the cache service
|
// if no passphrase was explicitly set try to get it from the cache service
|
||||||
if (passphrase == null) {
|
if (mPassphrase == null) {
|
||||||
// returns "" if key has no passphrase
|
// returns "" if key has no passphrase
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(context, encData.getKeyID());
|
mPassphrase =
|
||||||
|
PassphraseCacheService.getCachedPassphrase(mContext, encData.getKeyID());
|
||||||
|
|
||||||
// if passphrase was not cached, return here indicating that a passphrase is missing!
|
// if passphrase was not cached, return here
|
||||||
if (passphrase == null) {
|
// indicating that a passphrase is missing!
|
||||||
|
if (mPassphrase == null) {
|
||||||
returnData.setKeyPassphraseNeeded(true);
|
returnData.setKeyPassphraseNeeded(true);
|
||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
@ -330,7 +307,7 @@ public class PgpDecryptVerify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (secretKey == null) {
|
if (secretKey == null) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found));
|
throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
|
||||||
}
|
}
|
||||||
|
|
||||||
currentProgress += 5;
|
currentProgress += 5;
|
||||||
@ -339,14 +316,14 @@ public class PgpDecryptVerify {
|
|||||||
try {
|
try {
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
passphrase.toCharArray());
|
mPassphrase.toCharArray());
|
||||||
privateKey = secretKey.extractPrivateKey(keyDecryptor);
|
privateKey = secretKey.extractPrivateKey(keyDecryptor);
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
throw new PGPException(context.getString(R.string.error_wrong_passphrase));
|
throw new PGPException(mContext.getString(R.string.error_wrong_passphrase));
|
||||||
}
|
}
|
||||||
if (privateKey == null) {
|
if (privateKey == null) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
context.getString(R.string.error_could_not_extract_private_key));
|
mContext.getString(R.string.error_could_not_extract_private_key));
|
||||||
}
|
}
|
||||||
currentProgress += 5;
|
currentProgress += 5;
|
||||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||||
@ -386,7 +363,7 @@ public class PgpDecryptVerify {
|
|||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
signature = sigList.get(i);
|
signature = sigList.get(i);
|
||||||
signatureKey = ProviderHelper
|
signatureKey = ProviderHelper
|
||||||
.getPGPPublicKeyByKeyId(context, signature.getKeyID());
|
.getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
|
||||||
if (signatureKeyId == 0) {
|
if (signatureKeyId == 0) {
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
}
|
}
|
||||||
@ -397,7 +374,7 @@ public class PgpDecryptVerify {
|
|||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
String userId = null;
|
String userId = null;
|
||||||
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
|
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
|
||||||
context, signatureKeyId);
|
mContext, signatureKeyId);
|
||||||
if (signKeyRing != null) {
|
if (signKeyRing != null) {
|
||||||
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||||
}
|
}
|
||||||
@ -409,7 +386,8 @@ public class PgpDecryptVerify {
|
|||||||
signatureResult.setKeyId(signatureKeyId);
|
signatureResult.setKeyId(signatureKeyId);
|
||||||
|
|
||||||
if (signature != null) {
|
if (signature != null) {
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider()
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
signature.init(contentVerifierBuilderProvider, signatureKey);
|
signature.init(contentVerifierBuilderProvider, signatureKey);
|
||||||
@ -444,9 +422,9 @@ public class PgpDecryptVerify {
|
|||||||
int n;
|
int n;
|
||||||
// TODO: progress calculation is broken here! Try to rework it based on commented code!
|
// TODO: progress calculation is broken here! Try to rework it based on commented code!
|
||||||
// int progress = 0;
|
// int progress = 0;
|
||||||
long startPos = data.getStreamPosition();
|
long startPos = mData.getStreamPosition();
|
||||||
while ((n = dataIn.read(buffer)) > 0) {
|
while ((n = dataIn.read(buffer)) > 0) {
|
||||||
outStream.write(buffer, 0, n);
|
mOutStream.write(buffer, 0, n);
|
||||||
// progress += n;
|
// progress += n;
|
||||||
if (signature != null) {
|
if (signature != null) {
|
||||||
try {
|
try {
|
||||||
@ -460,11 +438,11 @@ public class PgpDecryptVerify {
|
|||||||
// unknown size, but try to at least have a moving, slowing down progress bar
|
// unknown size, but try to at least have a moving, slowing down progress bar
|
||||||
// currentProgress = startProgress + (endProgress - startProgress) * progress
|
// currentProgress = startProgress + (endProgress - startProgress) * progress
|
||||||
// / (progress + 100000);
|
// / (progress + 100000);
|
||||||
if (data.getSize() - startPos == 0) {
|
if (mData.getSize() - startPos == 0) {
|
||||||
currentProgress = endProgress;
|
currentProgress = endProgress;
|
||||||
} else {
|
} else {
|
||||||
currentProgress = (int) (startProgress + (endProgress - startProgress)
|
currentProgress = (int) (startProgress + (endProgress - startProgress)
|
||||||
* (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
|
* (mData.getStreamPosition() - startPos) / (mData.getSize() - startPos));
|
||||||
}
|
}
|
||||||
updateProgress(currentProgress, 100);
|
updateProgress(currentProgress, 100);
|
||||||
}
|
}
|
||||||
@ -480,7 +458,7 @@ public class PgpDecryptVerify {
|
|||||||
signatureResult.setSignatureOnly(false);
|
signatureResult.setSignatureOnly(false);
|
||||||
|
|
||||||
//Now check binding signatures
|
//Now check binding signatures
|
||||||
boolean validKeyBinding = verifyKeyBinding(context, messageSignature, signatureKey);
|
boolean validKeyBinding = verifyKeyBinding(mContext, messageSignature, signatureKey);
|
||||||
boolean validSignature = signature.verify(messageSignature);
|
boolean validSignature = signature.verify(messageSignature);
|
||||||
|
|
||||||
// TODO: implement CERTIFIED!
|
// TODO: implement CERTIFIED!
|
||||||
@ -499,7 +477,7 @@ public class PgpDecryptVerify {
|
|||||||
} else {
|
} else {
|
||||||
// failed
|
// failed
|
||||||
Log.d(Constants.TAG, "Integrity verification: failed!");
|
Log.d(Constants.TAG, "Integrity verification: failed!");
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_integrity_check_failed));
|
throw new PgpGeneralException(mContext.getString(R.string.error_integrity_check_failed));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no integrity check
|
// no integrity check
|
||||||
@ -555,21 +533,21 @@ public class PgpDecryptVerify {
|
|||||||
out.close();
|
out.close();
|
||||||
|
|
||||||
byte[] clearText = out.toByteArray();
|
byte[] clearText = out.toByteArray();
|
||||||
outStream.write(clearText);
|
mOutStream.write(clearText);
|
||||||
|
|
||||||
updateProgress(R.string.progress_processing_signature, 60, 100);
|
updateProgress(R.string.progress_processing_signature, 60, 100);
|
||||||
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
|
||||||
|
|
||||||
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
|
PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
|
||||||
if (sigList == null) {
|
if (sigList == null) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_corrupt_data));
|
throw new PgpGeneralException(mContext.getString(R.string.error_corrupt_data));
|
||||||
}
|
}
|
||||||
PGPSignature signature = null;
|
PGPSignature signature = null;
|
||||||
long signatureKeyId = 0;
|
long signatureKeyId = 0;
|
||||||
PGPPublicKey signatureKey = null;
|
PGPPublicKey signatureKey = null;
|
||||||
for (int i = 0; i < sigList.size(); ++i) {
|
for (int i = 0; i < sigList.size(); ++i) {
|
||||||
signature = sigList.get(i);
|
signature = sigList.get(i);
|
||||||
signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID());
|
signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
|
||||||
if (signatureKeyId == 0) {
|
if (signatureKeyId == 0) {
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
}
|
}
|
||||||
@ -579,7 +557,7 @@ public class PgpDecryptVerify {
|
|||||||
} else {
|
} else {
|
||||||
signatureKeyId = signature.getKeyID();
|
signatureKeyId = signature.getKeyID();
|
||||||
String userId = null;
|
String userId = null;
|
||||||
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
|
PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
|
||||||
signatureKeyId);
|
signatureKeyId);
|
||||||
if (signKeyRing != null) {
|
if (signKeyRing != null) {
|
||||||
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
|
||||||
@ -623,7 +601,7 @@ public class PgpDecryptVerify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Now check binding signatures
|
//Now check binding signatures
|
||||||
boolean validKeyBinding = verifyKeyBinding(context, signature, signatureKey);
|
boolean validKeyBinding = verifyKeyBinding(mContext, signature, signatureKey);
|
||||||
boolean validSignature = signature.verify();
|
boolean validSignature = signature.verify();
|
||||||
|
|
||||||
if (validSignature & validKeyBinding) {
|
if (validSignature & validKeyBinding) {
|
||||||
@ -638,7 +616,8 @@ public class PgpDecryptVerify {
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) {
|
private static boolean verifyKeyBinding(Context context,
|
||||||
|
PGPSignature signature, PGPPublicKey signatureKey) {
|
||||||
long signatureKeyId = signature.getKeyID();
|
long signatureKeyId = signature.getKeyID();
|
||||||
boolean validKeyBinding = false;
|
boolean validKeyBinding = false;
|
||||||
|
|
||||||
@ -673,7 +652,8 @@ public class PgpDecryptVerify {
|
|||||||
//about keys without subkey signing. Can't get it to import a slightly broken one
|
//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.
|
//either, so we will err on bad subkey binding here.
|
||||||
PGPSignature sig = itr.next();
|
PGPSignature sig = itr.next();
|
||||||
if (sig.getKeyID() == masterPublicKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
|
if (sig.getKeyID() == masterPublicKey.getKeyID() &&
|
||||||
|
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
|
||||||
//check and if ok, check primary key binding.
|
//check and if ok, check primary key binding.
|
||||||
try {
|
try {
|
||||||
sig.init(contentVerifierBuilderProvider, masterPublicKey);
|
sig.init(contentVerifierBuilderProvider, masterPublicKey);
|
||||||
@ -684,34 +664,37 @@ public class PgpDecryptVerify {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validTempSubkeyBinding)
|
if (validTempSubkeyBinding) {
|
||||||
validSubkeyBinding = true;
|
validSubkeyBinding = true;
|
||||||
|
}
|
||||||
if (validTempSubkeyBinding) {
|
if (validTempSubkeyBinding) {
|
||||||
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
|
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
|
||||||
masterPublicKey, signingPublicKey);
|
masterPublicKey, signingPublicKey);
|
||||||
if (validPrimaryKeyBinding)
|
if (validPrimaryKeyBinding) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
|
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
|
||||||
masterPublicKey, signingPublicKey);
|
masterPublicKey, signingPublicKey);
|
||||||
if (validPrimaryKeyBinding)
|
if (validPrimaryKeyBinding) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (validSubkeyBinding & validPrimaryKeyBinding);
|
return (validSubkeyBinding & validPrimaryKeyBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector Pkts,
|
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
|
||||||
PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
|
||||||
boolean validPrimaryKeyBinding = false;
|
boolean validPrimaryKeyBinding = false;
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
new JcaPGPContentVerifierBuilderProvider()
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
PGPSignatureList eSigList;
|
PGPSignatureList eSigList;
|
||||||
|
|
||||||
if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
|
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
|
||||||
try {
|
try {
|
||||||
eSigList = Pkts.getEmbeddedSignatures();
|
eSigList = pkts.getEmbeddedSignatures();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return false;
|
return false;
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
@ -723,8 +706,9 @@ public class PgpDecryptVerify {
|
|||||||
try {
|
try {
|
||||||
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
|
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
|
||||||
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
|
||||||
if (validPrimaryKeyBinding)
|
if (validPrimaryKeyBinding) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
continue;
|
continue;
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
@ -743,10 +727,9 @@ public class PgpDecryptVerify {
|
|||||||
* @param sig
|
* @param sig
|
||||||
* @param line
|
* @param line
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
private static void processLine(PGPSignature sig, byte[] line)
|
private static void processLine(PGPSignature sig, byte[] line)
|
||||||
throws SignatureException, IOException {
|
throws SignatureException {
|
||||||
int length = getLengthWithoutWhiteSpace(line);
|
int length = getLengthWithoutWhiteSpace(line);
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
sig.update(line, 0, length);
|
sig.update(line, 0, length);
|
||||||
|
@ -19,36 +19,35 @@ package org.sufficientlysecure.keychain.pgp;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
|
||||||
public class PgpDecryptVerifyResult implements Parcelable {
|
public class PgpDecryptVerifyResult implements Parcelable {
|
||||||
boolean symmetricPassphraseNeeded;
|
boolean mSymmetricPassphraseNeeded;
|
||||||
boolean keyPassphraseNeeded;
|
boolean mKeyPassphraseNeeded;
|
||||||
OpenPgpSignatureResult signatureResult;
|
OpenPgpSignatureResult mSignatureResult;
|
||||||
|
|
||||||
public boolean isSymmetricPassphraseNeeded() {
|
public boolean isSymmetricPassphraseNeeded() {
|
||||||
return symmetricPassphraseNeeded;
|
return mSymmetricPassphraseNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
|
public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
|
||||||
this.symmetricPassphraseNeeded = symmetricPassphraseNeeded;
|
this.mSymmetricPassphraseNeeded = symmetricPassphraseNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isKeyPassphraseNeeded() {
|
public boolean isKeyPassphraseNeeded() {
|
||||||
return keyPassphraseNeeded;
|
return mKeyPassphraseNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
|
public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
|
||||||
this.keyPassphraseNeeded = keyPassphraseNeeded;
|
this.mKeyPassphraseNeeded = keyPassphraseNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpSignatureResult getSignatureResult() {
|
public OpenPgpSignatureResult getSignatureResult() {
|
||||||
return signatureResult;
|
return mSignatureResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
|
public void setSignatureResult(OpenPgpSignatureResult signatureResult) {
|
||||||
this.signatureResult = signatureResult;
|
this.mSignatureResult = signatureResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PgpDecryptVerifyResult() {
|
public PgpDecryptVerifyResult() {
|
||||||
@ -56,9 +55,9 @@ public class PgpDecryptVerifyResult implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
|
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
|
||||||
this.symmetricPassphraseNeeded = b.symmetricPassphraseNeeded;
|
this.mSymmetricPassphraseNeeded = b.mSymmetricPassphraseNeeded;
|
||||||
this.keyPassphraseNeeded = b.keyPassphraseNeeded;
|
this.mKeyPassphraseNeeded = b.mKeyPassphraseNeeded;
|
||||||
this.signatureResult = b.signatureResult;
|
this.mSignatureResult = b.mSignatureResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -67,17 +66,17 @@ public class PgpDecryptVerifyResult implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeByte((byte) (symmetricPassphraseNeeded ? 1 : 0));
|
dest.writeByte((byte) (mSymmetricPassphraseNeeded ? 1 : 0));
|
||||||
dest.writeByte((byte) (keyPassphraseNeeded ? 1 : 0));
|
dest.writeByte((byte) (mKeyPassphraseNeeded ? 1 : 0));
|
||||||
dest.writeParcelable(signatureResult, 0);
|
dest.writeParcelable(mSignatureResult, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
|
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
|
||||||
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
|
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
|
||||||
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
|
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
|
||||||
vr.symmetricPassphraseNeeded = source.readByte() == 1;
|
vr.mSymmetricPassphraseNeeded = source.readByte() == 1;
|
||||||
vr.keyPassphraseNeeded = source.readByte() == 1;
|
vr.mKeyPassphraseNeeded = source.readByte() == 1;
|
||||||
vr.signatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
|
vr.mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,22 +17,10 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.io.File;
|
import android.content.Context;
|
||||||
import java.io.FileNotFoundException;
|
import android.content.pm.PackageInfo;
|
||||||
import java.io.IOException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import java.io.InputStream;
|
import org.spongycastle.openpgp.*;
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPEncryptedDataList;
|
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
@ -42,21 +30,25 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
import android.content.Context;
|
import java.io.File;
|
||||||
import android.content.pm.PackageInfo;
|
import java.io.IOException;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import java.io.InputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class PgpHelper {
|
public class PgpHelper {
|
||||||
|
|
||||||
public static Pattern PGP_MESSAGE = Pattern.compile(
|
public static final Pattern PGP_MESSAGE = Pattern.compile(
|
||||||
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
|
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
|
||||||
|
|
||||||
public static Pattern PGP_SIGNED_MESSAGE = Pattern
|
public static final Pattern PGP_SIGNED_MESSAGE = Pattern
|
||||||
.compile(
|
.compile(
|
||||||
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
||||||
Pattern.DOTALL);
|
Pattern.DOTALL);
|
||||||
|
|
||||||
public static Pattern PGP_PUBLIC_KEY = Pattern.compile(
|
public static final Pattern PGP_PUBLIC_KEY = Pattern.compile(
|
||||||
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
|
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
|
||||||
Pattern.DOTALL);
|
Pattern.DOTALL);
|
||||||
|
|
||||||
@ -187,17 +179,16 @@ public class PgpHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes file securely by overwriting it with random data before deleting it.
|
* Deletes file securely by overwriting it with random data before deleting it.
|
||||||
*
|
* <p/>
|
||||||
* TODO: Does this really help on flash storage?
|
* TODO: Does this really help on flash storage?
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param progress
|
* @param progress
|
||||||
* @param file
|
* @param file
|
||||||
* @throws FileNotFoundException
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
|
public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file)
|
||||||
throws FileNotFoundException, IOException {
|
throws IOException {
|
||||||
long length = file.length();
|
long length = file.length();
|
||||||
SecureRandom random = new SecureRandom();
|
SecureRandom random = new SecureRandom();
|
||||||
RandomAccessFile raf = new RandomAccessFile(file, "rws");
|
RandomAccessFile raf = new RandomAccessFile(file, "rws");
|
||||||
@ -207,8 +198,9 @@ public class PgpHelper {
|
|||||||
int pos = 0;
|
int pos = 0;
|
||||||
String msg = context.getString(R.string.progress_deleting_securely, file.getName());
|
String msg = context.getString(R.string.progress_deleting_securely, file.getName());
|
||||||
while (pos < length) {
|
while (pos < length) {
|
||||||
if (progress != null)
|
if (progress != null) {
|
||||||
progress.setProgress(msg, (int) (100 * pos / length), 100);
|
progress.setProgress(msg, (int) (100 * pos / length), 100);
|
||||||
|
}
|
||||||
random.nextBytes(data);
|
random.nextBytes(data);
|
||||||
raf.write(data);
|
raf.write(data);
|
||||||
pos += data.length;
|
pos += data.length;
|
||||||
|
@ -17,24 +17,11 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import android.content.Context;
|
||||||
import java.io.ByteArrayOutputStream;
|
import android.os.Bundle;
|
||||||
import java.io.FileNotFoundException;
|
import android.os.Environment;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.*;
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
@ -43,28 +30,37 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
|||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||||
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
import org.sufficientlysecure.keychain.util.*;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
|
||||||
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
|
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
|
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import java.io.ByteArrayOutputStream;
|
||||||
import android.os.Bundle;
|
import java.io.IOException;
|
||||||
import android.os.Environment;
|
import java.io.OutputStream;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class PgpImportExport {
|
public class PgpImportExport {
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ProgressDialogUpdater mProgress;
|
private ProgressDialogUpdater mProgress;
|
||||||
|
|
||||||
|
private KeychainServiceListener mKeychainServiceListener;
|
||||||
|
|
||||||
public PgpImportExport(Context context, ProgressDialogUpdater progress) {
|
public PgpImportExport(Context context, ProgressDialogUpdater progress) {
|
||||||
super();
|
super();
|
||||||
this.mContext = context;
|
this.mContext = context;
|
||||||
this.mProgress = progress;
|
this.mProgress = progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PgpImportExport(Context context,
|
||||||
|
ProgressDialogUpdater progress, KeychainServiceListener keychainListener) {
|
||||||
|
super();
|
||||||
|
this.mContext = context;
|
||||||
|
this.mProgress = progress;
|
||||||
|
this.mKeychainServiceListener = keychainListener;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateProgress(int message, int current, int total) {
|
public void updateProgress(int message, int current, int total) {
|
||||||
if (mProgress != null) {
|
if (mProgress != null) {
|
||||||
mProgress.setProgress(message, current, total);
|
mProgress.setProgress(message, current, total);
|
||||||
@ -85,8 +81,9 @@ public class PgpImportExport {
|
|||||||
|
|
||||||
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
|
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
|
ArmoredOutputStream aos = null;
|
||||||
try {
|
try {
|
||||||
|
aos = new ArmoredOutputStream(bos);
|
||||||
aos.write(keyring.getEncoded());
|
aos.write(keyring.getEncoded());
|
||||||
aos.close();
|
aos.close();
|
||||||
|
|
||||||
@ -101,7 +98,8 @@ public class PgpImportExport {
|
|||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
bos.close();
|
if (aos != null) { aos.close(); }
|
||||||
|
if (bos != null) { bos.close(); }
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,59 +159,68 @@ public class PgpImportExport {
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bundle exportKeyRings(ArrayList<Long> keyRingMasterKeyIds, int keyType,
|
public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds, ArrayList<Long> secretKeyRingMasterIds,
|
||||||
OutputStream outStream) throws PgpGeneralException, FileNotFoundException,
|
OutputStream outStream) throws PgpGeneralException,
|
||||||
PGPException, IOException {
|
PGPException, IOException {
|
||||||
Bundle returnData = new Bundle();
|
Bundle returnData = new Bundle();
|
||||||
|
|
||||||
|
int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size();
|
||||||
|
int progress = 0;
|
||||||
|
|
||||||
updateProgress(
|
updateProgress(
|
||||||
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
|
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
|
||||||
keyRingMasterKeyIds.size()), 0, 100);
|
masterKeyIdsSize), 0, 100);
|
||||||
|
|
||||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
mContext.getString(R.string.error_external_storage_not_ready));
|
mContext.getString(R.string.error_external_storage_not_ready));
|
||||||
}
|
}
|
||||||
|
// For each public masterKey id
|
||||||
|
for (long pubKeyMasterId : publicKeyRingMasterIds) {
|
||||||
|
progress++;
|
||||||
|
// Create an output stream
|
||||||
|
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
|
||||||
|
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||||
|
|
||||||
if (keyType == Id.type.secret_key) {
|
updateProgress(progress * 100 / masterKeyIdsSize, 100);
|
||||||
ArmoredOutputStream outSec = new ArmoredOutputStream(outStream);
|
PGPPublicKeyRing publicKeyRing =
|
||||||
outSec.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
ProviderHelper.getPGPPublicKeyRingByMasterKeyId(mContext, pubKeyMasterId);
|
||||||
|
|
||||||
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
|
if (publicKeyRing != null) {
|
||||||
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
|
publicKeyRing.encode(arOutStream);
|
||||||
|
|
||||||
PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
|
|
||||||
mContext, keyRingMasterKeyIds.get(i));
|
|
||||||
|
|
||||||
if (secretKeyRing != null) {
|
|
||||||
secretKeyRing.encode(outSec);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
outSec.close();
|
|
||||||
} else {
|
|
||||||
// export public keyrings...
|
|
||||||
ArmoredOutputStream outPub = new ArmoredOutputStream(outStream);
|
|
||||||
outPub.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
|
||||||
|
|
||||||
for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) {
|
if (mKeychainServiceListener.hasServiceStopped()) {
|
||||||
// double the needed time if exporting both public and secret parts
|
arOutStream.close();
|
||||||
if (keyType == Id.type.secret_key) {
|
return null;
|
||||||
updateProgress(i * 100 / keyRingMasterKeyIds.size() / 2, 100);
|
|
||||||
} else {
|
|
||||||
updateProgress(i * 100 / keyRingMasterKeyIds.size(), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(
|
|
||||||
mContext, keyRingMasterKeyIds.get(i));
|
|
||||||
|
|
||||||
if (publicKeyRing != null) {
|
|
||||||
publicKeyRing.encode(outPub);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
outPub.close();
|
|
||||||
|
arOutStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
returnData.putInt(KeychainIntentService.RESULT_EXPORT, keyRingMasterKeyIds.size());
|
// For each secret masterKey id
|
||||||
|
for (long secretKeyMasterId : secretKeyRingMasterIds) {
|
||||||
|
progress++;
|
||||||
|
// Create an output stream
|
||||||
|
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
|
||||||
|
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||||
|
|
||||||
|
updateProgress(progress * 100 / masterKeyIdsSize, 100);
|
||||||
|
PGPSecretKeyRing secretKeyRing =
|
||||||
|
ProviderHelper.getPGPSecretKeyRingByMasterKeyId(mContext, secretKeyMasterId);
|
||||||
|
|
||||||
|
if (secretKeyRing != null) {
|
||||||
|
secretKeyRing.encode(arOutStream);
|
||||||
|
}
|
||||||
|
if (mKeychainServiceListener.hasServiceStopped()) {
|
||||||
|
arOutStream.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
arOutStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize);
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
|
|
||||||
@ -234,7 +241,7 @@ public class PgpImportExport {
|
|||||||
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
|
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
|
||||||
secretKeyRing.getSecretKeys())) {
|
secretKeyRing.getSecretKeys())) {
|
||||||
if (!testSecretKey.isMasterKey()) {
|
if (!testSecretKey.isMasterKey()) {
|
||||||
if (PgpKeyHelper.isSecretKeyPrivateEmpty(testSecretKey)) {
|
if (testSecretKey.isPrivateKeyEmpty()) {
|
||||||
// this is bad, something is very wrong...
|
// this is bad, something is very wrong...
|
||||||
save = false;
|
save = false;
|
||||||
status = Id.return_value.bad;
|
status = Id.return_value.bad;
|
||||||
@ -255,8 +262,9 @@ public class PgpImportExport {
|
|||||||
}
|
}
|
||||||
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
|
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
|
||||||
}
|
}
|
||||||
if (newPubRing != null)
|
if (newPubRing != null) {
|
||||||
ProviderHelper.saveKeyRing(mContext, newPubRing);
|
ProviderHelper.saveKeyRing(mContext, newPubRing);
|
||||||
|
}
|
||||||
// TODO: remove status returns, use exceptions!
|
// TODO: remove status returns, use exceptions!
|
||||||
status = Id.return_value.ok;
|
status = Id.return_value.ok;
|
||||||
}
|
}
|
||||||
|
@ -17,28 +17,27 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import android.content.Context;
|
||||||
import java.util.Date;
|
import android.graphics.Color;
|
||||||
import java.util.GregorianCalendar;
|
import android.text.Spannable;
|
||||||
import java.util.Locale;
|
import android.text.SpannableStringBuilder;
|
||||||
import java.util.Vector;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
import org.spongycastle.openpgp.*;
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
|
||||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
|
||||||
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.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import android.content.Context;
|
import java.security.DigestException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class PgpKeyHelper {
|
public class PgpKeyHelper {
|
||||||
|
|
||||||
@ -466,57 +465,32 @@ public class PgpKeyHelper {
|
|||||||
String algorithmStr;
|
String algorithmStr;
|
||||||
|
|
||||||
switch (algorithm) {
|
switch (algorithm) {
|
||||||
case PGPPublicKey.RSA_ENCRYPT:
|
case PGPPublicKey.RSA_ENCRYPT:
|
||||||
case PGPPublicKey.RSA_GENERAL:
|
case PGPPublicKey.RSA_GENERAL:
|
||||||
case PGPPublicKey.RSA_SIGN: {
|
case PGPPublicKey.RSA_SIGN: {
|
||||||
algorithmStr = "RSA";
|
algorithmStr = "RSA";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PGPPublicKey.DSA: {
|
case PGPPublicKey.DSA: {
|
||||||
algorithmStr = "DSA";
|
algorithmStr = "DSA";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PGPPublicKey.ELGAMAL_ENCRYPT:
|
case PGPPublicKey.ELGAMAL_ENCRYPT:
|
||||||
case PGPPublicKey.ELGAMAL_GENERAL: {
|
case PGPPublicKey.ELGAMAL_GENERAL: {
|
||||||
algorithmStr = "ElGamal";
|
algorithmStr = "ElGamal";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
algorithmStr = "Unknown";
|
algorithmStr = "Unknown";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return algorithmStr + ", " + keySize + " bit";
|
return algorithmStr + ", " + keySize + " bit";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts fingerprint to hex with whitespaces after 4 characters
|
|
||||||
*
|
|
||||||
* @param fp
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String convertFingerprintToHex(byte[] fp, boolean chunked) {
|
|
||||||
String fingerPrint = "";
|
|
||||||
for (int i = 0; i < fp.length; ++i) {
|
|
||||||
if (chunked && i != 0 && i % 10 == 0) {
|
|
||||||
fingerPrint += " ";
|
|
||||||
} else if (chunked && i != 0 && i % 2 == 0) {
|
|
||||||
fingerPrint += " ";
|
|
||||||
}
|
|
||||||
String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US);
|
|
||||||
while (chunk.length() < 2) {
|
|
||||||
chunk = "0" + chunk;
|
|
||||||
}
|
|
||||||
fingerPrint += chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fingerPrint;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFingerPrint(Context context, long keyId) {
|
public static String getFingerPrint(Context context, long keyId) {
|
||||||
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
|
PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
|
||||||
// if it is no public key get it from your own keys...
|
// if it is no public key get it from your own keys...
|
||||||
@ -529,45 +503,140 @@ public class PgpKeyHelper {
|
|||||||
key = secretKey.getPublicKey();
|
key = secretKey.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
return convertFingerprintToHex(key.getFingerprint(), true);
|
return convertFingerprintToHex(key.getFingerprint());
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
|
|
||||||
return secretKey.isPrivateKeyEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
|
|
||||||
// PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
|
|
||||||
// if (secretKey == null) {
|
|
||||||
// Log.e(Constants.TAG, "Key could not be found!");
|
|
||||||
// return false; // could be a public key, assume it is not empty
|
|
||||||
// }
|
|
||||||
// return isSecretKeyPrivateEmpty(secretKey);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public static String convertKeyIdToHex(long keyId) {
|
|
||||||
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);
|
|
||||||
while (fingerPrint.length() < 8) {
|
|
||||||
fingerPrint = "0" + fingerPrint;
|
|
||||||
}
|
|
||||||
return fingerPrint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: documentation
|
* Converts fingerprint to hex (optional: with whitespaces after 4 characters)
|
||||||
|
* <p/>
|
||||||
|
* Fingerprint is shown using lowercase characters. Studies have shown that humans can
|
||||||
|
* better differentiate between numbers and letters when letters are lowercase.
|
||||||
|
*
|
||||||
|
* @param fingerprint
|
||||||
|
* @param split split into 4 character chunks
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String convertFingerprintToHex(byte[] fingerprint) {
|
||||||
|
String hexString = Hex.toHexString(fingerprint);
|
||||||
|
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert key id from long to 64 bit hex string
|
||||||
|
* <p/>
|
||||||
|
* V4: "The Key ID is the low-order 64 bits of the fingerprint"
|
||||||
|
* <p/>
|
||||||
|
* see http://tools.ietf.org/html/rfc4880#section-12.2
|
||||||
*
|
*
|
||||||
* @param keyId
|
* @param keyId
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static String convertKeyToHex(long keyId) {
|
public static String convertKeyIdToHex(long keyId) {
|
||||||
return convertKeyIdToHex(keyId >> 32) + convertKeyIdToHex(keyId);
|
long upper = keyId >> 32;
|
||||||
|
if (upper == 0) {
|
||||||
|
// this is a short key id
|
||||||
|
return convertKeyIdToHexShort(keyId);
|
||||||
|
}
|
||||||
|
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long convertHexToKeyId(String data) {
|
public static String convertKeyIdToHexShort(long keyId) {
|
||||||
int len = data.length();
|
return "0x" + convertKeyIdToHex32bit(keyId);
|
||||||
String s2 = data.substring(len - 8);
|
}
|
||||||
String s1 = data.substring(0, len - 8);
|
|
||||||
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
|
private static String convertKeyIdToHex32bit(long keyId) {
|
||||||
|
String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
|
||||||
|
while (hexString.length() < 8) {
|
||||||
|
hexString = "0" + hexString;
|
||||||
|
}
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
|
||||||
|
// split by 4 characters
|
||||||
|
fingerprint = fingerprint.replaceAll("(.{4})(?!$)", "$1 ");
|
||||||
|
|
||||||
|
// add line breaks to have a consistent "image" that can be recognized
|
||||||
|
char[] chars = fingerprint.toCharArray();
|
||||||
|
chars[24] = '\n';
|
||||||
|
fingerprint = String.valueOf(chars);
|
||||||
|
|
||||||
|
SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
|
||||||
|
try {
|
||||||
|
// for each 4 characters of the fingerprint + 1 space
|
||||||
|
for (int i = 0; i < fingerprint.length(); i += 5) {
|
||||||
|
int spanEnd = Math.min(i + 4, fingerprint.length());
|
||||||
|
String fourChars = fingerprint.substring(i, spanEnd);
|
||||||
|
|
||||||
|
int raw = Integer.parseInt(fourChars, 16);
|
||||||
|
byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)};
|
||||||
|
int[] color = getRgbForData(bytes);
|
||||||
|
int r = color[0];
|
||||||
|
int g = color[1];
|
||||||
|
int b = color[2];
|
||||||
|
|
||||||
|
// we cannot change black by multiplication, so adjust it to an almost-black grey,
|
||||||
|
// which will then be brightened to the minimal brightness level
|
||||||
|
if (r == 0 && g == 0 && b == 0) {
|
||||||
|
r = 1;
|
||||||
|
g = 1;
|
||||||
|
b = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert rgb to brightness
|
||||||
|
double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||||
|
|
||||||
|
// If a color is too dark to be seen on black,
|
||||||
|
// then brighten it up to a minimal brightness.
|
||||||
|
if (brightness < 80) {
|
||||||
|
double factor = 80.0 / brightness;
|
||||||
|
r = Math.min(255, (int) (r * factor));
|
||||||
|
g = Math.min(255, (int) (g * factor));
|
||||||
|
b = Math.min(255, (int) (b * factor));
|
||||||
|
|
||||||
|
// If it is too light, then darken it to a respective maximal brightness.
|
||||||
|
} else if (brightness > 180) {
|
||||||
|
double factor = 180.0 / brightness;
|
||||||
|
r = (int) (r * factor);
|
||||||
|
g = (int) (g * factor);
|
||||||
|
b = (int) (b * factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a foreground color with the 3 digest integers as RGB
|
||||||
|
// and then converting that int to hex to use as a color
|
||||||
|
sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)),
|
||||||
|
i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(Constants.TAG, "Colorization failed", e);
|
||||||
|
// if anything goes wrong, then just display the fingerprint without colour,
|
||||||
|
// instead of partially correct colour or wrong colours
|
||||||
|
return new SpannableStringBuilder(fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given bytes to a unique RGB color using SHA1 algorithm
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* @return an integer array containing 3 numeric color representations (Red, Green, Black)
|
||||||
|
* @throws java.security.NoSuchAlgorithmException
|
||||||
|
* @throws java.security.DigestException
|
||||||
|
*/
|
||||||
|
private static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||||
|
|
||||||
|
md.update(bytes);
|
||||||
|
byte[] digest = md.digest();
|
||||||
|
|
||||||
|
int[] result = {((int) digest[0] + 256) % 256,
|
||||||
|
((int) digest[1] + 256) % 256,
|
||||||
|
((int) digest[2] + 256) % 256};
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -577,7 +646,7 @@ public class PgpKeyHelper {
|
|||||||
* @return array with naming (0), email (1), comment (2)
|
* @return array with naming (0), email (1), comment (2)
|
||||||
*/
|
*/
|
||||||
public static String[] splitUserId(String userId) {
|
public static String[] splitUserId(String userId) {
|
||||||
String[] result = new String[] { null, null, null };
|
String[] result = new String[]{null, null, null};
|
||||||
|
|
||||||
if (userId == null || userId.equals("")) {
|
if (userId == null || userId.equals("")) {
|
||||||
return result;
|
return result;
|
||||||
@ -598,7 +667,6 @@ public class PgpKeyHelper {
|
|||||||
result[0] = matcher.group(1);
|
result[0] = matcher.group(1);
|
||||||
result[1] = matcher.group(3);
|
result[1] = matcher.group(3);
|
||||||
result[2] = matcher.group(2);
|
result[2] = matcher.group(2);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -30,35 +30,19 @@ import java.util.Date;
|
|||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import android.content.Context;
|
||||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||||
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
|
||||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||||
import org.spongycastle.jce.spec.ElGamalParameterSpec;
|
import org.spongycastle.jce.spec.ElGamalParameterSpec;
|
||||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
import org.spongycastle.openpgp.*;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
|
||||||
import org.spongycastle.openpgp.PGPKeyPair;
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRingGenerator;
|
|
||||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
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.PGPSignatureSubpacketVector;
|
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
import org.spongycastle.openpgp.PGPUtil;
|
||||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||||
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
|
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.*;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
@ -70,20 +54,34 @@ import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
import org.sufficientlysecure.keychain.util.Primes;
|
||||||
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
public class PgpKeyOperation {
|
public class PgpKeyOperation {
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ProgressDialogUpdater mProgress;
|
private final ProgressDialogUpdater mProgress;
|
||||||
|
|
||||||
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] {
|
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
|
||||||
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
|
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
|
||||||
SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
|
SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
|
||||||
SymmetricKeyAlgorithmTags.TRIPLE_DES };
|
SymmetricKeyAlgorithmTags.TRIPLE_DES};
|
||||||
private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1,
|
private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{HashAlgorithmTags.SHA1,
|
||||||
HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 };
|
HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160};
|
||||||
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] {
|
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
|
||||||
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
|
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
|
||||||
CompressionAlgorithmTags.ZIP };
|
CompressionAlgorithmTags.ZIP};
|
||||||
|
|
||||||
public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
|
public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
|
||||||
super();
|
super();
|
||||||
@ -104,8 +102,9 @@ public class PgpKeyOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
|
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
|
||||||
boolean isMasterKey) throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
|
boolean isMasterKey)
|
||||||
PgpGeneralException, InvalidAlgorithmParameterException {
|
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
|
||||||
|
PgpGeneralException, InvalidAlgorithmParameterException {
|
||||||
|
|
||||||
if (keySize < 512) {
|
if (keySize < 512) {
|
||||||
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
|
throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
|
||||||
@ -119,41 +118,41 @@ public class PgpKeyOperation {
|
|||||||
KeyPairGenerator keyGen;
|
KeyPairGenerator keyGen;
|
||||||
|
|
||||||
switch (algorithmChoice) {
|
switch (algorithmChoice) {
|
||||||
case Id.choice.algorithm.dsa: {
|
case Id.choice.algorithm.dsa: {
|
||||||
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
keyGen.initialize(keySize, new SecureRandom());
|
keyGen.initialize(keySize, new SecureRandom());
|
||||||
algorithm = PGPPublicKey.DSA;
|
algorithm = PGPPublicKey.DSA;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case Id.choice.algorithm.elgamal: {
|
|
||||||
if (isMasterKey) {
|
|
||||||
throw new PgpGeneralException(
|
|
||||||
mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
|
|
||||||
}
|
}
|
||||||
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
||||||
BigInteger p = Primes.getBestPrime(keySize);
|
|
||||||
BigInteger g = new BigInteger("2");
|
|
||||||
|
|
||||||
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
|
case Id.choice.algorithm.elgamal: {
|
||||||
|
if (isMasterKey) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
|
||||||
|
}
|
||||||
|
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
BigInteger p = Primes.getBestPrime(keySize);
|
||||||
|
BigInteger g = new BigInteger("2");
|
||||||
|
|
||||||
keyGen.initialize(elParams);
|
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
|
||||||
algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Id.choice.algorithm.rsa: {
|
keyGen.initialize(elParams);
|
||||||
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
|
||||||
keyGen.initialize(keySize, new SecureRandom());
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
algorithm = PGPPublicKey.RSA_GENERAL;
|
case Id.choice.algorithm.rsa: {
|
||||||
break;
|
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
}
|
keyGen.initialize(keySize, new SecureRandom());
|
||||||
|
|
||||||
default: {
|
algorithm = PGPPublicKey.RSA_GENERAL;
|
||||||
throw new PgpGeneralException(
|
break;
|
||||||
mContext.getString(R.string.error_unknown_algorithm_choice));
|
}
|
||||||
}
|
|
||||||
|
default: {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
mContext.getString(R.string.error_unknown_algorithm_choice));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// build new key pair
|
// build new key pair
|
||||||
@ -170,11 +169,10 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
|
||||||
sha1Calc, isMasterKey, keyEncryptor);
|
sha1Calc, isMasterKey, keyEncryptor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
|
||||||
String newPassPhrase) throws IOException, PGPException {
|
String newPassPhrase) throws IOException, PGPException {
|
||||||
|
|
||||||
updateProgress(R.string.progress_building_key, 0, 100);
|
updateProgress(R.string.progress_building_key, 0, 100);
|
||||||
if (oldPassPhrase == null) {
|
if (oldPassPhrase == null) {
|
||||||
@ -221,15 +219,15 @@ public class PgpKeyOperation {
|
|||||||
updateProgress(R.string.progress_certifying_master_key, 20, 100);
|
updateProgress(R.string.progress_certifying_master_key, 20, 100);
|
||||||
int user_id_index = 0;
|
int user_id_index = 0;
|
||||||
for (String userId : userIds) {
|
for (String userId : userIds) {
|
||||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
|
masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||||
|
|
||||||
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
|
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
|
||||||
|
|
||||||
PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
|
PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
|
||||||
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
|
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
|
||||||
user_id_index++;
|
user_id_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +278,7 @@ public class PgpKeyOperation {
|
|||||||
updateProgress(R.string.progress_adding_sub_keys, 40, 100);
|
updateProgress(R.string.progress_adding_sub_keys, 40, 100);
|
||||||
|
|
||||||
for (int i = 1; i < keys.size(); ++i) {
|
for (int i = 1; i < keys.size(); ++i) {
|
||||||
updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100);
|
updateProgress(40 + 40 * (i - 1) / (keys.size() - 1), 100);
|
||||||
|
|
||||||
PGPSecretKey subKey = keys.get(i);
|
PGPSecretKey subKey = keys.get(i);
|
||||||
PGPPublicKey subPublicKey = subKey.getPublicKey();
|
PGPPublicKey subPublicKey = subKey.getPublicKey();
|
||||||
@ -345,7 +343,7 @@ public class PgpKeyOperation {
|
|||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void buildSecretKey(SaveKeyringParcel saveParcel) throws PgpGeneralException,
|
public void buildSecretKey (SaveKeyringParcel saveParcel)throws PgpGeneralException,
|
||||||
PGPException, SignatureException, IOException {
|
PGPException, SignatureException, IOException {
|
||||||
|
|
||||||
updateProgress(R.string.progress_building_key, 0, 100);
|
updateProgress(R.string.progress_building_key, 0, 100);
|
||||||
@ -387,7 +385,7 @@ public class PgpKeyOperation {
|
|||||||
Todo
|
Todo
|
||||||
identify more things which need to be preserved - e.g. trust levels?
|
identify more things which need to be preserved - e.g. trust levels?
|
||||||
user attributes
|
user attributes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (saveParcel.deletedKeys != null) {
|
if (saveParcel.deletedKeys != null) {
|
||||||
for (PGPSecretKey dKey : saveParcel.deletedKeys) {
|
for (PGPSecretKey dKey : saveParcel.deletedKeys) {
|
||||||
@ -546,7 +544,7 @@ public class PgpKeyOperation {
|
|||||||
unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
|
unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
|
||||||
|
|
||||||
for (int i = 1; i < saveParcel.keys.size(); ++i) {
|
for (int i = 1; i < saveParcel.keys.size(); ++i) {
|
||||||
updateProgress(40 + 50 * i/ saveParcel.keys.size(), 100);
|
updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
|
||||||
if (saveParcel.moddedKeys[i]) {
|
if (saveParcel.moddedKeys[i]) {
|
||||||
PGPSecretKey subKey = saveParcel.keys.get(i);
|
PGPSecretKey subKey = saveParcel.keys.get(i);
|
||||||
PGPPublicKey subPublicKey = subKey.getPublicKey();
|
PGPPublicKey subPublicKey = subKey.getPublicKey();
|
||||||
@ -558,8 +556,8 @@ public class PgpKeyOperation {
|
|||||||
"".toCharArray());
|
"".toCharArray());
|
||||||
} else {
|
} else {
|
||||||
keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
|
keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
saveParcel.oldPassPhrase.toCharArray());
|
saveParcel.oldPassPhrase.toCharArray());
|
||||||
}
|
}
|
||||||
PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
|
PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
|
||||||
PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
|
PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
|
||||||
@ -642,50 +640,80 @@ public class PgpKeyOperation {
|
|||||||
mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
|
mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
|
||||||
updateProgress(R.string.progress_saving_key_ring, 90, 100);
|
updateProgress(R.string.progress_saving_key_ring, 90, 100);
|
||||||
|
|
||||||
|
/* additional handy debug info
|
||||||
|
|
||||||
|
Log.d(Constants.TAG, " ------- in private key -------");
|
||||||
|
|
||||||
|
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
|
||||||
|
for(PGPSignature sig : new IterableIterator<PGPSignature>(secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
|
||||||
|
Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(Constants.TAG, " ------- in public key -------");
|
||||||
|
|
||||||
|
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
|
||||||
|
for(PGPSignature sig : new IterableIterator<PGPSignature>(publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
|
||||||
|
Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
ProviderHelper.saveKeyRing(mContext, mKR);
|
ProviderHelper.saveKeyRing(mContext, mKR);
|
||||||
ProviderHelper.saveKeyRing(mContext, pKR);
|
ProviderHelper.saveKeyRing(mContext, pKR);
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, String passphrase)
|
public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
|
||||||
throws PgpGeneralException, PGPException, SignatureException {
|
throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
|
PGPException, SignatureException {
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
throw new PgpGeneralException("Unable to obtain passphrase");
|
throw new PgpGeneralException("Unable to obtain passphrase");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
||||||
|
PGPSignatureGenerator signatureGenerator; {
|
||||||
|
|
||||||
|
PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
|
||||||
|
if (certificationKey == null) {
|
||||||
|
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
||||||
|
}
|
||||||
|
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||||
|
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
|
||||||
|
if (signaturePrivateKey == null) {
|
||||||
|
throw new PgpGeneralException(
|
||||||
|
mContext.getString(R.string.error_could_not_extract_private_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: SHA256 fixed?
|
||||||
|
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
|
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
||||||
|
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // supply signatureGenerator with a SubpacketVector
|
||||||
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
||||||
|
signatureGenerator.setHashedSubpackets(packetVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch public key ring, add the certification and return it
|
||||||
PGPPublicKeyRing pubring = ProviderHelper
|
PGPPublicKeyRing pubring = ProviderHelper
|
||||||
.getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
|
.getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
|
||||||
|
PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
|
||||||
PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
|
for(String userId : new IterableIterator<String>(userIds.iterator())) {
|
||||||
if (certificationKey == null) {
|
PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
|
||||||
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
|
||||||
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
|
|
||||||
if (signaturePrivateKey == null) {
|
|
||||||
throw new PgpGeneralException(
|
|
||||||
mContext.getString(R.string.error_could_not_extract_private_key));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: SHA256 fixed?
|
|
||||||
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
|
||||||
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
|
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
||||||
|
|
||||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
|
|
||||||
contentSignerBuilder);
|
|
||||||
|
|
||||||
signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey);
|
|
||||||
|
|
||||||
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
|
||||||
|
|
||||||
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
|
||||||
signatureGenerator.setHashedSubpackets(packetVector);
|
|
||||||
|
|
||||||
PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId),
|
|
||||||
signatureGenerator.generate());
|
|
||||||
pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
|
pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
|
||||||
|
|
||||||
return pubring;
|
return pubring;
|
||||||
|
@ -18,28 +18,11 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.spongycastle.bcpg.BCPGOutputStream;
|
import org.spongycastle.bcpg.BCPGOutputStream;
|
||||||
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
|
import org.spongycastle.openpgp.*;
|
||||||
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.PBESecretKeyDecryptor;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
import org.spongycastle.openpgp.operator.jcajce.*;
|
||||||
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.Constants;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
@ -49,11 +32,7 @@ import org.sufficientlysecure.keychain.util.InputData;
|
|||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
@ -63,110 +42,110 @@ import java.util.Date;
|
|||||||
* This class uses a Builder pattern!
|
* This class uses a Builder pattern!
|
||||||
*/
|
*/
|
||||||
public class PgpSignEncrypt {
|
public class PgpSignEncrypt {
|
||||||
private Context context;
|
private Context mContext;
|
||||||
private InputData data;
|
private InputData mData;
|
||||||
private OutputStream outStream;
|
private OutputStream mOutStream;
|
||||||
|
|
||||||
private ProgressDialogUpdater progress;
|
private ProgressDialogUpdater mProgress;
|
||||||
private boolean enableAsciiArmorOutput;
|
private boolean mEnableAsciiArmorOutput;
|
||||||
private int compressionId;
|
private int mCompressionId;
|
||||||
private long[] encryptionKeyIds;
|
private long[] mEncryptionKeyIds;
|
||||||
private String encryptionPassphrase;
|
private String mEncryptionPassphrase;
|
||||||
private int symmetricEncryptionAlgorithm;
|
private int mSymmetricEncryptionAlgorithm;
|
||||||
private long signatureKeyId;
|
private long mSignatureKeyId;
|
||||||
private int signatureHashAlgorithm;
|
private int mSignatureHashAlgorithm;
|
||||||
private boolean signatureForceV3;
|
private boolean mSignatureForceV3;
|
||||||
private String signaturePassphrase;
|
private String mSignaturePassphrase;
|
||||||
|
|
||||||
private PgpSignEncrypt(Builder builder) {
|
private PgpSignEncrypt(Builder builder) {
|
||||||
// private Constructor can only be called from Builder
|
// private Constructor can only be called from Builder
|
||||||
this.context = builder.context;
|
this.mContext = builder.mContext;
|
||||||
this.data = builder.data;
|
this.mData = builder.mData;
|
||||||
this.outStream = builder.outStream;
|
this.mOutStream = builder.mOutStream;
|
||||||
|
|
||||||
this.progress = builder.progress;
|
this.mProgress = builder.mProgress;
|
||||||
this.enableAsciiArmorOutput = builder.enableAsciiArmorOutput;
|
this.mEnableAsciiArmorOutput = builder.mEnableAsciiArmorOutput;
|
||||||
this.compressionId = builder.compressionId;
|
this.mCompressionId = builder.mCompressionId;
|
||||||
this.encryptionKeyIds = builder.encryptionKeyIds;
|
this.mEncryptionKeyIds = builder.mEncryptionKeyIds;
|
||||||
this.encryptionPassphrase = builder.encryptionPassphrase;
|
this.mEncryptionPassphrase = builder.mEncryptionPassphrase;
|
||||||
this.symmetricEncryptionAlgorithm = builder.symmetricEncryptionAlgorithm;
|
this.mSymmetricEncryptionAlgorithm = builder.mSymmetricEncryptionAlgorithm;
|
||||||
this.signatureKeyId = builder.signatureKeyId;
|
this.mSignatureKeyId = builder.mSignatureKeyId;
|
||||||
this.signatureHashAlgorithm = builder.signatureHashAlgorithm;
|
this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
|
||||||
this.signatureForceV3 = builder.signatureForceV3;
|
this.mSignatureForceV3 = builder.mSignatureForceV3;
|
||||||
this.signaturePassphrase = builder.signaturePassphrase;
|
this.mSignaturePassphrase = builder.mSignaturePassphrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
// mandatory parameter
|
// mandatory parameter
|
||||||
private Context context;
|
private Context mContext;
|
||||||
private InputData data;
|
private InputData mData;
|
||||||
private OutputStream outStream;
|
private OutputStream mOutStream;
|
||||||
|
|
||||||
// optional
|
// optional
|
||||||
private ProgressDialogUpdater progress = null;
|
private ProgressDialogUpdater mProgress = null;
|
||||||
private boolean enableAsciiArmorOutput = false;
|
private boolean mEnableAsciiArmorOutput = false;
|
||||||
private int compressionId = Id.choice.compression.none;
|
private int mCompressionId = Id.choice.compression.none;
|
||||||
private long[] encryptionKeyIds = new long[0];
|
private long[] mEncryptionKeyIds = new long[0];
|
||||||
private String encryptionPassphrase = null;
|
private String mEncryptionPassphrase = null;
|
||||||
private int symmetricEncryptionAlgorithm = 0;
|
private int mSymmetricEncryptionAlgorithm = 0;
|
||||||
private long signatureKeyId = Id.key.none;
|
private long mSignatureKeyId = Id.key.none;
|
||||||
private int signatureHashAlgorithm = 0;
|
private int mSignatureHashAlgorithm = 0;
|
||||||
private boolean signatureForceV3 = false;
|
private boolean mSignatureForceV3 = false;
|
||||||
private String signaturePassphrase = null;
|
private String mSignaturePassphrase = null;
|
||||||
|
|
||||||
public Builder(Context context, InputData data, OutputStream outStream) {
|
public Builder(Context context, InputData data, OutputStream outStream) {
|
||||||
this.context = context;
|
this.mContext = context;
|
||||||
this.data = data;
|
this.mData = data;
|
||||||
this.outStream = outStream;
|
this.mOutStream = outStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder progress(ProgressDialogUpdater progress) {
|
public Builder progress(ProgressDialogUpdater progress) {
|
||||||
this.progress = progress;
|
this.mProgress = progress;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder enableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
|
public Builder enableAsciiArmorOutput(boolean enableAsciiArmorOutput) {
|
||||||
this.enableAsciiArmorOutput = enableAsciiArmorOutput;
|
this.mEnableAsciiArmorOutput = enableAsciiArmorOutput;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder compressionId(int compressionId) {
|
public Builder compressionId(int compressionId) {
|
||||||
this.compressionId = compressionId;
|
this.mCompressionId = compressionId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder encryptionKeyIds(long[] encryptionKeyIds) {
|
public Builder encryptionKeyIds(long[] encryptionKeyIds) {
|
||||||
this.encryptionKeyIds = encryptionKeyIds;
|
this.mEncryptionKeyIds = encryptionKeyIds;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder encryptionPassphrase(String encryptionPassphrase) {
|
public Builder encryptionPassphrase(String encryptionPassphrase) {
|
||||||
this.encryptionPassphrase = encryptionPassphrase;
|
this.mEncryptionPassphrase = encryptionPassphrase;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder symmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
|
public Builder symmetricEncryptionAlgorithm(int symmetricEncryptionAlgorithm) {
|
||||||
this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
|
this.mSymmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder signatureKeyId(long signatureKeyId) {
|
public Builder signatureKeyId(long signatureKeyId) {
|
||||||
this.signatureKeyId = signatureKeyId;
|
this.mSignatureKeyId = signatureKeyId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder signatureHashAlgorithm(int signatureHashAlgorithm) {
|
public Builder signatureHashAlgorithm(int signatureHashAlgorithm) {
|
||||||
this.signatureHashAlgorithm = signatureHashAlgorithm;
|
this.mSignatureHashAlgorithm = signatureHashAlgorithm;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder signatureForceV3(boolean signatureForceV3) {
|
public Builder signatureForceV3(boolean signatureForceV3) {
|
||||||
this.signatureForceV3 = signatureForceV3;
|
this.mSignatureForceV3 = signatureForceV3;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder signaturePassphrase(String signaturePassphrase) {
|
public Builder signaturePassphrase(String signaturePassphrase) {
|
||||||
this.signaturePassphrase = signaturePassphrase;
|
this.mSignaturePassphrase = signaturePassphrase;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,14 +155,14 @@ public class PgpSignEncrypt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgress(int message, int current, int total) {
|
public void updateProgress(int message, int current, int total) {
|
||||||
if (progress != null) {
|
if (mProgress != null) {
|
||||||
progress.setProgress(message, current, total);
|
mProgress.setProgress(message, current, total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateProgress(int current, int total) {
|
public void updateProgress(int current, int total) {
|
||||||
if (progress != null) {
|
if (mProgress != null) {
|
||||||
progress.setProgress(current, total);
|
mProgress.setProgress(current, total);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,17 +180,17 @@ public class PgpSignEncrypt {
|
|||||||
throws IOException, PgpGeneralException, PGPException, NoSuchProviderException,
|
throws IOException, PgpGeneralException, PGPException, NoSuchProviderException,
|
||||||
NoSuchAlgorithmException, SignatureException {
|
NoSuchAlgorithmException, SignatureException {
|
||||||
|
|
||||||
boolean enableSignature = signatureKeyId != Id.key.none;
|
boolean enableSignature = mSignatureKeyId != Id.key.none;
|
||||||
boolean enableEncryption = (encryptionKeyIds.length != 0 || encryptionPassphrase != null);
|
boolean enableEncryption = (mEncryptionKeyIds.length != 0 || mEncryptionPassphrase != null);
|
||||||
boolean enableCompression = (enableEncryption && compressionId != Id.choice.compression.none);
|
boolean enableCompression = (enableEncryption && mCompressionId != Id.choice.compression.none);
|
||||||
|
|
||||||
Log.d(Constants.TAG, "enableSignature:" + enableSignature
|
Log.d(Constants.TAG, "enableSignature:" + enableSignature
|
||||||
+ "\nenableEncryption:" + enableEncryption
|
+ "\nenableEncryption:" + enableEncryption
|
||||||
+ "\nenableCompression:" + enableCompression
|
+ "\nenableCompression:" + enableCompression
|
||||||
+ "\nenableAsciiArmorOutput:" + enableAsciiArmorOutput);
|
+ "\nenableAsciiArmorOutput:" + mEnableAsciiArmorOutput);
|
||||||
|
|
||||||
int signatureType;
|
int signatureType;
|
||||||
if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
|
if (mEnableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
|
||||||
// for sign-only ascii text
|
// for sign-only ascii text
|
||||||
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
|
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
|
||||||
} else {
|
} else {
|
||||||
@ -220,12 +199,12 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
ArmoredOutputStream armorOut = null;
|
ArmoredOutputStream armorOut = null;
|
||||||
OutputStream out;
|
OutputStream out;
|
||||||
if (enableAsciiArmorOutput) {
|
if (mEnableAsciiArmorOutput) {
|
||||||
armorOut = new ArmoredOutputStream(outStream);
|
armorOut = new ArmoredOutputStream(mOutStream);
|
||||||
armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
|
armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||||
out = armorOut;
|
out = armorOut;
|
||||||
} else {
|
} else {
|
||||||
out = outStream;
|
out = mOutStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get keys for signature generation for later usage */
|
/* Get keys for signature generation for later usage */
|
||||||
@ -233,25 +212,25 @@ public class PgpSignEncrypt {
|
|||||||
PGPSecretKeyRing signingKeyRing = null;
|
PGPSecretKeyRing signingKeyRing = null;
|
||||||
PGPPrivateKey signaturePrivateKey = null;
|
PGPPrivateKey signaturePrivateKey = null;
|
||||||
if (enableSignature) {
|
if (enableSignature) {
|
||||||
signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
|
signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
|
||||||
signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
|
signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
|
||||||
if (signingKey == null) {
|
if (signingKey == null) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
|
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signaturePassphrase == null) {
|
if (mSignaturePassphrase == null) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
context.getString(R.string.error_no_signature_passphrase));
|
mContext.getString(R.string.error_no_signature_passphrase));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
|
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
|
||||||
|
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
|
||||||
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
||||||
if (signaturePrivateKey == null) {
|
if (signaturePrivateKey == null) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
context.getString(R.string.error_could_not_extract_private_key));
|
mContext.getString(R.string.error_could_not_extract_private_key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateProgress(R.string.progress_preparing_streams, 5, 100);
|
updateProgress(R.string.progress_preparing_streams, 5, 100);
|
||||||
@ -261,23 +240,23 @@ public class PgpSignEncrypt {
|
|||||||
if (enableEncryption) {
|
if (enableEncryption) {
|
||||||
// has Integrity packet enabled!
|
// has Integrity packet enabled!
|
||||||
JcePGPDataEncryptorBuilder encryptorBuilder =
|
JcePGPDataEncryptorBuilder encryptorBuilder =
|
||||||
new JcePGPDataEncryptorBuilder(symmetricEncryptionAlgorithm)
|
new JcePGPDataEncryptorBuilder(mSymmetricEncryptionAlgorithm)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME)
|
||||||
.setWithIntegrityPacket(true);
|
.setWithIntegrityPacket(true);
|
||||||
|
|
||||||
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
|
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
|
||||||
|
|
||||||
if (encryptionKeyIds.length == 0) {
|
if (mEncryptionKeyIds.length == 0) {
|
||||||
// Symmetric encryption
|
// Symmetric encryption
|
||||||
Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
|
Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
|
||||||
|
|
||||||
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
|
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
|
||||||
new JcePBEKeyEncryptionMethodGenerator(encryptionPassphrase.toCharArray());
|
new JcePBEKeyEncryptionMethodGenerator(mEncryptionPassphrase.toCharArray());
|
||||||
cPk.addMethod(symmetricEncryptionGenerator);
|
cPk.addMethod(symmetricEncryptionGenerator);
|
||||||
} else {
|
} else {
|
||||||
// Asymmetric encryption
|
// Asymmetric encryption
|
||||||
for (long id : encryptionKeyIds) {
|
for (long id : mEncryptionKeyIds) {
|
||||||
PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(context, id);
|
PGPPublicKey key = PgpKeyHelper.getEncryptPublicKey(mContext, id);
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
|
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
|
||||||
new JcePublicKeyKeyEncryptionMethodGenerator(key);
|
new JcePublicKeyKeyEncryptionMethodGenerator(key);
|
||||||
@ -295,10 +274,10 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
// content signer based on signing key algorithm and chosen hash algorithm
|
// content signer based on signing key algorithm and chosen hash algorithm
|
||||||
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
|
signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
|
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
|
||||||
signatureV3Generator.init(signatureType, signaturePrivateKey);
|
signatureV3Generator.init(signatureType, signaturePrivateKey);
|
||||||
} else {
|
} else {
|
||||||
@ -322,14 +301,14 @@ public class PgpSignEncrypt {
|
|||||||
encryptionOut = cPk.open(out, new byte[1 << 16]);
|
encryptionOut = cPk.open(out, new byte[1 << 16]);
|
||||||
|
|
||||||
if (enableCompression) {
|
if (enableCompression) {
|
||||||
compressGen = new PGPCompressedDataGenerator(compressionId);
|
compressGen = new PGPCompressedDataGenerator(mCompressionId);
|
||||||
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
|
bcpgOut = new BCPGOutputStream(compressGen.open(encryptionOut));
|
||||||
} else {
|
} else {
|
||||||
bcpgOut = new BCPGOutputStream(encryptionOut);
|
bcpgOut = new BCPGOutputStream(encryptionOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignature) {
|
if (enableSignature) {
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
|
signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
|
||||||
} else {
|
} else {
|
||||||
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
|
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
|
||||||
@ -345,13 +324,13 @@ public class PgpSignEncrypt {
|
|||||||
long progress = 0;
|
long progress = 0;
|
||||||
int n;
|
int n;
|
||||||
byte[] buffer = new byte[1 << 16];
|
byte[] buffer = new byte[1 << 16];
|
||||||
InputStream in = data.getInputStream();
|
InputStream in = mData.getInputStream();
|
||||||
while ((n = in.read(buffer)) > 0) {
|
while ((n = in.read(buffer)) > 0) {
|
||||||
pOut.write(buffer, 0, n);
|
pOut.write(buffer, 0, n);
|
||||||
|
|
||||||
// update signature buffer if signature is requested
|
// update signature buffer if signature is requested
|
||||||
if (enableSignature) {
|
if (enableSignature) {
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator.update(buffer, 0, n);
|
signatureV3Generator.update(buffer, 0, n);
|
||||||
} else {
|
} else {
|
||||||
signatureGenerator.update(buffer, 0, n);
|
signatureGenerator.update(buffer, 0, n);
|
||||||
@ -359,26 +338,26 @@ public class PgpSignEncrypt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress += n;
|
progress += n;
|
||||||
if (data.getSize() != 0) {
|
if (mData.getSize() != 0) {
|
||||||
updateProgress((int) (20 + (95 - 20) * progress / data.getSize()), 100);
|
updateProgress((int) (20 + (95 - 20) * progress / mData.getSize()), 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
literalGen.close();
|
literalGen.close();
|
||||||
} else if (enableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
|
} else if (mEnableAsciiArmorOutput && enableSignature && !enableEncryption && !enableCompression) {
|
||||||
/* sign-only of ascii text */
|
/* sign-only of ascii text */
|
||||||
|
|
||||||
updateProgress(R.string.progress_signing, 40, 100);
|
updateProgress(R.string.progress_signing, 40, 100);
|
||||||
|
|
||||||
// write directly on armor output stream
|
// write directly on armor output stream
|
||||||
armorOut.beginClearText(signatureHashAlgorithm);
|
armorOut.beginClearText(mSignatureHashAlgorithm);
|
||||||
|
|
||||||
InputStream in = data.getInputStream();
|
InputStream in = mData.getInputStream();
|
||||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||||
|
|
||||||
final byte[] newline = "\r\n".getBytes("UTF-8");
|
final byte[] newline = "\r\n".getBytes("UTF-8");
|
||||||
|
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
processLine(reader.readLine(), armorOut, signatureV3Generator);
|
processLine(reader.readLine(), armorOut, signatureV3Generator);
|
||||||
} else {
|
} else {
|
||||||
processLine(reader.readLine(), armorOut, signatureGenerator);
|
processLine(reader.readLine(), armorOut, signatureGenerator);
|
||||||
@ -395,7 +374,7 @@ public class PgpSignEncrypt {
|
|||||||
armorOut.write(newline);
|
armorOut.write(newline);
|
||||||
|
|
||||||
// update signature buffer with input line
|
// update signature buffer with input line
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator.update(newline);
|
signatureV3Generator.update(newline);
|
||||||
processLine(line, armorOut, signatureV3Generator);
|
processLine(line, armorOut, signatureV3Generator);
|
||||||
} else {
|
} else {
|
||||||
@ -415,7 +394,7 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
if (enableSignature) {
|
if (enableSignature) {
|
||||||
updateProgress(R.string.progress_generating_signature, 95, 100);
|
updateProgress(R.string.progress_generating_signature, 95, 100);
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator.generate().encode(pOut);
|
signatureV3Generator.generate().encode(pOut);
|
||||||
} else {
|
} else {
|
||||||
signatureGenerator.generate().encode(pOut);
|
signatureGenerator.generate().encode(pOut);
|
||||||
@ -432,12 +411,12 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
encryptionOut.close();
|
encryptionOut.close();
|
||||||
}
|
}
|
||||||
if (enableAsciiArmorOutput) {
|
if (mEnableAsciiArmorOutput) {
|
||||||
armorOut.close();
|
armorOut.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
out.close();
|
out.close();
|
||||||
outStream.close();
|
mOutStream.close();
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
}
|
}
|
||||||
@ -449,35 +428,36 @@ public class PgpSignEncrypt {
|
|||||||
SignatureException {
|
SignatureException {
|
||||||
|
|
||||||
OutputStream out;
|
OutputStream out;
|
||||||
if (enableAsciiArmorOutput) {
|
if (mEnableAsciiArmorOutput) {
|
||||||
// Ascii Armor (Radix-64)
|
// Ascii Armor (Radix-64)
|
||||||
ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
|
ArmoredOutputStream armorOut = new ArmoredOutputStream(mOutStream);
|
||||||
armorOut.setHeader("Version", PgpHelper.getFullVersion(context));
|
armorOut.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
||||||
out = armorOut;
|
out = armorOut;
|
||||||
} else {
|
} else {
|
||||||
out = outStream;
|
out = mOutStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signatureKeyId == 0) {
|
if (mSignatureKeyId == 0) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_no_signature_key));
|
throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSecretKeyRing signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId);
|
PGPSecretKeyRing signingKeyRing =
|
||||||
PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(context, signatureKeyId);
|
ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
|
||||||
|
PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
|
||||||
if (signingKey == null) {
|
if (signingKey == null) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_signature_failed));
|
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signaturePassphrase == null) {
|
if (mSignaturePassphrase == null) {
|
||||||
throw new PgpGeneralException(context.getString(R.string.error_no_signature_passphrase));
|
throw new PgpGeneralException(mContext.getString(R.string.error_no_signature_passphrase));
|
||||||
}
|
}
|
||||||
|
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray());
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
|
||||||
PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
|
||||||
if (signaturePrivateKey == null) {
|
if (signaturePrivateKey == null) {
|
||||||
throw new PgpGeneralException(
|
throw new PgpGeneralException(
|
||||||
context.getString(R.string.error_could_not_extract_private_key));
|
mContext.getString(R.string.error_could_not_extract_private_key));
|
||||||
}
|
}
|
||||||
updateProgress(R.string.progress_preparing_streams, 0, 100);
|
updateProgress(R.string.progress_preparing_streams, 0, 100);
|
||||||
|
|
||||||
@ -490,12 +470,12 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
// content signer based on signing key algorithm and chosen hash algorithm
|
// content signer based on signing key algorithm and chosen hash algorithm
|
||||||
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
|
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey
|
||||||
.getPublicKey().getAlgorithm(), signatureHashAlgorithm)
|
.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
|
||||||
PGPSignatureGenerator signatureGenerator = null;
|
PGPSignatureGenerator signatureGenerator = null;
|
||||||
PGPV3SignatureGenerator signatureV3Generator = null;
|
PGPV3SignatureGenerator signatureV3Generator = null;
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
|
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
|
||||||
signatureV3Generator.init(type, signaturePrivateKey);
|
signatureV3Generator.init(type, signaturePrivateKey);
|
||||||
} else {
|
} else {
|
||||||
@ -510,7 +490,7 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
updateProgress(R.string.progress_signing, 40, 100);
|
updateProgress(R.string.progress_signing, 40, 100);
|
||||||
|
|
||||||
InputStream inStream = data.getInputStream();
|
InputStream inStream = mData.getInputStream();
|
||||||
// if (binary) {
|
// if (binary) {
|
||||||
// byte[] buffer = new byte[1 << 16];
|
// byte[] buffer = new byte[1 << 16];
|
||||||
// int n = 0;
|
// int n = 0;
|
||||||
@ -527,7 +507,7 @@ public class PgpSignEncrypt {
|
|||||||
|
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
processLine(line, null, signatureV3Generator);
|
processLine(line, null, signatureV3Generator);
|
||||||
signatureV3Generator.update(newline);
|
signatureV3Generator.update(newline);
|
||||||
} else {
|
} else {
|
||||||
@ -538,13 +518,13 @@ public class PgpSignEncrypt {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
BCPGOutputStream bOut = new BCPGOutputStream(out);
|
BCPGOutputStream bOut = new BCPGOutputStream(out);
|
||||||
if (signatureForceV3) {
|
if (mSignatureForceV3) {
|
||||||
signatureV3Generator.generate().encode(bOut);
|
signatureV3Generator.generate().encode(bOut);
|
||||||
} else {
|
} else {
|
||||||
signatureGenerator.generate().encode(bOut);
|
signatureGenerator.generate().encode(bOut);
|
||||||
}
|
}
|
||||||
out.close();
|
out.close();
|
||||||
outStream.close();
|
mOutStream.close();
|
||||||
|
|
||||||
updateProgress(R.string.progress_done, 100, 100);
|
updateProgress(R.string.progress_done, 100, 100);
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import javax.security.auth.callback.Callback;
|
|
||||||
import javax.security.auth.callback.CallbackHandler;
|
|
||||||
import javax.security.auth.callback.PasswordCallback;
|
|
||||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
|
||||||
|
|
||||||
import org.spongycastle.asn1.DERObjectIdentifier;
|
import org.spongycastle.asn1.DERObjectIdentifier;
|
||||||
import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
|
import org.spongycastle.asn1.x509.*;
|
||||||
import org.spongycastle.asn1.x509.BasicConstraints;
|
|
||||||
import org.spongycastle.asn1.x509.GeneralName;
|
|
||||||
import org.spongycastle.asn1.x509.GeneralNames;
|
|
||||||
import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
|
|
||||||
import org.spongycastle.asn1.x509.X509Extensions;
|
|
||||||
import org.spongycastle.asn1.x509.X509Name;
|
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
@ -38,9 +29,23 @@ import org.spongycastle.x509.extension.SubjectKeyIdentifierStructure;
|
|||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import javax.security.auth.callback.Callback;
|
||||||
|
import javax.security.auth.callback.CallbackHandler;
|
||||||
|
import javax.security.auth.callback.PasswordCallback;
|
||||||
|
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
public class PgpToX509 {
|
public class PgpToX509 {
|
||||||
public final static String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge";
|
public static final String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge";
|
||||||
public final static String DN_COMMON_PART_OU = "OpenPGP Keychain cert";
|
public static final String DN_COMMON_PART_OU = "OpenPGP Keychain cert";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a self-signed certificate from a public and private key. The (critical) key-usage
|
* Creates a self-signed certificate from a public and private key. The (critical) key-usage
|
||||||
@ -48,20 +53,14 @@ public class PgpToX509 {
|
|||||||
* and certificate-signing. The (non-critical) Netscape extension is set up with: SSL client and
|
* and certificate-signing. The (non-critical) Netscape extension is set up with: SSL client and
|
||||||
* S/MIME. A URI subjectAltName may also be set up.
|
* S/MIME. A URI subjectAltName may also be set up.
|
||||||
*
|
*
|
||||||
* @param pubKey
|
* @param pubKey public key
|
||||||
* public key
|
* @param privKey private key
|
||||||
* @param privKey
|
* @param subject subject (and issuer) DN for this certificate, RFC 2253 format preferred.
|
||||||
* private key
|
* @param startDate date from which the certificate will be valid (defaults to current date and time
|
||||||
* @param subject
|
* if null)
|
||||||
* subject (and issuer) DN for this certificate, RFC 2253 format preferred.
|
* @param endDate date until which the certificate will be valid (defaults to current date and time
|
||||||
* @param startDate
|
* if null) *
|
||||||
* date from which the certificate will be valid (defaults to current date and time
|
* @param subjAltNameURI URI to be placed in subjectAltName
|
||||||
* if null)
|
|
||||||
* @param endDate
|
|
||||||
* date until which the certificate will be valid (defaults to current date and time
|
|
||||||
* if null) *
|
|
||||||
* @param subjAltNameURI
|
|
||||||
* URI to be placed in subjectAltName
|
|
||||||
* @return self-signed certificate
|
* @return self-signed certificate
|
||||||
* @throws InvalidKeyException
|
* @throws InvalidKeyException
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
@ -70,11 +69,10 @@ public class PgpToX509 {
|
|||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
* @throws CertificateException
|
* @throws CertificateException
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*
|
|
||||||
* @author Bruno Harbulot
|
* @author Bruno Harbulot
|
||||||
*/
|
*/
|
||||||
public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey,
|
public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey,
|
||||||
X509Name subject, Date startDate, Date endDate, String subjAltNameURI)
|
X509Name subject, Date startDate, Date endDate, String subjAltNameURI)
|
||||||
throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
|
throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
|
||||||
SignatureException, CertificateException, NoSuchProviderException {
|
SignatureException, CertificateException, NoSuchProviderException {
|
||||||
|
|
||||||
@ -172,14 +170,11 @@ public class PgpToX509 {
|
|||||||
/**
|
/**
|
||||||
* Creates a self-signed certificate from a PGP Secret Key.
|
* Creates a self-signed certificate from a PGP Secret Key.
|
||||||
*
|
*
|
||||||
* @param pgpSecKey
|
* @param pgpSecKey PGP Secret Key (from which one can extract the public and private keys and other
|
||||||
* PGP Secret Key (from which one can extract the public and private keys and other
|
* attributes).
|
||||||
* attributes).
|
* @param pgpPrivKey PGP Private Key corresponding to the Secret Key (password callbacks should be done
|
||||||
* @param pgpPrivKey
|
* before calling this method)
|
||||||
* PGP Private Key corresponding to the Secret Key (password callbacks should be done
|
* @param subjAltNameURI optional URI to embed in the subject alternative-name
|
||||||
* before calling this method)
|
|
||||||
* @param subjAltNameURI
|
|
||||||
* optional URI to embed in the subject alternative-name
|
|
||||||
* @return self-signed certificate
|
* @return self-signed certificate
|
||||||
* @throws PGPException
|
* @throws PGPException
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
@ -187,11 +182,10 @@ public class PgpToX509 {
|
|||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
* @throws CertificateException
|
* @throws CertificateException
|
||||||
*
|
|
||||||
* @author Bruno Harbulot
|
* @author Bruno Harbulot
|
||||||
*/
|
*/
|
||||||
public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey,
|
public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey,
|
||||||
PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException,
|
PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException,
|
||||||
NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
|
NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
|
||||||
SignatureException, CertificateException {
|
SignatureException, CertificateException {
|
||||||
// get public key from secret key
|
// get public key from secret key
|
||||||
@ -213,7 +207,7 @@ public class PgpToX509 {
|
|||||||
x509NameValues.add(DN_COMMON_PART_OU);
|
x509NameValues.add(DN_COMMON_PART_OU);
|
||||||
|
|
||||||
for (@SuppressWarnings("unchecked")
|
for (@SuppressWarnings("unchecked")
|
||||||
Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserIDs(); it.hasNext();) {
|
Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserIDs(); it.hasNext(); ) {
|
||||||
Object attrib = it.next();
|
Object attrib = it.next();
|
||||||
x509NameOids.add(X509Name.CN);
|
x509NameOids.add(X509Name.CN);
|
||||||
x509NameValues.add("CryptoCall");
|
x509NameValues.add("CryptoCall");
|
||||||
@ -225,7 +219,7 @@ public class PgpToX509 {
|
|||||||
*/
|
*/
|
||||||
Log.d(Constants.TAG, "User attributes: ");
|
Log.d(Constants.TAG, "User attributes: ");
|
||||||
for (@SuppressWarnings("unchecked")
|
for (@SuppressWarnings("unchecked")
|
||||||
Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserAttributes(); it.hasNext();) {
|
Iterator<Object> it = (Iterator<Object>) pgpSecKey.getUserAttributes(); it.hasNext(); ) {
|
||||||
Object attrib = it.next();
|
Object attrib = it.next();
|
||||||
Log.d(Constants.TAG, " - " + attrib + " -- " + attrib.getClass());
|
Log.d(Constants.TAG, " - " + attrib + " -- " + attrib.getClass());
|
||||||
}
|
}
|
||||||
@ -263,12 +257,11 @@ public class PgpToX509 {
|
|||||||
* allow passwords to be stored within your application.
|
* allow passwords to be stored within your application.
|
||||||
*
|
*
|
||||||
* @author Bruno Harbulot.
|
* @author Bruno Harbulot.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public final static class PredefinedPasswordCallbackHandler implements CallbackHandler {
|
public static final class PredefinedPasswordCallbackHandler implements CallbackHandler {
|
||||||
|
|
||||||
private char[] password;
|
private char[] mPassword;
|
||||||
private String prompt;
|
private String mPrompt;
|
||||||
|
|
||||||
public PredefinedPasswordCallbackHandler(String password) {
|
public PredefinedPasswordCallbackHandler(String password) {
|
||||||
this(password == null ? null : password.toCharArray(), null);
|
this(password == null ? null : password.toCharArray(), null);
|
||||||
@ -283,16 +276,16 @@ public class PgpToX509 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PredefinedPasswordCallbackHandler(char[] password, String prompt) {
|
public PredefinedPasswordCallbackHandler(char[] password, String prompt) {
|
||||||
this.password = password;
|
this.mPassword = password;
|
||||||
this.prompt = prompt;
|
this.mPrompt = prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||||
for (Callback callback : callbacks) {
|
for (Callback callback : callbacks) {
|
||||||
if (callback instanceof PasswordCallback) {
|
if (callback instanceof PasswordCallback) {
|
||||||
PasswordCallback pwCallback = (PasswordCallback) callback;
|
PasswordCallback pwCallback = (PasswordCallback) callback;
|
||||||
if ((this.prompt == null) || (this.prompt.equals(pwCallback.getPrompt()))) {
|
if ((this.mPrompt == null) || (this.mPrompt.equals(pwCallback.getPrompt()))) {
|
||||||
pwCallback.setPassword(this.password);
|
pwCallback.setPassword(this.mPassword);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedCallbackException(callback, "Unrecognised callback.");
|
throw new UnsupportedCallbackException(callback, "Unrecognised callback.");
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2013 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.exception;
|
package org.sufficientlysecure.keychain.pgp.exception;
|
||||||
|
|
||||||
public class NoAsymmetricEncryptionException extends Exception {
|
public class NoAsymmetricEncryptionException extends Exception {
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2013 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.exception;
|
package org.sufficientlysecure.keychain.pgp.exception;
|
||||||
|
|
||||||
public class PgpGeneralException extends Exception {
|
public class PgpGeneralException extends Exception {
|
||||||
|
@ -17,11 +17,11 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.provider;
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
|
||||||
public class KeychainContract {
|
public class KeychainContract {
|
||||||
|
|
||||||
interface KeyRingsColumns {
|
interface KeyRingsColumns {
|
||||||
@ -57,10 +57,15 @@ public class KeychainContract {
|
|||||||
interface ApiAppsColumns {
|
interface ApiAppsColumns {
|
||||||
String PACKAGE_NAME = "package_name";
|
String PACKAGE_NAME = "package_name";
|
||||||
String PACKAGE_SIGNATURE = "package_signature";
|
String PACKAGE_SIGNATURE = "package_signature";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiAppsAccountsColumns {
|
||||||
|
String ACCOUNT_NAME = "account_name";
|
||||||
String KEY_ID = "key_id"; // not a database id
|
String KEY_ID = "key_id"; // not a database id
|
||||||
String ENCRYPTION_ALGORITHM = "encryption_algorithm";
|
String ENCRYPTION_ALGORITHM = "encryption_algorithm";
|
||||||
String HASH_ALORITHM = "hash_algorithm";
|
String HASH_ALORITHM = "hash_algorithm";
|
||||||
String COMPRESSION = "compression";
|
String COMPRESSION = "compression";
|
||||||
|
String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class KeyTypes {
|
public static final class KeyTypes {
|
||||||
@ -88,18 +93,26 @@ public class KeychainContract {
|
|||||||
public static final String PATH_KEYS = "keys";
|
public static final String PATH_KEYS = "keys";
|
||||||
|
|
||||||
public static final String BASE_API_APPS = "api_apps";
|
public static final String BASE_API_APPS = "api_apps";
|
||||||
public static final String PATH_BY_PACKAGE_NAME = "package_name";
|
public static final String PATH_ACCOUNTS = "accounts";
|
||||||
|
|
||||||
public static class KeyRings implements KeyRingsColumns, BaseColumns {
|
public static class KeyRings implements KeyRingsColumns, BaseColumns {
|
||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_KEY_RINGS).build();
|
.appendPath(BASE_KEY_RINGS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/**
|
||||||
|
* Use if multiple items get returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring";
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring";
|
||||||
|
|
||||||
/** Use if a single item is returned */
|
/**
|
||||||
|
* Use if a single item is returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
|
||||||
|
|
||||||
|
public static Uri buildUnifiedKeyRingsUri() {
|
||||||
|
return CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
public static Uri buildPublicKeyRingsUri() {
|
public static Uri buildPublicKeyRingsUri() {
|
||||||
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
|
return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
|
||||||
}
|
}
|
||||||
@ -147,6 +160,7 @@ public class KeychainContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Uri buildSecretKeyRingsByEmailsUri(String emails) {
|
public static Uri buildSecretKeyRingsByEmailsUri(String emails) {
|
||||||
|
// TODO: encoded?
|
||||||
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS)
|
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS)
|
||||||
.appendPath(emails).build();
|
.appendPath(emails).build();
|
||||||
}
|
}
|
||||||
@ -161,10 +175,14 @@ public class KeychainContract {
|
|||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_KEY_RINGS).build();
|
.appendPath(BASE_KEY_RINGS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/**
|
||||||
|
* Use if multiple items get returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key";
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key";
|
||||||
|
|
||||||
/** Use if a single item is returned */
|
/**
|
||||||
|
* Use if a single item is returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key";
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key";
|
||||||
|
|
||||||
public static Uri buildPublicKeysUri(String keyRingRowId) {
|
public static Uri buildPublicKeysUri(String keyRingRowId) {
|
||||||
@ -200,10 +218,14 @@ public class KeychainContract {
|
|||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_KEY_RINGS).build();
|
.appendPath(BASE_KEY_RINGS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/**
|
||||||
|
* Use if multiple items get returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
|
||||||
|
|
||||||
/** Use if a single item is returned */
|
/**
|
||||||
|
* Use if a single item is returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
|
||||||
|
|
||||||
public static Uri buildPublicUserIdsUri(String keyRingRowId) {
|
public static Uri buildPublicUserIdsUri(String keyRingRowId) {
|
||||||
@ -239,20 +261,44 @@ public class KeychainContract {
|
|||||||
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
.appendPath(BASE_API_APPS).build();
|
.appendPath(BASE_API_APPS).build();
|
||||||
|
|
||||||
/** Use if multiple items get returned */
|
/**
|
||||||
|
* Use if multiple items get returned
|
||||||
|
*/
|
||||||
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps";
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps";
|
||||||
|
|
||||||
/** Use if a single item is returned */
|
/**
|
||||||
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps";
|
* Use if a single item is returned
|
||||||
|
*/
|
||||||
public static Uri buildIdUri(String rowId) {
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app";
|
||||||
return CONTENT_URI.buildUpon().appendPath(rowId).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Uri buildByPackageNameUri(String packageName) {
|
public static Uri buildByPackageNameUri(String packageName) {
|
||||||
return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName)
|
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns {
|
||||||
|
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
|
||||||
|
.appendPath(BASE_API_APPS).build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use if multiple items get returned
|
||||||
|
*/
|
||||||
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_app.accounts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use if a single item is returned
|
||||||
|
*/
|
||||||
|
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app.account";
|
||||||
|
|
||||||
|
public static Uri buildBaseUri(String packageName) {
|
||||||
|
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri buildByPackageAndAccountUri(String packageName, String accountName) {
|
||||||
|
return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
|
||||||
|
.appendEncodedPath(accountName).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DataStream {
|
public static class DataStream {
|
||||||
|
@ -17,27 +17,29 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.provider;
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
public class KeychainDatabase extends SQLiteOpenHelper {
|
public class KeychainDatabase extends SQLiteOpenHelper {
|
||||||
private static final String DATABASE_NAME = "apg.db";
|
private static final String DATABASE_NAME = "apg.db";
|
||||||
private static final int DATABASE_VERSION = 7;
|
private static final int DATABASE_VERSION = 8;
|
||||||
|
|
||||||
public interface Tables {
|
public interface Tables {
|
||||||
String KEY_RINGS = "key_rings";
|
String KEY_RINGS = "key_rings";
|
||||||
String KEYS = "keys";
|
String KEYS = "keys";
|
||||||
String USER_IDS = "user_ids";
|
String USER_IDS = "user_ids";
|
||||||
String API_APPS = "api_apps";
|
String API_APPS = "api_apps";
|
||||||
|
String API_ACCOUNTS = "api_accounts";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
|
private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
|
||||||
@ -62,26 +64,35 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
+ KeysColumns.KEY_DATA + " BLOB,"
|
+ KeysColumns.KEY_DATA + " BLOB,"
|
||||||
+ KeysColumns.RANK + " INTEGER, "
|
+ KeysColumns.RANK + " INTEGER, "
|
||||||
+ KeysColumns.FINGERPRINT + " BLOB, "
|
+ KeysColumns.FINGERPRINT + " BLOB, "
|
||||||
+ KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
|
+ KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, "
|
||||||
+ KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
|
+ "FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES "
|
||||||
+ BaseColumns._ID + ") ON DELETE CASCADE)";
|
+ Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
|
private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
|
||||||
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ UserIdsColumns.USER_ID + " TEXT, "
|
+ UserIdsColumns.USER_ID + " TEXT, "
|
||||||
+ UserIdsColumns.RANK + " INTEGER, "
|
+ UserIdsColumns.RANK + " INTEGER, "
|
||||||
+ UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
|
+ UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, "
|
||||||
+ UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
|
+ "FOREIGN KEY(" + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES "
|
||||||
+ BaseColumns._ID + ") ON DELETE CASCADE)";
|
+ Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
|
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
|
||||||
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, "
|
+ ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, "
|
||||||
+ ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, "
|
+ ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)";
|
||||||
+ ApiAppsColumns.KEY_ID + " INT64, "
|
|
||||||
+ ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
|
private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS
|
||||||
+ ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
|
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ ApiAppsColumns.COMPRESSION + " INTEGER)";
|
+ ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, "
|
||||||
|
+ ApiAppsAccountsColumns.KEY_ID + " INT64, "
|
||||||
|
+ ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
|
||||||
|
+ ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, "
|
||||||
|
+ ApiAppsAccountsColumns.COMPRESSION + " INTEGER, "
|
||||||
|
+ ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, "
|
||||||
|
+ "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", "
|
||||||
|
+ ApiAppsAccountsColumns.PACKAGE_NAME + "), "
|
||||||
|
+ "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES "
|
||||||
|
+ Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)";
|
||||||
|
|
||||||
KeychainDatabase(Context context) {
|
KeychainDatabase(Context context) {
|
||||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
@ -95,6 +106,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
db.execSQL(CREATE_KEYS);
|
db.execSQL(CREATE_KEYS);
|
||||||
db.execSQL(CREATE_USER_IDS);
|
db.execSQL(CREATE_USER_IDS);
|
||||||
db.execSQL(CREATE_API_APPS);
|
db.execSQL(CREATE_API_APPS);
|
||||||
|
db.execSQL(CREATE_API_APPS_ACCOUNTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -134,6 +146,12 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
|
db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
|
||||||
+ " BLOB;");
|
+ " BLOB;");
|
||||||
break;
|
break;
|
||||||
|
case 7:
|
||||||
|
// new db layout for api apps
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
|
||||||
|
db.execSQL(CREATE_API_APPS);
|
||||||
|
db.execSQL(CREATE_API_APPS_ACCOUNTS);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -17,21 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.provider;
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.UriMatcher;
|
import android.content.UriMatcher;
|
||||||
@ -44,6 +29,22 @@ import android.net.Uri;
|
|||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class KeychainProvider extends ContentProvider {
|
public class KeychainProvider extends ContentProvider {
|
||||||
// public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME
|
// public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME
|
||||||
// + ".action.DATABASE_CHANGE";
|
// + ".action.DATABASE_CHANGE";
|
||||||
@ -63,6 +64,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
private static final int PUBLIC_KEY_RING_USER_ID = 121;
|
private static final int PUBLIC_KEY_RING_USER_ID = 121;
|
||||||
private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122;
|
private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122;
|
||||||
|
private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID = 123;
|
||||||
|
|
||||||
private static final int SECRET_KEY_RING = 201;
|
private static final int SECRET_KEY_RING = 201;
|
||||||
private static final int SECRET_KEY_RING_BY_ROW_ID = 202;
|
private static final int SECRET_KEY_RING_BY_ROW_ID = 202;
|
||||||
@ -78,8 +80,11 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
|
private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
|
||||||
|
|
||||||
private static final int API_APPS = 301;
|
private static final int API_APPS = 301;
|
||||||
private static final int API_APPS_BY_ROW_ID = 302;
|
|
||||||
private static final int API_APPS_BY_PACKAGE_NAME = 303;
|
private static final int API_APPS_BY_PACKAGE_NAME = 303;
|
||||||
|
private static final int API_ACCOUNTS = 304;
|
||||||
|
private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
|
||||||
|
|
||||||
|
private static final int UNIFIED_KEY_RING = 401;
|
||||||
|
|
||||||
// private static final int DATA_STREAM = 401;
|
// private static final int DATA_STREAM = 401;
|
||||||
|
|
||||||
@ -94,6 +99,15 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
String authority = KeychainContract.CONTENT_AUTHORITY;
|
String authority = KeychainContract.CONTENT_AUTHORITY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unified key rings
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* key_rings
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS, UNIFIED_KEY_RING);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public key rings
|
* public key rings
|
||||||
*
|
*
|
||||||
@ -147,6 +161,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
* <pre>
|
* <pre>
|
||||||
* key_rings/public/#/user_ids
|
* key_rings/public/#/user_ids
|
||||||
* key_rings/public/#/user_ids/#
|
* key_rings/public/#/user_ids/#
|
||||||
|
* key_rings/public/master_key_id/#/user_ids
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
||||||
@ -155,6 +170,10 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
||||||
+ KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
|
+ KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
|
||||||
PUBLIC_KEY_RING_USER_ID_BY_ROW_ID);
|
PUBLIC_KEY_RING_USER_ID_BY_ROW_ID);
|
||||||
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
||||||
|
+ KeychainContract.PATH_PUBLIC + "/"
|
||||||
|
+ KeychainContract.PATH_BY_MASTER_KEY_ID + "/*/" + KeychainContract.PATH_USER_IDS,
|
||||||
|
PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* secret key rings
|
* secret key rings
|
||||||
@ -220,11 +239,22 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* API apps
|
* API apps
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* api_apps
|
||||||
|
* api_apps/_ (package name)
|
||||||
|
*
|
||||||
|
* api_apps/_/accounts
|
||||||
|
* api_apps/_/accounts/_ (account name)
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
|
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
|
||||||
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID);
|
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
|
||||||
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
|
|
||||||
+ KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME);
|
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
|
||||||
|
+ KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS);
|
||||||
|
matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
|
||||||
|
+ KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* data stream
|
* data stream
|
||||||
@ -238,7 +268,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
return matcher;
|
return matcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeychainDatabase mApgDatabase;
|
private KeychainDatabase mKeychainDatabase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
@ -246,7 +276,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
mUriMatcher = buildUriMatcher();
|
mUriMatcher = buildUriMatcher();
|
||||||
mApgDatabase = new KeychainDatabase(getContext());
|
mKeychainDatabase = new KeychainDatabase(getContext());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +312,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
return Keys.CONTENT_ITEM_TYPE;
|
return Keys.CONTENT_ITEM_TYPE;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_USER_ID:
|
case PUBLIC_KEY_RING_USER_ID:
|
||||||
|
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
|
||||||
case SECRET_KEY_RING_USER_ID:
|
case SECRET_KEY_RING_USER_ID:
|
||||||
return UserIds.CONTENT_TYPE;
|
return UserIds.CONTENT_TYPE;
|
||||||
|
|
||||||
@ -292,10 +323,15 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
case API_APPS:
|
case API_APPS:
|
||||||
return ApiApps.CONTENT_TYPE;
|
return ApiApps.CONTENT_TYPE;
|
||||||
|
|
||||||
case API_APPS_BY_ROW_ID:
|
|
||||||
case API_APPS_BY_PACKAGE_NAME:
|
case API_APPS_BY_PACKAGE_NAME:
|
||||||
return ApiApps.CONTENT_ITEM_TYPE;
|
return ApiApps.CONTENT_ITEM_TYPE;
|
||||||
|
|
||||||
|
case API_ACCOUNTS:
|
||||||
|
return ApiAccounts.CONTENT_TYPE;
|
||||||
|
|
||||||
|
case API_ACCOUNTS_BY_ACCOUNT_NAME:
|
||||||
|
return ApiAccounts.CONTENT_ITEM_TYPE;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
||||||
}
|
}
|
||||||
@ -304,7 +340,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
/**
|
/**
|
||||||
* Returns type of the query (secret/public)
|
* Returns type of the query (secret/public)
|
||||||
*
|
*
|
||||||
* @param uri
|
* @param match
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private int getKeyType(int match) {
|
private int getKeyType(int match) {
|
||||||
@ -319,6 +355,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
case PUBLIC_KEY_RING_KEY:
|
case PUBLIC_KEY_RING_KEY:
|
||||||
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
|
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
|
||||||
case PUBLIC_KEY_RING_USER_ID:
|
case PUBLIC_KEY_RING_USER_ID:
|
||||||
|
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
|
||||||
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
|
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
|
||||||
type = KeyTypes.PUBLIC;
|
type = KeyTypes.PUBLIC;
|
||||||
break;
|
break;
|
||||||
@ -356,15 +393,25 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
|
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
|
||||||
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
|
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
|
||||||
+ KeyRingsColumns.KEY_RING_DATA);
|
+ KeyRingsColumns.KEY_RING_DATA);
|
||||||
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.ALGORITHM, Tables.KEYS + "." + KeysColumns.ALGORITHM);
|
||||||
|
projectionMap.put(KeysColumns.KEY_SIZE, Tables.KEYS + "." + KeysColumns.KEY_SIZE);
|
||||||
|
projectionMap.put(KeysColumns.CREATION, Tables.KEYS + "." + KeysColumns.CREATION);
|
||||||
|
projectionMap.put(KeysColumns.EXPIRY, Tables.KEYS + "." + KeysColumns.EXPIRY);
|
||||||
|
projectionMap.put(KeysColumns.KEY_RING_ROW_ID, Tables.KEYS + "." + KeysColumns.KEY_RING_ROW_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(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);
|
||||||
|
|
||||||
|
// type attribute is special: if there is any grouping, choose secret over public type
|
||||||
|
projectionMap.put(KeyRingsColumns.TYPE,
|
||||||
|
"MAX(" + Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + ") AS " + KeyRingsColumns.TYPE);
|
||||||
|
|
||||||
return projectionMap;
|
return projectionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,13 +442,27 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
return projectionMap;
|
return projectionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HashMap<String, String> getProjectionMapForUserIds() {
|
||||||
|
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
projectionMap.put(BaseColumns._ID, Tables.USER_IDS + "." + BaseColumns._ID);
|
||||||
|
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
|
||||||
|
projectionMap.put(UserIdsColumns.RANK, Tables.USER_IDS + "." + UserIdsColumns.RANK);
|
||||||
|
projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
|
||||||
|
+ KeyRingsColumns.MASTER_KEY_ID);
|
||||||
|
|
||||||
|
return projectionMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
|
* Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
|
||||||
*/
|
*/
|
||||||
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
|
private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
|
||||||
// public or secret keyring
|
if (match != UNIFIED_KEY_RING) {
|
||||||
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
|
// public or secret keyring
|
||||||
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
|
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
|
||||||
|
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
|
||||||
|
}
|
||||||
|
|
||||||
// join keyrings with keys and userIds
|
// join keyrings with keys and userIds
|
||||||
// Only get user id and key with rank 0 (main user id and main key)
|
// Only get user id and key with rank 0 (main user id and main key)
|
||||||
@ -451,11 +512,26 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
|
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
|
||||||
|
|
||||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||||
SQLiteDatabase db = mApgDatabase.getReadableDatabase();
|
SQLiteDatabase db = mKeychainDatabase.getReadableDatabase();
|
||||||
|
|
||||||
int match = mUriMatcher.match(uri);
|
int match = mUriMatcher.match(uri);
|
||||||
|
|
||||||
|
// all query() parameters, for good measure
|
||||||
|
String groupBy = null, having = null;
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
|
case UNIFIED_KEY_RING:
|
||||||
|
qb = buildKeyRingQuery(qb, match);
|
||||||
|
|
||||||
|
// GROUP BY so we don't get duplicates
|
||||||
|
groupBy = Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID;
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
|
sortOrder = KeyRings.TYPE + " DESC, " +
|
||||||
|
Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
case PUBLIC_KEY_RING:
|
case PUBLIC_KEY_RING:
|
||||||
case SECRET_KEY_RING:
|
case SECRET_KEY_RING:
|
||||||
qb = buildKeyRingQuery(qb, match);
|
qb = buildKeyRingQuery(qb, match);
|
||||||
@ -465,7 +541,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_BY_ROW_ID:
|
case PUBLIC_KEY_RING_BY_ROW_ID:
|
||||||
case SECRET_KEY_RING_BY_ROW_ID:
|
case SECRET_KEY_RING_BY_ROW_ID:
|
||||||
qb = buildKeyRingQuery(qb, match);
|
qb = buildKeyRingQuery(qb, match);
|
||||||
@ -478,7 +553,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
|
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
|
||||||
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
|
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
|
||||||
qb = buildKeyRingQuery(qb, match);
|
qb = buildKeyRingQuery(qb, match);
|
||||||
@ -491,7 +565,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SECRET_KEY_RING_BY_KEY_ID:
|
case SECRET_KEY_RING_BY_KEY_ID:
|
||||||
case PUBLIC_KEY_RING_BY_KEY_ID:
|
case PUBLIC_KEY_RING_BY_KEY_ID:
|
||||||
qb = buildKeyRingQueryWithSpecificKey(qb, match);
|
qb = buildKeyRingQueryWithSpecificKey(qb, match);
|
||||||
@ -504,7 +577,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SECRET_KEY_RING_BY_EMAILS:
|
case SECRET_KEY_RING_BY_EMAILS:
|
||||||
case PUBLIC_KEY_RING_BY_EMAILS:
|
case PUBLIC_KEY_RING_BY_EMAILS:
|
||||||
qb = buildKeyRingQuery(qb, match);
|
qb = buildKeyRingQuery(qb, match);
|
||||||
@ -534,7 +606,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SECRET_KEY_RING_BY_LIKE_EMAIL:
|
case SECRET_KEY_RING_BY_LIKE_EMAIL:
|
||||||
case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
|
case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
|
||||||
qb = buildKeyRingQuery(qb, match);
|
qb = buildKeyRingQuery(qb, match);
|
||||||
@ -550,7 +621,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
+ "))");
|
+ "))");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_KEY:
|
case PUBLIC_KEY_RING_KEY:
|
||||||
case SECRET_KEY_RING_KEY:
|
case SECRET_KEY_RING_KEY:
|
||||||
qb.setTables(Tables.KEYS);
|
qb.setTables(Tables.KEYS);
|
||||||
@ -563,7 +633,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
qb.setProjectionMap(getProjectionMapForKeys());
|
qb.setProjectionMap(getProjectionMapForKeys());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
|
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
|
||||||
case SECRET_KEY_RING_KEY_BY_ROW_ID:
|
case SECRET_KEY_RING_KEY_BY_ROW_ID:
|
||||||
qb.setTables(Tables.KEYS);
|
qb.setTables(Tables.KEYS);
|
||||||
@ -579,7 +648,16 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
qb.setProjectionMap(getProjectionMapForKeys());
|
qb.setProjectionMap(getProjectionMapForKeys());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
|
||||||
|
qb.setTables(Tables.USER_IDS + " INNER JOIN " + Tables.KEY_RINGS + " ON " + "("
|
||||||
|
+ Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
|
||||||
|
+ KeysColumns.KEY_RING_ROW_ID + " )");
|
||||||
|
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
|
||||||
|
qb.appendWhereEscapeString(uri.getPathSegments().get(3));
|
||||||
|
|
||||||
|
qb.setProjectionMap(getProjectionMapForUserIds());
|
||||||
|
|
||||||
|
break;
|
||||||
case PUBLIC_KEY_RING_USER_ID:
|
case PUBLIC_KEY_RING_USER_ID:
|
||||||
case SECRET_KEY_RING_USER_ID:
|
case SECRET_KEY_RING_USER_ID:
|
||||||
qb.setTables(Tables.USER_IDS);
|
qb.setTables(Tables.USER_IDS);
|
||||||
@ -587,7 +665,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
|
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
|
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
|
||||||
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
|
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
|
||||||
qb.setTables(Tables.USER_IDS);
|
qb.setTables(Tables.USER_IDS);
|
||||||
@ -598,25 +675,31 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case API_APPS:
|
case API_APPS:
|
||||||
qb.setTables(Tables.API_APPS);
|
qb.setTables(Tables.API_APPS);
|
||||||
|
|
||||||
break;
|
|
||||||
case API_APPS_BY_ROW_ID:
|
|
||||||
qb.setTables(Tables.API_APPS);
|
|
||||||
|
|
||||||
qb.appendWhere(BaseColumns._ID + " = ");
|
|
||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case API_APPS_BY_PACKAGE_NAME:
|
case API_APPS_BY_PACKAGE_NAME:
|
||||||
qb.setTables(Tables.API_APPS);
|
qb.setTables(Tables.API_APPS);
|
||||||
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
|
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
case API_ACCOUNTS:
|
||||||
|
qb.setTables(Tables.API_ACCOUNTS);
|
||||||
|
qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
|
||||||
|
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
|
||||||
|
|
||||||
|
break;
|
||||||
|
case API_ACCOUNTS_BY_ACCOUNT_NAME:
|
||||||
|
qb.setTables(Tables.API_ACCOUNTS);
|
||||||
|
qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
|
||||||
|
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
|
||||||
|
|
||||||
|
qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = ");
|
||||||
|
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
||||||
|
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||||
|
|
||||||
@ -630,7 +713,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
orderBy = sortOrder;
|
orderBy = sortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
|
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
|
||||||
|
|
||||||
// Tell the cursor what uri to watch, so it knows when its source data changes
|
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||||
c.setNotificationUri(getContext().getContentResolver(), uri);
|
c.setNotificationUri(getContext().getContentResolver(), uri);
|
||||||
@ -653,7 +736,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
public Uri insert(Uri uri, ContentValues values) {
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
|
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
|
||||||
|
|
||||||
final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
|
final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
|
||||||
|
|
||||||
Uri rowUri = null;
|
Uri rowUri = null;
|
||||||
long rowId = -1;
|
long rowId = -1;
|
||||||
@ -673,12 +756,14 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
values.put(Keys.TYPE, KeyTypes.PUBLIC);
|
values.put(Keys.TYPE, KeyTypes.PUBLIC);
|
||||||
|
|
||||||
rowId = db.insertOrThrow(Tables.KEYS, null, values);
|
rowId = db.insertOrThrow(Tables.KEYS, null, values);
|
||||||
|
// TODO: this is wrong:
|
||||||
rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
|
rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
|
||||||
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
|
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PUBLIC_KEY_RING_USER_ID:
|
case PUBLIC_KEY_RING_USER_ID:
|
||||||
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
|
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
|
||||||
|
// TODO: this is wrong:
|
||||||
rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
|
rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
|
||||||
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
|
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
|
||||||
|
|
||||||
@ -695,18 +780,33 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
values.put(Keys.TYPE, KeyTypes.SECRET);
|
values.put(Keys.TYPE, KeyTypes.SECRET);
|
||||||
|
|
||||||
rowId = db.insertOrThrow(Tables.KEYS, null, values);
|
rowId = db.insertOrThrow(Tables.KEYS, null, values);
|
||||||
|
// TODO: this is wrong:
|
||||||
rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
|
rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
|
||||||
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
|
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case SECRET_KEY_RING_USER_ID:
|
case SECRET_KEY_RING_USER_ID:
|
||||||
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
|
rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
|
||||||
|
// TODO: this is wrong:
|
||||||
rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
|
rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case API_APPS:
|
case API_APPS:
|
||||||
rowId = db.insertOrThrow(Tables.API_APPS, null, values);
|
rowId = db.insertOrThrow(Tables.API_APPS, null, values);
|
||||||
rowUri = ApiApps.buildIdUri(Long.toString(rowId));
|
// rowUri = ApiApps.buildIdUri(Long.toString(rowId));
|
||||||
|
|
||||||
|
break;
|
||||||
|
case API_ACCOUNTS:
|
||||||
|
// set foreign key automatically based on given uri
|
||||||
|
// e.g., api_apps/com.example.app/accounts/
|
||||||
|
String packageName = uri.getPathSegments().get(1);
|
||||||
|
values.put(ApiAccounts.PACKAGE_NAME, packageName);
|
||||||
|
|
||||||
|
Log.d(Constants.TAG, "provider packageName: " + packageName);
|
||||||
|
|
||||||
|
rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values);
|
||||||
|
// TODO: this is wrong:
|
||||||
|
// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -730,7 +830,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
Log.v(Constants.TAG, "delete(uri=" + uri + ")");
|
Log.v(Constants.TAG, "delete(uri=" + uri + ")");
|
||||||
|
|
||||||
final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
|
final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
|
||||||
|
|
||||||
int count;
|
int count;
|
||||||
final int match = mUriMatcher.match(uri);
|
final int match = mUriMatcher.match(uri);
|
||||||
@ -766,12 +866,12 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
|
count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
|
||||||
selectionArgs);
|
selectionArgs);
|
||||||
break;
|
break;
|
||||||
case API_APPS_BY_ROW_ID:
|
case API_APPS_BY_PACKAGE_NAME:
|
||||||
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
|
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, selection),
|
||||||
selectionArgs);
|
selectionArgs);
|
||||||
break;
|
break;
|
||||||
case API_APPS_BY_PACKAGE_NAME:
|
case API_ACCOUNTS_BY_ACCOUNT_NAME:
|
||||||
count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
|
count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, selection),
|
||||||
selectionArgs);
|
selectionArgs);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -791,7 +891,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
|
Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
|
||||||
|
|
||||||
final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
|
final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
|
||||||
|
|
||||||
String defaultSelection = null;
|
String defaultSelection = null;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -836,13 +936,13 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
count = db.update(Tables.USER_IDS, values,
|
count = db.update(Tables.USER_IDS, values,
|
||||||
buildDefaultUserIdsSelection(uri, selection), selectionArgs);
|
buildDefaultUserIdsSelection(uri, selection), selectionArgs);
|
||||||
break;
|
break;
|
||||||
case API_APPS_BY_ROW_ID:
|
|
||||||
count = db.update(Tables.API_APPS, values,
|
|
||||||
buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
|
|
||||||
break;
|
|
||||||
case API_APPS_BY_PACKAGE_NAME:
|
case API_APPS_BY_PACKAGE_NAME:
|
||||||
count = db.update(Tables.API_APPS, values,
|
count = db.update(Tables.API_APPS, values,
|
||||||
buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
|
buildDefaultApiAppsSelection(uri, selection), selectionArgs);
|
||||||
|
break;
|
||||||
|
case API_ACCOUNTS_BY_ACCOUNT_NAME:
|
||||||
|
count = db.update(Tables.API_ACCOUNTS, values,
|
||||||
|
buildDefaultApiAccountsSelection(uri, selection), selectionArgs);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
throw new UnsupportedOperationException("Unknown uri: " + uri);
|
||||||
@ -862,7 +962,8 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
* Build default selection statement for KeyRings. If no extra selection is specified only build
|
* Build default selection statement for KeyRings. If no extra selection is specified only build
|
||||||
* where clause with rowId
|
* where clause with rowId
|
||||||
*
|
*
|
||||||
* @param uri
|
* @param defaultSelection
|
||||||
|
* @param keyType
|
||||||
* @param selection
|
* @param selection
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -940,19 +1041,29 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
* @param selection
|
* @param selection
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) {
|
private String buildDefaultApiAppsSelection(Uri uri, String selection) {
|
||||||
String lastPathSegment = uri.getLastPathSegment();
|
String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
|
||||||
|
|
||||||
String andSelection = "";
|
String andSelection = "";
|
||||||
if (!TextUtils.isEmpty(selection)) {
|
if (!TextUtils.isEmpty(selection)) {
|
||||||
andSelection = " AND (" + selection + ")";
|
andSelection = " AND (" + selection + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packageSelection) {
|
return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection;
|
||||||
return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection;
|
}
|
||||||
} else {
|
|
||||||
return BaseColumns._ID + "=" + lastPathSegment + andSelection;
|
private String buildDefaultApiAccountsSelection(Uri uri, String selection) {
|
||||||
|
String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1));
|
||||||
|
String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
|
||||||
|
|
||||||
|
String andSelection = "";
|
||||||
|
if (!TextUtils.isEmpty(selection)) {
|
||||||
|
andSelection = " AND (" + selection + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND "
|
||||||
|
+ ApiAccounts.ACCOUNT_NAME + "=" + accountName
|
||||||
|
+ andSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
|
@ -17,10 +17,9 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.provider;
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
|
||||||
public class KeychainServiceBlobContract {
|
public class KeychainServiceBlobContract {
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ import android.content.Context;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
|
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
|
||||||
|
|
||||||
public class KeychainServiceBlobDatabase extends SQLiteOpenHelper {
|
public class KeychainServiceBlobDatabase extends SQLiteOpenHelper {
|
||||||
|
@ -18,11 +18,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.provider;
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@ -31,7 +26,10 @@ import android.database.sqlite.SQLiteDatabase;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@ -40,7 +38,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class KeychainServiceBlobProvider extends ContentProvider {
|
public class KeychainServiceBlobProvider extends ContentProvider {
|
||||||
private static final String STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs";
|
private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs";
|
||||||
|
|
||||||
private KeychainServiceBlobDatabase mBlobDatabase = null;
|
private KeychainServiceBlobDatabase mBlobDatabase = null;
|
||||||
|
|
||||||
@ -55,7 +53,9 @@ public class KeychainServiceBlobProvider extends ContentProvider {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(Uri uri, ContentValues ignored) {
|
public Uri insert(Uri uri, ContentValues ignored) {
|
||||||
// ContentValues are actually ignored, because we want to store a blob with no more
|
// ContentValues are actually ignored, because we want to store a blob with no more
|
||||||
@ -74,7 +74,9 @@ public class KeychainServiceBlobProvider extends ContentProvider {
|
|||||||
return Uri.withAppendedPath(insertedUri, password);
|
return Uri.withAppendedPath(insertedUri, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException,
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException,
|
||||||
FileNotFoundException {
|
FileNotFoundException {
|
||||||
@ -91,9 +93,9 @@ public class KeychainServiceBlobProvider extends ContentProvider {
|
|||||||
|
|
||||||
// get the data
|
// get the data
|
||||||
SQLiteDatabase db = mBlobDatabase.getReadableDatabase();
|
SQLiteDatabase db = mBlobDatabase.getReadableDatabase();
|
||||||
Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID },
|
Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[]{BaseColumns._ID},
|
||||||
BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?",
|
BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?",
|
||||||
new String[] { id, key }, null, null, null);
|
new String[]{id, key}, null, null, null);
|
||||||
|
|
||||||
if (result.getCount() == 0) {
|
if (result.getCount() == 0) {
|
||||||
// either the key is wrong or no id exists
|
// either the key is wrong or no id exists
|
||||||
@ -124,26 +126,34 @@ public class KeychainServiceBlobProvider extends ContentProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getType(Uri uri) {
|
public String getType(Uri uri) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||||
String sortOrder) {
|
String sortOrder) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.provider;
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import android.content.*;
|
||||||
import java.io.IOException;
|
import android.database.Cursor;
|
||||||
import java.util.ArrayList;
|
import android.database.DatabaseUtils;
|
||||||
import java.util.Date;
|
import android.net.Uri;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
import org.spongycastle.openpgp.PGPKeyRing;
|
||||||
@ -38,19 +39,15 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
|||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||||
import org.sufficientlysecure.keychain.service.remote.AppSettings;
|
import org.sufficientlysecure.keychain.remote.AccountSettings;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AppSettings;
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import android.content.ContentProviderOperation;
|
import java.io.ByteArrayOutputStream;
|
||||||
import android.content.ContentResolver;
|
import java.io.IOException;
|
||||||
import android.content.ContentValues;
|
import java.util.ArrayList;
|
||||||
import android.content.Context;
|
import java.util.Date;
|
||||||
import android.content.OperationApplicationException;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
|
|
||||||
public class ProviderHelper {
|
public class ProviderHelper {
|
||||||
|
|
||||||
@ -87,7 +84,7 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId
|
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
|
||||||
*/
|
*/
|
||||||
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
|
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
|
||||||
long masterKeyId) {
|
long masterKeyId) {
|
||||||
@ -110,11 +107,8 @@ public class ProviderHelper {
|
|||||||
*/
|
*/
|
||||||
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
|
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
|
||||||
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
|
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
|
||||||
if (keyRing == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyRing.getPublicKey(keyId);
|
return (keyRing == null) ? null : keyRing.getPublicKey(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,11 +143,8 @@ public class ProviderHelper {
|
|||||||
*/
|
*/
|
||||||
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
|
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
|
||||||
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
|
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
|
||||||
if (keyRing == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return keyRing.getSecretKey(keyId);
|
return (keyRing == null) ? null : keyRing.getSecretKey(keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,7 +159,8 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
// get current _ID of key
|
// get current _ID of key
|
||||||
long currentRowId = -1;
|
long currentRowId = -1;
|
||||||
Cursor oldQuery = context.getContentResolver().query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
|
Cursor oldQuery = context.getContentResolver()
|
||||||
|
.query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
|
||||||
if (oldQuery != null && oldQuery.moveToFirst()) {
|
if (oldQuery != null && oldQuery.moveToFirst()) {
|
||||||
currentRowId = oldQuery.getLong(0);
|
currentRowId = oldQuery.getLong(0);
|
||||||
} else {
|
} else {
|
||||||
@ -184,10 +176,12 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
// use exactly the same _ID again to replace key in-place.
|
// use exactly the same _ID again to replace key in-place.
|
||||||
// NOTE: If we would not use the same _ID again, getting back to the ViewKeyActivity would result in Nullpointer,
|
// NOTE: If we would not use the same _ID again,
|
||||||
|
// getting back to the ViewKeyActivity would result in Nullpointer,
|
||||||
// because the currently loaded key would be gone from the database
|
// because the currently loaded key would be gone from the database
|
||||||
if (currentRowId != -1)
|
if (currentRowId != -1) {
|
||||||
values.put(KeyRings._ID, currentRowId);
|
values.put(KeyRings._ID, currentRowId);
|
||||||
|
}
|
||||||
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
|
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
|
||||||
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
|
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
|
||||||
|
|
||||||
@ -211,8 +205,11 @@ public class ProviderHelper {
|
|||||||
++userIdRank;
|
++userIdRank;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PGPSignature certification : new IterableIterator<PGPSignature>(masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) {
|
for (PGPSignature certification :
|
||||||
//TODO: how to do this?? we need to verify the signatures again and again when they are displayed...
|
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
|
// if (certification.verify
|
||||||
// operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
|
// operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
|
||||||
}
|
}
|
||||||
@ -239,7 +236,8 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
// get current _ID of key
|
// get current _ID of key
|
||||||
long currentRowId = -1;
|
long currentRowId = -1;
|
||||||
Cursor oldQuery = context.getContentResolver().query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
|
Cursor oldQuery = context.getContentResolver()
|
||||||
|
.query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
|
||||||
if (oldQuery != null && oldQuery.moveToFirst()) {
|
if (oldQuery != null && oldQuery.moveToFirst()) {
|
||||||
currentRowId = oldQuery.getLong(0);
|
currentRowId = oldQuery.getLong(0);
|
||||||
} else {
|
} else {
|
||||||
@ -255,10 +253,12 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
// use exactly the same _ID again to replace key in-place.
|
// use exactly the same _ID again to replace key in-place.
|
||||||
// NOTE: If we would not use the same _ID again, getting back to the ViewKeyActivity would result in Nullpointer,
|
// NOTE: If we would not use the same _ID again,
|
||||||
|
// getting back to the ViewKeyActivity would result in Nullpointer,
|
||||||
// because the currently loaded key would be gone from the database
|
// because the currently loaded key would be gone from the database
|
||||||
if (currentRowId != -1)
|
if (currentRowId != -1) {
|
||||||
values.put(KeyRings._ID, currentRowId);
|
values.put(KeyRings._ID, currentRowId);
|
||||||
|
}
|
||||||
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
|
values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
|
||||||
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
|
values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
|
||||||
|
|
||||||
@ -341,10 +341,10 @@ public class ProviderHelper {
|
|||||||
long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
|
long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
boolean has_private = true;
|
boolean hasPrivate = true;
|
||||||
if (key.isMasterKey()) {
|
if (key.isMasterKey()) {
|
||||||
if (PgpKeyHelper.isSecretKeyPrivateEmpty(key)) {
|
if (key.isPrivateKeyEmpty()) {
|
||||||
has_private = false;
|
hasPrivate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,9 +352,9 @@ public class ProviderHelper {
|
|||||||
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
|
values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
|
||||||
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
|
values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
|
||||||
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
|
values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
|
||||||
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && has_private));
|
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate));
|
||||||
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && has_private));
|
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate));
|
||||||
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && has_private);
|
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && hasPrivate);
|
||||||
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
|
values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
|
||||||
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
||||||
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
||||||
@ -410,6 +410,30 @@ public class ProviderHelper {
|
|||||||
return masterKeyIds;
|
return masterKeyIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper method
|
||||||
|
*/
|
||||||
|
private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) {
|
||||||
|
Cursor cursor = context.getContentResolver().query(queryUri,
|
||||||
|
new String[]{KeyRings._ID}, null, null, null);
|
||||||
|
|
||||||
|
ArrayList<Long> rowIds = new ArrayList<Long>();
|
||||||
|
if (cursor != null) {
|
||||||
|
int idCol = cursor.getColumnIndex(KeyRings._ID);
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
rowIds.add(cursor.getLong(idCol));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowIds;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves ids of all SecretKeyRings
|
* Retrieves ids of all SecretKeyRings
|
||||||
*/
|
*/
|
||||||
@ -426,6 +450,22 @@ public class ProviderHelper {
|
|||||||
return getKeyRingsMasterKeyIds(context, queryUri);
|
return getKeyRingsMasterKeyIds(context, queryUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ids of all SecretKeyRings
|
||||||
|
*/
|
||||||
|
public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) {
|
||||||
|
Uri queryUri = KeyRings.buildSecretKeyRingsUri();
|
||||||
|
return getKeyRingsRowIds(context, queryUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves ids of all PublicKeyRings
|
||||||
|
*/
|
||||||
|
public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) {
|
||||||
|
Uri queryUri = KeyRings.buildPublicKeyRingsUri();
|
||||||
|
return getKeyRingsRowIds(context, queryUri);
|
||||||
|
}
|
||||||
|
|
||||||
public static void deletePublicKeyRing(Context context, long rowId) {
|
public static void deletePublicKeyRing(Context context, long rowId) {
|
||||||
ContentResolver cr = context.getContentResolver();
|
ContentResolver cr = context.getContentResolver();
|
||||||
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
|
cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
|
||||||
@ -436,6 +476,15 @@ public class ProviderHelper {
|
|||||||
cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null);
|
cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteUnifiedKeyRing(Context context, String masterKeyId, boolean isSecretKey) {
|
||||||
|
ContentResolver cr = context.getContentResolver();
|
||||||
|
cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId), null, null);
|
||||||
|
if (isSecretKey) {
|
||||||
|
cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId), null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get master key id of keyring by its row id
|
* Get master key id of keyring by its row id
|
||||||
*/
|
*/
|
||||||
@ -449,13 +498,14 @@ public class ProviderHelper {
|
|||||||
*/
|
*/
|
||||||
public static boolean getSecretMasterKeyCanCertify(Context context, long keyRingRowId) {
|
public static boolean getSecretMasterKeyCanCertify(Context context, long keyRingRowId) {
|
||||||
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
|
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
|
||||||
return getMasterKeyCanCertify(context, queryUri, keyRingRowId);
|
return getMasterKeyCanCertify(context, queryUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private helper method to get master key private empty status of keyring by its row id
|
* Private helper method to get master key private empty status of keyring by its row id
|
||||||
*/
|
*/
|
||||||
private static boolean getMasterKeyCanCertify(Context context, Uri queryUri, long keyRingRowId) {
|
|
||||||
|
public static boolean getMasterKeyCanCertify(Context context, Uri queryUri) {
|
||||||
String[] projection = new String[]{
|
String[] projection = new String[]{
|
||||||
KeyRings.MASTER_KEY_ID,
|
KeyRings.MASTER_KEY_ID,
|
||||||
"(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
|
"(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
|
||||||
@ -481,6 +531,12 @@ public class ProviderHelper {
|
|||||||
return (masterKeyId > 0);
|
return (masterKeyId > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasSecretKeyByMasterKeyId(Context context, long masterKeyId) {
|
||||||
|
Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
|
||||||
|
// see if we can get our master key id back from the uri
|
||||||
|
return getMasterKeyId(context, queryUri) == masterKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get master key id of keyring by its row id
|
* Get master key id of keyring by its row id
|
||||||
*/
|
*/
|
||||||
@ -512,6 +568,26 @@ public class ProviderHelper {
|
|||||||
return masterKeyId;
|
return masterKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getRowId(Context context, Uri queryUri) {
|
||||||
|
String[] projection = new String[]{KeyRings._ID};
|
||||||
|
Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
|
||||||
|
|
||||||
|
long rowId = 0;
|
||||||
|
try {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int idCol = cursor.getColumnIndexOrThrow(KeyRings._ID);
|
||||||
|
|
||||||
|
rowId = cursor.getLong(idCol);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get fingerprint of key
|
* Get fingerprint of key
|
||||||
*/
|
*/
|
||||||
@ -733,19 +809,28 @@ public class ProviderHelper {
|
|||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
|
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
|
||||||
values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature());
|
values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature());
|
||||||
values.put(ApiApps.KEY_ID, appSettings.getKeyId());
|
return values;
|
||||||
values.put(ApiApps.COMPRESSION, appSettings.getCompression());
|
}
|
||||||
values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm());
|
|
||||||
values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm());
|
|
||||||
|
|
||||||
|
private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName());
|
||||||
|
values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId());
|
||||||
|
values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression());
|
||||||
|
values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm());
|
||||||
|
values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm());
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void insertApiApp(Context context, AppSettings appSettings) {
|
public static void insertApiApp(Context context, AppSettings appSettings) {
|
||||||
context.getContentResolver().insert(ApiApps.CONTENT_URI,
|
context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI,
|
||||||
contentValueForApiApps(appSettings));
|
contentValueForApiApps(appSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) {
|
||||||
|
context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings));
|
||||||
|
}
|
||||||
|
|
||||||
public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) {
|
public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) {
|
||||||
if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null,
|
if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null,
|
||||||
null) <= 0) {
|
null) <= 0) {
|
||||||
@ -753,30 +838,59 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) {
|
||||||
|
if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null,
|
||||||
|
null) <= 0) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be an uri pointing to an account
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param uri
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static AppSettings getApiAppSettings(Context context, Uri uri) {
|
public static AppSettings getApiAppSettings(Context context, Uri uri) {
|
||||||
AppSettings settings = null;
|
AppSettings settings = null;
|
||||||
|
|
||||||
Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
|
Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
|
||||||
if (cur != null && cur.moveToFirst()) {
|
if (cur != null && cur.moveToFirst()) {
|
||||||
settings = new AppSettings();
|
settings = new AppSettings();
|
||||||
settings.setPackageName(cur.getString(cur
|
settings.setPackageName(cur.getString(
|
||||||
.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
|
cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
|
||||||
settings.setPackageSignature(cur.getBlob(cur
|
settings.setPackageSignature(cur.getBlob(
|
||||||
.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
|
cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
|
||||||
settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID)));
|
}
|
||||||
settings.setCompression(cur.getInt(cur
|
|
||||||
.getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION)));
|
return settings;
|
||||||
settings.setHashAlgorithm(cur.getInt(cur
|
}
|
||||||
.getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM)));
|
|
||||||
settings.setEncryptionAlgorithm(cur.getInt(cur
|
public static AccountSettings getApiAccountSettings(Context context, Uri uri) {
|
||||||
.getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM)));
|
AccountSettings settings = null;
|
||||||
|
|
||||||
|
Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
|
||||||
|
if (cur != null && cur.moveToFirst()) {
|
||||||
|
settings = new AccountSettings();
|
||||||
|
|
||||||
|
settings.setAccountName(cur.getString(
|
||||||
|
cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
|
||||||
|
settings.setKeyId(cur.getLong(
|
||||||
|
cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
|
||||||
|
settings.setCompression(cur.getInt(
|
||||||
|
cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
|
||||||
|
settings.setHashAlgorithm(cur.getInt(
|
||||||
|
cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
|
||||||
|
settings.setEncryptionAlgorithm(cur.getInt(
|
||||||
|
cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getApiAppSignature(Context context, String packageName) {
|
public static byte[] getApiAppSignature(Context context, String packageName) {
|
||||||
Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName);
|
Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
|
||||||
|
|
||||||
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
|
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
|
||||||
|
|
||||||
|
@ -15,80 +15,71 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.HashAlgorithmTags;
|
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
|
||||||
public class AppSettings {
|
public class AccountSettings {
|
||||||
private String packageName;
|
private String mAccountName;
|
||||||
private byte[] packageSignature;
|
private long mKeyId = Id.key.none;
|
||||||
private long keyId = Id.key.none;
|
private int mEncryptionAlgorithm;
|
||||||
private int encryptionAlgorithm;
|
private int mHashAlgorithm;
|
||||||
private int hashAlgorithm;
|
private int mCompression;
|
||||||
private int compression;
|
|
||||||
|
|
||||||
public AppSettings() {
|
public AccountSettings() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppSettings(String packageName, byte[] packageSignature) {
|
public AccountSettings(String accountName) {
|
||||||
super();
|
super();
|
||||||
this.packageName = packageName;
|
this.mAccountName = accountName;
|
||||||
this.packageSignature = packageSignature;
|
|
||||||
// defaults:
|
// defaults:
|
||||||
this.encryptionAlgorithm = PGPEncryptedData.AES_256;
|
this.mEncryptionAlgorithm = PGPEncryptedData.AES_256;
|
||||||
this.hashAlgorithm = HashAlgorithmTags.SHA512;
|
this.mHashAlgorithm = HashAlgorithmTags.SHA512;
|
||||||
this.compression = Id.choice.compression.zlib;
|
this.mCompression = Id.choice.compression.zlib;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPackageName() {
|
public String getAccountName() {
|
||||||
return packageName;
|
return mAccountName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPackageName(String packageName) {
|
public void setAccountName(String mAccountName) {
|
||||||
this.packageName = packageName;
|
this.mAccountName = mAccountName;
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getPackageSignature() {
|
|
||||||
return packageSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPackageSignature(byte[] packageSignature) {
|
|
||||||
this.packageSignature = packageSignature;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKeyId() {
|
public long getKeyId() {
|
||||||
return keyId;
|
return mKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyId(long scretKeyId) {
|
public void setKeyId(long scretKeyId) {
|
||||||
this.keyId = scretKeyId;
|
this.mKeyId = scretKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getEncryptionAlgorithm() {
|
public int getEncryptionAlgorithm() {
|
||||||
return encryptionAlgorithm;
|
return mEncryptionAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEncryptionAlgorithm(int encryptionAlgorithm) {
|
public void setEncryptionAlgorithm(int encryptionAlgorithm) {
|
||||||
this.encryptionAlgorithm = encryptionAlgorithm;
|
this.mEncryptionAlgorithm = encryptionAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHashAlgorithm() {
|
public int getHashAlgorithm() {
|
||||||
return hashAlgorithm;
|
return mHashAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHashAlgorithm(int hashAlgorithm) {
|
public void setHashAlgorithm(int hashAlgorithm) {
|
||||||
this.hashAlgorithm = hashAlgorithm;
|
this.mHashAlgorithm = hashAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCompression() {
|
public int getCompression() {
|
||||||
return compression;
|
return mCompression;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCompression(int compression) {
|
public void setCompression(int compression) {
|
||||||
this.compression = compression;
|
this.mCompression = compression;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.remote;
|
||||||
|
|
||||||
|
import org.spongycastle.bcpg.HashAlgorithmTags;
|
||||||
|
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
|
||||||
|
public class AppSettings {
|
||||||
|
private String mPackageName;
|
||||||
|
private byte[] mPackageSignature;
|
||||||
|
|
||||||
|
public AppSettings() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AppSettings(String packageName, byte[] packageSignature) {
|
||||||
|
super();
|
||||||
|
this.mPackageName = packageName;
|
||||||
|
this.mPackageSignature = packageSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPackageName() {
|
||||||
|
return mPackageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPackageName(String packageName) {
|
||||||
|
this.mPackageName = packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getPackageSignature() {
|
||||||
|
return mPackageSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPackageSignature(byte[] packageSignature) {
|
||||||
|
this.mPackageSignature = packageSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -37,6 +37,7 @@ 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;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
@ -47,10 +48,6 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
public class OpenPgpService extends RemoteService {
|
public class OpenPgpService extends RemoteService {
|
||||||
|
|
||||||
private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
|
|
||||||
private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
|
|
||||||
private static final int PRIVATE_REQUEST_CODE_GET_KEYS = 553;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search database for key ids based on emails.
|
* Search database for key ids based on emails.
|
||||||
*
|
*
|
||||||
@ -100,7 +97,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
// return PendingIntent to be executed by client
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
@ -126,7 +125,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
|
||||||
// pass params through to activity that it can be returned again later to repeat pgp operation
|
// pass params through to activity that it can be returned again later to repeat pgp operation
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
// return PendingIntent to be executed by client
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
@ -136,7 +137,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Intent signImpl(Intent data, ParcelFileDescriptor input,
|
private Intent signImpl(Intent data, ParcelFileDescriptor input,
|
||||||
ParcelFileDescriptor output, AppSettings appSettings) {
|
ParcelFileDescriptor output, AccountSettings accSettings) {
|
||||||
try {
|
try {
|
||||||
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
@ -145,11 +146,11 @@ public class OpenPgpService extends RemoteService {
|
|||||||
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
|
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
|
||||||
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
||||||
} else {
|
} else {
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
|
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
|
||||||
}
|
}
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
// get PendingIntent for passphrase input, add it to given params and return to client
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
|
||||||
return passphraseBundle;
|
return passphraseBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +164,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
// sign-only
|
// sign-only
|
||||||
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
|
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
|
||||||
builder.enableAsciiArmorOutput(asciiArmor)
|
builder.enableAsciiArmorOutput(asciiArmor)
|
||||||
.signatureHashAlgorithm(appSettings.getHashAlgorithm())
|
.signatureHashAlgorithm(accSettings.getHashAlgorithm())
|
||||||
.signatureForceV3(false)
|
.signatureForceV3(false)
|
||||||
.signatureKeyId(appSettings.getKeyId())
|
.signatureKeyId(accSettings.getKeyId())
|
||||||
.signaturePassphrase(passphrase);
|
.signaturePassphrase(passphrase);
|
||||||
builder.build().execute();
|
builder.build().execute();
|
||||||
} finally {
|
} finally {
|
||||||
@ -186,7 +187,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
|
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
|
||||||
ParcelFileDescriptor output, AppSettings appSettings, boolean sign) {
|
ParcelFileDescriptor output, AccountSettings accSettings, boolean sign) {
|
||||||
try {
|
try {
|
||||||
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||||
|
|
||||||
@ -208,14 +209,15 @@ public class OpenPgpService extends RemoteService {
|
|||||||
} else {
|
} else {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
result.putExtra(OpenPgpApi.RESULT_ERROR,
|
||||||
new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
|
new OpenPgpError(OpenPgpError.GENERIC_ERROR,
|
||||||
|
"Missing parameter user_ids or key_ids!"));
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add own key for encryption
|
// add own key for encryption
|
||||||
keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
|
keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
|
||||||
keyIds[keyIds.length - 1] = appSettings.getKeyId();
|
keyIds[keyIds.length - 1] = accSettings.getKeyId();
|
||||||
|
|
||||||
// build InputData and write into OutputStream
|
// build InputData and write into OutputStream
|
||||||
// Get Input- and OutputStream from ParcelFileDescriptor
|
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||||
@ -227,8 +229,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
|
|
||||||
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
|
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
|
||||||
builder.enableAsciiArmorOutput(asciiArmor)
|
builder.enableAsciiArmorOutput(asciiArmor)
|
||||||
.compressionId(appSettings.getCompression())
|
.compressionId(accSettings.getCompression())
|
||||||
.symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
|
.symmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
|
||||||
.encryptionKeyIds(keyIds);
|
.encryptionKeyIds(keyIds);
|
||||||
|
|
||||||
if (sign) {
|
if (sign) {
|
||||||
@ -237,18 +239,18 @@ public class OpenPgpService extends RemoteService {
|
|||||||
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
||||||
} else {
|
} else {
|
||||||
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
|
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
|
||||||
appSettings.getKeyId());
|
accSettings.getKeyId());
|
||||||
}
|
}
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
// get PendingIntent for passphrase input, add it to given params and return to client
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
|
||||||
return passphraseBundle;
|
return passphraseBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign and encrypt
|
// sign and encrypt
|
||||||
builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
|
builder.signatureHashAlgorithm(accSettings.getHashAlgorithm())
|
||||||
.signatureForceV3(false)
|
.signatureForceV3(false)
|
||||||
.signatureKeyId(appSettings.getKeyId())
|
.signatureKeyId(accSettings.getKeyId())
|
||||||
.signaturePassphrase(passphrase);
|
.signaturePassphrase(passphrase);
|
||||||
} else {
|
} else {
|
||||||
// encrypt only
|
// encrypt only
|
||||||
@ -274,7 +276,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
|
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
|
||||||
ParcelFileDescriptor output, AppSettings appSettings) {
|
ParcelFileDescriptor output, AccountSettings accSettings) {
|
||||||
try {
|
try {
|
||||||
// Get Input- and OutputStream from ParcelFileDescriptor
|
// Get Input- and OutputStream from ParcelFileDescriptor
|
||||||
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
|
||||||
@ -289,7 +291,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
|
|
||||||
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
|
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
|
||||||
builder.assumeSymmetric(false) // no support for symmetric encryption
|
builder.assumeSymmetric(false) // no support for symmetric encryption
|
||||||
.enforcedKeyId(appSettings.getKeyId()) // allow only the private key for this app for decryption
|
// allow only the private key for this app for decryption
|
||||||
|
.enforcedKeyId(accSettings.getKeyId())
|
||||||
.passphrase(passphrase);
|
.passphrase(passphrase);
|
||||||
|
|
||||||
// TODO: currently does not support binary signed-only content
|
// TODO: currently does not support binary signed-only content
|
||||||
@ -297,7 +300,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
|
|
||||||
if (decryptVerifyResult.isKeyPassphraseNeeded()) {
|
if (decryptVerifyResult.isKeyPassphraseNeeded()) {
|
||||||
// get PendingIntent for passphrase input, add it to given params and return to client
|
// get PendingIntent for passphrase input, add it to given params and return to client
|
||||||
Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
|
Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
|
||||||
return passphraseBundle;
|
return passphraseBundle;
|
||||||
} else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
|
} else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
|
||||||
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
|
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
|
||||||
@ -314,8 +317,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
|
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
}
|
}
|
||||||
@ -354,8 +358,9 @@ public class OpenPgpService extends RemoteService {
|
|||||||
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
|
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
@ -403,7 +408,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
// version code is required and needs to correspond to version code of service!
|
// version code is required and needs to correspond to version code of service!
|
||||||
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
|
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
|
OpenPgpError error = new OpenPgpError
|
||||||
|
(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
|
||||||
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
|
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
|
||||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
|
||||||
return result;
|
return result;
|
||||||
@ -428,17 +434,26 @@ public class OpenPgpService extends RemoteService {
|
|||||||
return errorResult;
|
return errorResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AppSettings appSettings = getAppSettings();
|
String accName;
|
||||||
|
if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) {
|
||||||
|
accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME);
|
||||||
|
} else {
|
||||||
|
accName = "default";
|
||||||
|
}
|
||||||
|
final AccountSettings accSettings = getAccSettings(accName);
|
||||||
|
if (accSettings == null) {
|
||||||
|
return getCreateAccountIntent(data, accName);
|
||||||
|
}
|
||||||
|
|
||||||
String action = data.getAction();
|
String action = data.getAction();
|
||||||
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
|
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
|
||||||
return signImpl(data, input, output, appSettings);
|
return signImpl(data, input, output, accSettings);
|
||||||
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
|
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
|
||||||
return encryptAndSignImpl(data, input, output, appSettings, false);
|
return encryptAndSignImpl(data, input, output, accSettings, false);
|
||||||
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
|
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
|
||||||
return encryptAndSignImpl(data, input, output, appSettings, true);
|
return encryptAndSignImpl(data, input, output, accSettings, true);
|
||||||
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
|
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
|
||||||
return decryptAndVerifyImpl(data, input, output, appSettings);
|
return decryptAndVerifyImpl(data, input, output, accSettings);
|
||||||
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
|
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
|
||||||
return getKeyImpl(data);
|
return getKeyImpl(data);
|
||||||
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
@ -15,18 +15,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpError;
|
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
@ -39,16 +28,24 @@ import android.content.pm.Signature;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
|
|
||||||
|
import org.openintents.openpgp.OpenPgpError;
|
||||||
|
import org.openintents.openpgp.util.OpenPgpApi;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract service class for remote APIs that handle app registration and user input.
|
* Abstract service class for remote APIs that handle app registration and user input.
|
||||||
*/
|
*/
|
||||||
public abstract class RemoteService extends Service {
|
public abstract class RemoteService extends Service {
|
||||||
Context mContext;
|
Context mContext;
|
||||||
|
|
||||||
private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
|
|
||||||
private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
|
|
||||||
|
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return mContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
@ -56,13 +53,10 @@ public abstract class RemoteService extends Service {
|
|||||||
protected Intent isAllowed(Intent data) {
|
protected Intent isAllowed(Intent data) {
|
||||||
try {
|
try {
|
||||||
if (isCallerAllowed(false)) {
|
if (isCallerAllowed(false)) {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
String[] callingPackages = getPackageManager().getPackagesForUid(
|
String packageName = getCurrentCallingPackage();
|
||||||
Binder.getCallingUid());
|
Log.d(Constants.TAG, "isAllowed packageName: " + packageName);
|
||||||
// TODO: currently simply uses first entry
|
|
||||||
String packageName = callingPackages[0];
|
|
||||||
|
|
||||||
byte[] packageSignature;
|
byte[] packageSignature;
|
||||||
try {
|
try {
|
||||||
@ -84,7 +78,9 @@ public abstract class RemoteService extends Service {
|
|||||||
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
|
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
// return PendingIntent to be executed by client
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
@ -98,10 +94,13 @@ public abstract class RemoteService extends Service {
|
|||||||
|
|
||||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
|
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, getString(R.string.api_error_wrong_signature));
|
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
|
||||||
|
getString(R.string.api_error_wrong_signature));
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0);
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
// return PendingIntent to be executed by client
|
// return PendingIntent to be executed by client
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
@ -123,26 +122,57 @@ public abstract class RemoteService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves AppSettings from database for the application calling this remote service
|
* Returns package name associated with the UID, which is assigned to the process that sent you the
|
||||||
|
* current transaction that is being processed :)
|
||||||
|
*
|
||||||
|
* @return package name
|
||||||
|
*/
|
||||||
|
private String getCurrentCallingPackage() {
|
||||||
|
// TODO:
|
||||||
|
// callingPackages contains more than one entry when sharedUserId has been used...
|
||||||
|
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
||||||
|
String currentPkg = callingPackages[0];
|
||||||
|
Log.d(Constants.TAG, "currentPkg: " + currentPkg);
|
||||||
|
|
||||||
|
return currentPkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves AccountSettings from database for the application calling this remote service
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected AppSettings getAppSettings() {
|
protected AccountSettings getAccSettings(String accountName) {
|
||||||
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
String currentPkg = getCurrentCallingPackage();
|
||||||
|
Log.d(Constants.TAG, "accountName: " + accountName);
|
||||||
|
|
||||||
// get app settings for this package
|
Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName);
|
||||||
for (int i = 0; i < callingPackages.length; i++) {
|
|
||||||
String currentPkg = callingPackages[i];
|
|
||||||
|
|
||||||
Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg);
|
AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri);
|
||||||
|
|
||||||
AppSettings settings = ProviderHelper.getApiAppSettings(this, uri);
|
return settings; // can be null!
|
||||||
|
}
|
||||||
|
|
||||||
if (settings != null)
|
protected Intent getCreateAccountIntent(Intent data, String accountName) {
|
||||||
return settings;
|
String packageName = getCurrentCallingPackage();
|
||||||
}
|
Log.d(Constants.TAG, "accountName: " + accountName);
|
||||||
|
|
||||||
return null;
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
|
intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName);
|
||||||
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
|
// return PendingIntent to be executed by client
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
|
||||||
|
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,7 +207,7 @@ public abstract class RemoteService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(Constants.TAG, "Caller is NOT allowed!");
|
Log.d(Constants.TAG, "Uid is NOT allowed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +219,7 @@ public abstract class RemoteService extends Service {
|
|||||||
* @throws WrongPackageSignatureException
|
* @throws WrongPackageSignatureException
|
||||||
*/
|
*/
|
||||||
private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException {
|
private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException {
|
||||||
Log.d(Constants.TAG, "packageName: " + packageName);
|
Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
|
||||||
|
|
||||||
ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(this);
|
ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(this);
|
||||||
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
|
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
|
||||||
@ -217,6 +247,7 @@ public abstract class RemoteService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.remote;
|
||||||
|
|
||||||
|
public class WrongPackageSignatureException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -8294642703122196028L;
|
||||||
|
|
||||||
|
public WrongPackageSignatureException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -15,13 +15,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -31,17 +25,25 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
public class AppSettingsActivity extends ActionBarActivity {
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
private Uri mAppUri;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AccountSettings;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
private AppSettingsFragment mSettingsFragment;
|
public class AccountSettingsActivity extends ActionBarActivity {
|
||||||
|
private Uri mAccountUri;
|
||||||
|
|
||||||
|
private AccountSettingsFragment mAccountSettingsFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Inflate a "Done" custom action bar
|
// Inflate a "Done" custom action bar
|
||||||
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.api_settings_save,
|
ActionBarHelper.setOneButtonView(getSupportActionBar(),
|
||||||
|
R.string.api_settings_save, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -50,57 +52,58 @@ public class AppSettingsActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setContentView(R.layout.api_app_settings_activity);
|
setContentView(R.layout.api_account_settings_activity);
|
||||||
|
|
||||||
mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
|
mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
|
||||||
R.id.api_app_settings_fragment);
|
R.id.api_account_settings_fragment);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
mAppUri = intent.getData();
|
mAccountUri = intent.getData();
|
||||||
if (mAppUri == null) {
|
if (mAccountUri == null) {
|
||||||
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
|
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
Log.d(Constants.TAG, "uri: " + mAppUri);
|
Log.d(Constants.TAG, "uri: " + mAccountUri);
|
||||||
loadData(mAppUri);
|
loadData(savedInstanceState, mAccountUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
getMenuInflater().inflate(R.menu.api_app_settings, menu);
|
getMenuInflater().inflate(R.menu.api_account_settings, menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.menu_api_settings_revoke:
|
case R.id.menu_account_settings_delete:
|
||||||
revokeAccess();
|
deleteAccount();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_api_settings_cancel:
|
case R.id.menu_account_settings_cancel:
|
||||||
finish();
|
finish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadData(Uri appUri) {
|
private void loadData(Bundle savedInstanceState, Uri accountUri) {
|
||||||
AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
|
// TODO: load this also like other fragment with newInstance arguments?
|
||||||
mSettingsFragment.setAppSettings(settings);
|
AccountSettings settings = ProviderHelper.getApiAccountSettings(this, accountUri);
|
||||||
|
mAccountSettingsFragment.setAccSettings(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revokeAccess() {
|
private void deleteAccount() {
|
||||||
if (getContentResolver().delete(mAppUri, null, null) <= 0) {
|
if (getContentResolver().delete(mAccountUri, null, null) <= 0) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save() {
|
private void save() {
|
||||||
ProviderHelper.updateApiApp(this, mSettingsFragment.getAppSettings(), mAppUri);
|
ProviderHelper.updateApiAccount(this, mAccountSettingsFragment.getAccSettings(), mAccountUri);
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AccountSettings;
|
||||||
|
import org.sufficientlysecure.keychain.ui.EditKeyActivity;
|
||||||
|
import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
|
||||||
|
import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
|
||||||
|
import org.sufficientlysecure.keychain.util.AlgorithmNames;
|
||||||
|
|
||||||
|
public class AccountSettingsFragment extends Fragment implements
|
||||||
|
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
|
||||||
|
|
||||||
|
// model
|
||||||
|
private AccountSettings mAccSettings;
|
||||||
|
|
||||||
|
// view
|
||||||
|
private TextView mAccNameView;
|
||||||
|
private Spinner mEncryptionAlgorithm;
|
||||||
|
private Spinner mHashAlgorithm;
|
||||||
|
private Spinner mCompression;
|
||||||
|
|
||||||
|
private SelectSecretKeyLayoutFragment mSelectKeyFragment;
|
||||||
|
private BootstrapButton mCreateKeyButton;
|
||||||
|
|
||||||
|
KeyValueSpinnerAdapter mEncryptionAdapter;
|
||||||
|
KeyValueSpinnerAdapter mHashAdapter;
|
||||||
|
KeyValueSpinnerAdapter mCompressionAdapter;
|
||||||
|
|
||||||
|
public AccountSettings getAccSettings() {
|
||||||
|
return mAccSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccSettings(AccountSettings accountSettings) {
|
||||||
|
this.mAccSettings = accountSettings;
|
||||||
|
|
||||||
|
mAccNameView.setText(accountSettings.getAccountName());
|
||||||
|
mSelectKeyFragment.selectKey(accountSettings.getKeyId());
|
||||||
|
mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(accountSettings
|
||||||
|
.getEncryptionAlgorithm()));
|
||||||
|
mHashAlgorithm.setSelection(mHashAdapter.getPosition(accountSettings.getHashAlgorithm()));
|
||||||
|
mCompression.setSelection(mCompressionAdapter.getPosition(accountSettings.getCompression()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inflate the layout for this fragment
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.api_account_settings_fragment, container, false);
|
||||||
|
initView(view);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set error String on key selection
|
||||||
|
*
|
||||||
|
* @param error
|
||||||
|
*/
|
||||||
|
public void setErrorOnSelectKeyFragment(String error) {
|
||||||
|
mSelectKeyFragment.setError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView(View view) {
|
||||||
|
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
|
||||||
|
R.id.api_account_settings_select_key_fragment);
|
||||||
|
mSelectKeyFragment.setCallback(this);
|
||||||
|
|
||||||
|
mAccNameView = (TextView) view.findViewById(R.id.api_account_settings_acc_name);
|
||||||
|
mEncryptionAlgorithm = (Spinner) view
|
||||||
|
.findViewById(R.id.api_account_settings_encryption_algorithm);
|
||||||
|
mHashAlgorithm = (Spinner) view.findViewById(R.id.api_account_settings_hash_algorithm);
|
||||||
|
mCompression = (Spinner) view.findViewById(R.id.api_account_settings_compression);
|
||||||
|
mCreateKeyButton = (BootstrapButton) view.findViewById(R.id.api_account_settings_create_key);
|
||||||
|
|
||||||
|
mCreateKeyButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
createKey();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
|
||||||
|
|
||||||
|
mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
|
||||||
|
algorithmNames.getEncryptionNames());
|
||||||
|
mEncryptionAlgorithm.setAdapter(mEncryptionAdapter);
|
||||||
|
mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
mAccSettings.setEncryptionAlgorithm((int) id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
|
||||||
|
mHashAlgorithm.setAdapter(mHashAdapter);
|
||||||
|
mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
mAccSettings.setHashAlgorithm((int) id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
|
||||||
|
algorithmNames.getCompressionNames());
|
||||||
|
mCompression.setAdapter(mCompressionAdapter);
|
||||||
|
mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
mAccSettings.setCompression((int) id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createKey() {
|
||||||
|
Intent intent = new Intent(getActivity(), EditKeyActivity.class);
|
||||||
|
intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
|
||||||
|
intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
|
||||||
|
// set default user id to account name TODO: not working currently in EditKey
|
||||||
|
intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, mAccSettings.getAccountName());
|
||||||
|
startActivityForResult(intent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callback from select secret key fragment
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onKeySelected(long secretKeyId) {
|
||||||
|
mAccSettings.setKeyId(secretKeyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.ListFragment;
|
||||||
|
import android.support.v4.app.LoaderManager;
|
||||||
|
import android.support.v4.content.CursorLoader;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.ui.widget.FixedListView;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
public class AccountsListFragment extends ListFragment implements
|
||||||
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
|
private static final String ARG_DATA_URI = "uri";
|
||||||
|
|
||||||
|
// This is the Adapter being used to display the list's data.
|
||||||
|
AccountsAdapter mAdapter;
|
||||||
|
|
||||||
|
private Uri mDataUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new instance of this fragment
|
||||||
|
*/
|
||||||
|
public static AccountsListFragment newInstance(Uri dataUri) {
|
||||||
|
AccountsListFragment frag = new AccountsListFragment();
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelable(ARG_DATA_URI, dataUri);
|
||||||
|
|
||||||
|
frag.setArguments(args);
|
||||||
|
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View layout = super.onCreateView(inflater, container,
|
||||||
|
savedInstanceState);
|
||||||
|
ListView lv = (ListView) layout.findViewById(android.R.id.list);
|
||||||
|
ViewGroup parent = (ViewGroup) lv.getParent();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* http://stackoverflow.com/a/15880684
|
||||||
|
* Remove ListView and add FixedListView in its place.
|
||||||
|
* This is done here programatically to be still able to use the progressBar of ListFragment.
|
||||||
|
*
|
||||||
|
* We want FixedListView to be able to put this ListFragment inside a ScrollView
|
||||||
|
*/
|
||||||
|
int lvIndex = parent.indexOfChild(lv);
|
||||||
|
parent.removeViewAt(lvIndex);
|
||||||
|
FixedListView newLv = new FixedListView(getActivity());
|
||||||
|
newLv.setId(android.R.id.list);
|
||||||
|
parent.addView(newLv, lvIndex, lv.getLayoutParams());
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
|
mDataUri = getArguments().getParcelable(ARG_DATA_URI);
|
||||||
|
|
||||||
|
getListView().setOnItemClickListener(new OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||||
|
String selectedAccountName = mAdapter.getItemAccountName(position);
|
||||||
|
Uri accountUri = mDataUri.buildUpon().appendEncodedPath(selectedAccountName).build();
|
||||||
|
Log.d(Constants.TAG, "accountUri: " + accountUri);
|
||||||
|
|
||||||
|
// edit account settings
|
||||||
|
Intent intent = new Intent(getActivity(), AccountSettingsActivity.class);
|
||||||
|
intent.setData(accountUri);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Give some text to display if there is no data. In a real
|
||||||
|
// application this would come from a resource.
|
||||||
|
setEmptyText(getString(R.string.api_settings_accounts_empty));
|
||||||
|
|
||||||
|
// We have a menu item to show in action bar.
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
// Create an empty adapter we will use to display the loaded data.
|
||||||
|
mAdapter = new AccountsAdapter(getActivity(), null, 0);
|
||||||
|
setListAdapter(mAdapter);
|
||||||
|
|
||||||
|
// Prepare the loader. Either re-connect with an existing one,
|
||||||
|
// or start a new one.
|
||||||
|
getLoaderManager().initLoader(0, null, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the Contacts rows that we will retrieve.
|
||||||
|
static final String[] PROJECTION = new String[]{
|
||||||
|
KeychainContract.ApiAccounts._ID, // 0
|
||||||
|
KeychainContract.ApiAccounts.ACCOUNT_NAME // 1
|
||||||
|
};
|
||||||
|
|
||||||
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
|
// This is called when a new Loader needs to be created. This
|
||||||
|
// sample only has one Loader, so we don't care about the ID.
|
||||||
|
|
||||||
|
// Now create and return a CursorLoader that will take care of
|
||||||
|
// creating a Cursor for the data being displayed.
|
||||||
|
return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null,
|
||||||
|
KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
|
// Swap the new cursor in. (The framework will take care of closing the
|
||||||
|
// old cursor once we return.)
|
||||||
|
mAdapter.swapCursor(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoaderReset(Loader<Cursor> loader) {
|
||||||
|
// This is called when the last Cursor provided to onLoadFinished()
|
||||||
|
// above is about to be closed. We need to make sure we are no
|
||||||
|
// longer using it.
|
||||||
|
mAdapter.swapCursor(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AccountsAdapter extends CursorAdapter {
|
||||||
|
private LayoutInflater mInflater;
|
||||||
|
|
||||||
|
public AccountsAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
|
||||||
|
mInflater = LayoutInflater.from(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to CursorAdapter.getItemId().
|
||||||
|
* Required to build Uris for api app view, which is not based on row ids
|
||||||
|
*
|
||||||
|
* @param position
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getItemAccountName(int position) {
|
||||||
|
if (mDataValid && mCursor != null) {
|
||||||
|
if (mCursor.moveToPosition(position)) {
|
||||||
|
return mCursor.getString(1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
TextView text = (TextView) view.findViewById(R.id.api_accounts_adapter_item_name);
|
||||||
|
|
||||||
|
String accountName = cursor.getString(1);
|
||||||
|
text.setText(accountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return mInflater.inflate(R.layout.api_accounts_adapter_list_item, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AppSettings;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
public class AppSettingsActivity extends ActionBarActivity {
|
||||||
|
private Uri mAppUri;
|
||||||
|
|
||||||
|
private AppSettingsFragment mSettingsFragment;
|
||||||
|
private AccountsListFragment mAccountsListFragment;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// let the actionbar look like Android's contact app
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
actionBar.setIcon(android.R.color.transparent);
|
||||||
|
actionBar.setHomeButtonEnabled(true);
|
||||||
|
|
||||||
|
setContentView(R.layout.api_app_settings_activity);
|
||||||
|
|
||||||
|
mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
|
||||||
|
R.id.api_app_settings_fragment);
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
mAppUri = intent.getData();
|
||||||
|
if (mAppUri == null) {
|
||||||
|
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Log.d(Constants.TAG, "uri: " + mAppUri);
|
||||||
|
loadData(savedInstanceState, mAppUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
getMenuInflater().inflate(R.menu.api_app_settings, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_api_settings_revoke:
|
||||||
|
revokeAccess();
|
||||||
|
return true;
|
||||||
|
case R.id.menu_api_settings_cancel:
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadData(Bundle savedInstanceState, Uri appUri) {
|
||||||
|
// TODO: load this also like other fragment with newInstance arguments?
|
||||||
|
AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
|
||||||
|
mSettingsFragment.setAppSettings(settings);
|
||||||
|
|
||||||
|
String appName;
|
||||||
|
PackageManager pm = getPackageManager();
|
||||||
|
try {
|
||||||
|
ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0);
|
||||||
|
appName = (String) pm.getApplicationLabel(ai);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
// fallback
|
||||||
|
appName = settings.getPackageName();
|
||||||
|
}
|
||||||
|
setTitle(appName);
|
||||||
|
|
||||||
|
Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
|
||||||
|
Log.d(Constants.TAG, "accountsUri: " + accountsUri);
|
||||||
|
startListFragment(savedInstanceState, accountsUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startListFragment(Bundle savedInstanceState, Uri dataUri) {
|
||||||
|
// However, if we're being restored from a previous state,
|
||||||
|
// then we don't need to do anything and should return or else
|
||||||
|
// we could end up with overlapping fragments.
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an instance of the fragment
|
||||||
|
mAccountsListFragment = AccountsListFragment.newInstance(dataUri);
|
||||||
|
|
||||||
|
// Add the fragment to the 'fragment_container' FrameLayout
|
||||||
|
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
|
||||||
|
.commitAllowingStateLoss();
|
||||||
|
// do it immediately!
|
||||||
|
getSupportFragmentManager().executePendingTransactions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void revokeAccess() {
|
||||||
|
if (getContentResolver().delete(mAppUri, null, null) <= 0) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AppSettings;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public class AppSettingsFragment extends Fragment {
|
||||||
|
|
||||||
|
// model
|
||||||
|
private AppSettings mAppSettings;
|
||||||
|
|
||||||
|
// view
|
||||||
|
private TextView mAppNameView;
|
||||||
|
private ImageView mAppIconView;
|
||||||
|
private TextView mPackageName;
|
||||||
|
private TextView mPackageSignature;
|
||||||
|
|
||||||
|
public AppSettings getAppSettings() {
|
||||||
|
return mAppSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppSettings(AppSettings appSettings) {
|
||||||
|
this.mAppSettings = appSettings;
|
||||||
|
updateView(appSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inflate the layout for this fragment
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
|
||||||
|
mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
|
||||||
|
mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
|
||||||
|
mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
|
||||||
|
mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateView(AppSettings appSettings) {
|
||||||
|
// get application name and icon from package manager
|
||||||
|
String appName;
|
||||||
|
Drawable appIcon = null;
|
||||||
|
PackageManager pm = getActivity().getApplicationContext().getPackageManager();
|
||||||
|
try {
|
||||||
|
ApplicationInfo ai = pm.getApplicationInfo(appSettings.getPackageName(), 0);
|
||||||
|
|
||||||
|
appName = (String) pm.getApplicationLabel(ai);
|
||||||
|
appIcon = pm.getApplicationIcon(ai);
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
// fallback
|
||||||
|
appName = appSettings.getPackageName();
|
||||||
|
}
|
||||||
|
mAppNameView.setText(appName);
|
||||||
|
mAppIconView.setImageDrawable(appIcon);
|
||||||
|
|
||||||
|
// advanced info: package name
|
||||||
|
mPackageName.setText(appSettings.getPackageName());
|
||||||
|
|
||||||
|
// advanced info: package signature SHA-256
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
|
md.update(appSettings.getPackageSignature());
|
||||||
|
byte[] digest = md.digest();
|
||||||
|
String signature = new String(Hex.encode(digest));
|
||||||
|
|
||||||
|
mPackageSignature.setText(signature);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.e(Constants.TAG, "Should not happen!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -15,14 +15,13 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.ui.DrawerActivity;
|
import org.sufficientlysecure.keychain.ui.DrawerActivity;
|
||||||
|
|
||||||
import android.os.Bundle;
|
public class AppsListActivity extends DrawerActivity {
|
||||||
|
|
||||||
public class RegisteredAppsListActivity extends DrawerActivity {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
@ -15,14 +15,12 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import android.content.Context;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
|
||||||
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -30,11 +28,22 @@ import android.support.v4.app.ListFragment;
|
|||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.AdapterView.OnItemClickListener;
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class RegisteredAppsListFragment extends ListFragment implements
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
public class AppsListFragment extends ListFragment implements
|
||||||
LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
// This is the Adapter being used to display the list's data.
|
// This is the Adapter being used to display the list's data.
|
||||||
@ -47,9 +56,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
|
|||||||
getListView().setOnItemClickListener(new OnItemClickListener() {
|
getListView().setOnItemClickListener(new OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
|
||||||
|
String selectedPackageName = mAdapter.getItemPackageName(position);
|
||||||
// edit app settings
|
// edit app settings
|
||||||
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
|
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
|
||||||
intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
|
intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -71,7 +81,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
// These are the Contacts rows that we will retrieve.
|
// These are the Contacts rows that we will retrieve.
|
||||||
static final String[] PROJECTION = new String[] { ApiApps._ID, ApiApps.PACKAGE_NAME };
|
static final String[] PROJECTION = new String[]{
|
||||||
|
ApiApps._ID, // 0
|
||||||
|
ApiApps.PACKAGE_NAME // 1
|
||||||
|
};
|
||||||
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
// This is called when a new Loader needs to be created. This
|
// This is called when a new Loader needs to be created. This
|
||||||
@ -99,4 +112,65 @@ public class RegisteredAppsListFragment extends ListFragment implements
|
|||||||
mAdapter.swapCursor(null);
|
mAdapter.swapCursor(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RegisteredAppsAdapter extends CursorAdapter {
|
||||||
|
|
||||||
|
private LayoutInflater mInflater;
|
||||||
|
private PackageManager mPM;
|
||||||
|
|
||||||
|
public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
|
||||||
|
mInflater = LayoutInflater.from(context);
|
||||||
|
mPM = context.getApplicationContext().getPackageManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to CursorAdapter.getItemId().
|
||||||
|
* Required to build Uris for api app view, which is not based on row ids
|
||||||
|
*
|
||||||
|
* @param position
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getItemPackageName(int position) {
|
||||||
|
if (mDataValid && mCursor != null) {
|
||||||
|
if (mCursor.moveToPosition(position)) {
|
||||||
|
return mCursor.getString(1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
|
||||||
|
ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
|
||||||
|
|
||||||
|
String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
|
||||||
|
if (packageName != null) {
|
||||||
|
// get application name
|
||||||
|
try {
|
||||||
|
ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
|
||||||
|
|
||||||
|
text.setText(mPM.getApplicationLabel(ai));
|
||||||
|
icon.setImageDrawable(mPM.getApplicationIcon(ai));
|
||||||
|
} catch (final PackageManager.NameNotFoundException e) {
|
||||||
|
// fallback
|
||||||
|
text.setText(packageName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fallback
|
||||||
|
text.setText(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -15,7 +15,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
package org.sufficientlysecure.keychain.remote.ui;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -32,7 +32,10 @@ import org.sufficientlysecure.keychain.Id;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AccountSettings;
|
||||||
|
import org.sufficientlysecure.keychain.remote.AppSettings;
|
||||||
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
|
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;
|
||||||
@ -42,6 +45,8 @@ import java.util.ArrayList;
|
|||||||
public class RemoteServiceActivity extends ActionBarActivity {
|
public class RemoteServiceActivity extends ActionBarActivity {
|
||||||
|
|
||||||
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
|
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
|
||||||
|
public static final String ACTION_CREATE_ACCOUNT = Constants.INTENT_PREFIX
|
||||||
|
+ "API_ACTIVITY_CREATE_ACCOUNT";
|
||||||
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
|
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
|
||||||
+ "API_ACTIVITY_CACHE_PASSPHRASE";
|
+ "API_ACTIVITY_CACHE_PASSPHRASE";
|
||||||
public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX
|
public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX
|
||||||
@ -58,6 +63,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
// register action
|
// register action
|
||||||
public static final String EXTRA_PACKAGE_NAME = "package_name";
|
public static final String EXTRA_PACKAGE_NAME = "package_name";
|
||||||
public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature";
|
public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature";
|
||||||
|
// create acc action
|
||||||
|
public static final String EXTRA_ACC_NAME = "acc_name";
|
||||||
// select pub keys action
|
// select pub keys action
|
||||||
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
|
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
|
||||||
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
|
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
|
||||||
@ -66,7 +73,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
public static final String EXTRA_ERROR_MESSAGE = "error_message";
|
public static final String EXTRA_ERROR_MESSAGE = "error_message";
|
||||||
|
|
||||||
// register view
|
// register view
|
||||||
private AppSettingsFragment mSettingsFragment;
|
private AppSettingsFragment mAppSettingsFragment;
|
||||||
|
// create acc view
|
||||||
|
private AccountSettingsFragment mAccSettingsFragment;
|
||||||
// select pub keys view
|
// select pub keys view
|
||||||
private SelectPublicKeyFragment mSelectFragment;
|
private SelectPublicKeyFragment mSelectFragment;
|
||||||
|
|
||||||
@ -86,29 +95,26 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
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);
|
||||||
|
Log.d(Constants.TAG, "ACTION_REGISTER packageName: "+packageName);
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.api_register_allow,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
|
||||||
|
R.string.api_register_allow, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// Allow
|
// Allow
|
||||||
|
|
||||||
// user needs to select a key!
|
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
|
||||||
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
|
mAppSettingsFragment.getAppSettings());
|
||||||
mSettingsFragment.setErrorOnSelectKeyFragment(
|
|
||||||
getString(R.string.api_register_error_select_key));
|
|
||||||
} else {
|
|
||||||
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
|
|
||||||
mSettingsFragment.getAppSettings());
|
|
||||||
|
|
||||||
// give data through for new service call
|
// give data through for new service call
|
||||||
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
||||||
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||||
RemoteServiceActivity.this.finish();
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, R.string.api_register_disallow, new View.OnClickListener() {
|
}, R.string.api_register_disallow, R.drawable.ic_action_cancel,
|
||||||
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// Disallow
|
// Disallow
|
||||||
@ -118,13 +124,58 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
setContentView(R.layout.api_app_register_activity);
|
setContentView(R.layout.api_remote_register_app);
|
||||||
|
|
||||||
mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
|
mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
|
||||||
R.id.api_app_settings_fragment);
|
R.id.api_app_settings_fragment);
|
||||||
|
|
||||||
AppSettings settings = new AppSettings(packageName, packageSignature);
|
AppSettings settings = new AppSettings(packageName, packageSignature);
|
||||||
mSettingsFragment.setAppSettings(settings);
|
mAppSettingsFragment.setAppSettings(settings);
|
||||||
|
} else if (ACTION_CREATE_ACCOUNT.equals(action)) {
|
||||||
|
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
|
||||||
|
final String accName = extras.getString(EXTRA_ACC_NAME);
|
||||||
|
|
||||||
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
|
||||||
|
R.string.api_settings_save, R.drawable.ic_action_done,
|
||||||
|
new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// Save
|
||||||
|
|
||||||
|
// user needs to select a key!
|
||||||
|
if (mAccSettingsFragment.getAccSettings().getKeyId() == Id.key.none) {
|
||||||
|
mAccSettingsFragment.setErrorOnSelectKeyFragment(
|
||||||
|
getString(R.string.api_register_error_select_key));
|
||||||
|
} else {
|
||||||
|
ProviderHelper.insertApiAccount(RemoteServiceActivity.this,
|
||||||
|
KeychainContract.ApiAccounts.buildBaseUri(packageName),
|
||||||
|
mAccSettingsFragment.getAccSettings());
|
||||||
|
|
||||||
|
// give data through for new service call
|
||||||
|
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
||||||
|
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||||
|
RemoteServiceActivity.this.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, R.string.api_settings_cancel, R.drawable.ic_action_cancel,
|
||||||
|
new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
// Cancel
|
||||||
|
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
|
||||||
|
RemoteServiceActivity.this.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
setContentView(R.layout.api_remote_create_account);
|
||||||
|
|
||||||
|
mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
|
||||||
|
R.id.api_account_settings_fragment);
|
||||||
|
|
||||||
|
AccountSettings settings = new AccountSettings(accName);
|
||||||
|
mAccSettingsFragment.setAccSettings(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);
|
||||||
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
Intent resultData = extras.getParcelable(EXTRA_DATA);
|
||||||
@ -161,7 +212,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inflate a "Done"/"Cancel" custom action bar view
|
// Inflate a "Done"/"Cancel" custom action bar view
|
||||||
ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_okay,
|
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
|
||||||
|
R.string.btn_okay, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -173,7 +225,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
|
||||||
RemoteServiceActivity.this.finish();
|
RemoteServiceActivity.this.finish();
|
||||||
}
|
}
|
||||||
}, R.string.btn_do_not_save, new View.OnClickListener() {
|
}, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// cancel
|
// cancel
|
||||||
@ -183,7 +235,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
setContentView(R.layout.api_app_select_pub_keys_activity);
|
setContentView(R.layout.api_remote_select_pub_keys);
|
||||||
|
|
||||||
// set text on view
|
// set text on view
|
||||||
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
|
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
|
||||||
@ -214,7 +266,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
String text = "<font color=\"red\">" + errorMessage + "</font>";
|
String 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.setOneButtonView(getSupportActionBar(),
|
||||||
|
R.string.btn_okay, R.drawable.ic_action_done,
|
||||||
new View.OnClickListener() {
|
new View.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -224,7 +277,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setContentView(R.layout.api_app_error_message);
|
setContentView(R.layout.api_remote_error_message);
|
||||||
|
|
||||||
// set text on view
|
// set text on view
|
||||||
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
|
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
|
@ -17,47 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.service;
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.GregorianCalendar;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
|
||||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
|
||||||
import org.sufficientlysecure.keychain.util.HkpKeyServer;
|
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
|
|
||||||
|
|
||||||
import android.app.IntentService;
|
import android.app.IntentService;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -67,12 +26,32 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import org.spongycastle.openpgp.*;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||||
|
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||||
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.*;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
|
||||||
|
import org.sufficientlysecure.keychain.util.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Service contains all important long lasting operations for APG. It receives Intents with
|
* This Service contains all important long lasting operations for APG. It receives Intents with
|
||||||
* data from the activities or other apps, queues these intents, executes them, and stops itself
|
* data from the activities or other apps, queues these intents, executes them, and stops itself
|
||||||
* after doing them.
|
* after doing them.
|
||||||
*/
|
*/
|
||||||
public class KeychainIntentService extends IntentService implements ProgressDialogUpdater {
|
public class KeychainIntentService extends IntentService
|
||||||
|
implements ProgressDialogUpdater, KeychainServiceListener {
|
||||||
|
|
||||||
/* extras that can be given by intent */
|
/* extras that can be given by intent */
|
||||||
public static final String EXTRA_MESSENGER = "messenger";
|
public static final String EXTRA_MESSENGER = "messenger";
|
||||||
@ -159,6 +138,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
// sign key
|
// sign key
|
||||||
public static final String CERTIFY_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 CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
|
public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
|
||||||
|
public static final String CERTIFY_KEY_UIDS = "sign_key_uids";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* possible data keys as result send over messenger
|
* possible data keys as result send over messenger
|
||||||
@ -324,8 +304,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
builder.enableAsciiArmorOutput(useAsciiArmor)
|
builder.enableAsciiArmorOutput(useAsciiArmor)
|
||||||
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
||||||
.signatureKeyId(secretKeyId)
|
.signatureKeyId(secretKeyId)
|
||||||
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
.signatureHashAlgorithm(
|
||||||
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||||
|
.signaturePassphrase(
|
||||||
|
PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
builder.build().generateSignature();
|
builder.build().generateSignature();
|
||||||
} else if (signOnly) {
|
} else if (signOnly) {
|
||||||
@ -333,21 +315,26 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
builder.enableAsciiArmorOutput(useAsciiArmor)
|
builder.enableAsciiArmorOutput(useAsciiArmor)
|
||||||
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
||||||
.signatureKeyId(secretKeyId)
|
.signatureKeyId(secretKeyId)
|
||||||
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
.signatureHashAlgorithm(
|
||||||
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||||
|
.signaturePassphrase(
|
||||||
|
PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
builder.build().execute();
|
builder.build().execute();
|
||||||
} else {
|
} else {
|
||||||
Log.d(Constants.TAG, "encrypt...");
|
Log.d(Constants.TAG, "encrypt...");
|
||||||
builder.enableAsciiArmorOutput(useAsciiArmor)
|
builder.enableAsciiArmorOutput(useAsciiArmor)
|
||||||
.compressionId(compressionId)
|
.compressionId(compressionId)
|
||||||
.symmetricEncryptionAlgorithm(Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
|
.symmetricEncryptionAlgorithm(
|
||||||
|
Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
|
||||||
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
|
||||||
.encryptionKeyIds(encryptionKeyIds)
|
.encryptionKeyIds(encryptionKeyIds)
|
||||||
.encryptionPassphrase(encryptionPassphrase)
|
.encryptionPassphrase(encryptionPassphrase)
|
||||||
.signatureKeyId(secretKeyId)
|
.signatureKeyId(secretKeyId)
|
||||||
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
.signatureHashAlgorithm(
|
||||||
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
Preferences.getPreferences(this).getDefaultHashAlgorithm())
|
||||||
|
.signaturePassphrase(
|
||||||
|
PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
|
||||||
|
|
||||||
builder.build().execute();
|
builder.build().execute();
|
||||||
}
|
}
|
||||||
@ -586,13 +573,24 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
|
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
|
int keysTotal = 2;
|
||||||
|
int keysCreated = 0;
|
||||||
|
setProgress(
|
||||||
|
getApplicationContext().getResources().
|
||||||
|
getQuantityString(R.plurals.progress_generating, keysTotal),
|
||||||
|
keysCreated,
|
||||||
|
keysTotal);
|
||||||
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
|
||||||
|
|
||||||
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||||
4096, passphrase, true);
|
4096, passphrase, true);
|
||||||
|
keysCreated++;
|
||||||
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
|
||||||
4096, passphrase, false);
|
4096, passphrase, false);
|
||||||
|
keysCreated++;
|
||||||
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
// TODO: default to one master for cert, one sub for encrypt and one sub
|
// TODO: default to one master for cert, one sub for encrypt and one sub
|
||||||
// for sign
|
// for sign
|
||||||
@ -652,14 +650,11 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
if (data.containsKey(EXPORT_KEY_TYPE)) {
|
if (data.containsKey(EXPORT_KEY_TYPE)) {
|
||||||
keyType = data.getInt(EXPORT_KEY_TYPE);
|
keyType = data.getInt(EXPORT_KEY_TYPE);
|
||||||
}
|
}
|
||||||
|
long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
|
||||||
String outputFile = data.getString(EXPORT_FILENAME);
|
String outputFile = data.getString(EXPORT_FILENAME);
|
||||||
|
|
||||||
|
// If not exporting all keys get the masterKeyIds of the keys to export from the intent
|
||||||
boolean exportAll = data.getBoolean(EXPORT_ALL);
|
boolean exportAll = data.getBoolean(EXPORT_ALL);
|
||||||
long keyRingMasterKeyId = -1;
|
|
||||||
if (!exportAll) {
|
|
||||||
keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
|
|
||||||
@ -668,27 +663,42 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
|
throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutputStream
|
ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
|
||||||
FileOutputStream outStream = new FileOutputStream(outputFile);
|
ArrayList<Long> secretMasterKeyIds = new ArrayList<Long>();
|
||||||
|
ArrayList<Long> allPublicMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
|
||||||
|
ArrayList<Long> allSecretMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
|
||||||
|
|
||||||
ArrayList<Long> keyRingMasterKeyIds = new ArrayList<Long>();
|
|
||||||
if (exportAll) {
|
if (exportAll) {
|
||||||
// get all key ring row ids based on export type
|
// get all public key ring MasterKey ids
|
||||||
|
if (keyType == Id.type.public_key || keyType == Id.type.public_secret_key) {
|
||||||
if (keyType == Id.type.public_key) {
|
publicMasterKeyIds = allPublicMasterKeyIds;
|
||||||
keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
|
}
|
||||||
} else {
|
// get all secret key ring MasterKey ids
|
||||||
keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);
|
if (keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) {
|
||||||
|
secretMasterKeyIds = allSecretMasterKeyIds;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
keyRingMasterKeyIds.add(keyRingMasterKeyId);
|
|
||||||
|
for (long masterKeyId : masterKeyIds) {
|
||||||
|
if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key)
|
||||||
|
&& allPublicMasterKeyIds.contains(masterKeyId)) {
|
||||||
|
publicMasterKeyIds.add(masterKeyId);
|
||||||
|
}
|
||||||
|
if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key)
|
||||||
|
&& allSecretMasterKeyIds.contains(masterKeyId)) {
|
||||||
|
secretMasterKeyIds.add(masterKeyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle resultData = new Bundle();
|
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
|
||||||
|
Bundle resultData = pgpImportExport
|
||||||
|
.exportKeyRings(publicMasterKeyIds, secretMasterKeyIds,
|
||||||
|
new FileOutputStream(outputFile));
|
||||||
|
|
||||||
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
|
if (mIsCanceled) {
|
||||||
resultData = pgpImportExport
|
boolean isDeleted = new File(outputFile).delete();
|
||||||
.exportKeyRings(keyRingMasterKeyIds, keyType, outStream);
|
}
|
||||||
|
|
||||||
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
|
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -724,48 +734,58 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
|
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
|
||||||
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
|
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
|
||||||
|
|
||||||
|
// TODO: add extra which requires fingerprint suport and force verification!
|
||||||
|
// only supported by newer sks keyserver versions
|
||||||
|
|
||||||
// this downloads the keys and places them into the ImportKeysListEntry entries
|
// this downloads the keys and places them into the ImportKeysListEntry entries
|
||||||
HkpKeyServer server = new HkpKeyServer(keyServer);
|
HkpKeyServer server = new HkpKeyServer(keyServer);
|
||||||
|
|
||||||
for (ImportKeysListEntry entry : entries) {
|
for (ImportKeysListEntry entry : entries) {
|
||||||
byte[] downloadedKey = server.get(entry.getKeyId()).getBytes();
|
// if available use complete fingerprint for get request
|
||||||
|
byte[] downloadedKeyBytes;
|
||||||
|
if (entry.getFingerPrintHex() != null) {
|
||||||
|
downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
|
||||||
|
} else {
|
||||||
|
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// create PGPKeyRing object based on downloaded armored key
|
||||||
* TODO: copied from ImportKeysListLoader
|
PGPKeyRing downloadedKey = null;
|
||||||
*
|
BufferedInputStream bufferedInput =
|
||||||
*
|
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
|
||||||
* this parses the downloaded key
|
if (bufferedInput.available() > 0) {
|
||||||
*/
|
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
||||||
// need to have access to the bufferedInput, so we can reuse it for the possible
|
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
||||||
// PGPObject chunks after the first one, e.g. files with several consecutive ASCII
|
|
||||||
// armor blocks
|
|
||||||
BufferedInputStream bufferedInput = new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
|
|
||||||
try {
|
|
||||||
|
|
||||||
// read all available blocks... (asc files can contain many blocks with BEGIN END)
|
// get first object in block
|
||||||
while (bufferedInput.available() > 0) {
|
Object obj;
|
||||||
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
if ((obj = objectFactory.nextObject()) != null) {
|
||||||
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
Log.d(Constants.TAG, "Found class: " + obj.getClass());
|
||||||
|
|
||||||
// go through all objects in this block
|
if (obj instanceof PGPKeyRing) {
|
||||||
Object obj;
|
downloadedKey = (PGPKeyRing) obj;
|
||||||
while ((obj = objectFactory.nextObject()) != null) {
|
} else {
|
||||||
Log.d(Constants.TAG, "Found class: " + obj.getClass());
|
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
|
||||||
|
|
||||||
if (obj instanceof PGPKeyRing) {
|
|
||||||
PGPKeyRing newKeyring = (PGPKeyRing) obj;
|
|
||||||
|
|
||||||
entry.setBytes(newKeyring.getEncoded());
|
|
||||||
} else {
|
|
||||||
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(Constants.TAG, "Exception on parsing key file!", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify downloaded key by comparing fingerprints
|
||||||
|
if (entry.getFingerPrintHex() != null) {
|
||||||
|
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(downloadedKey.getPublicKey().getFingerprint());
|
||||||
|
if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
|
||||||
|
Log.d(Constants.TAG, "fingerprint of downloaded key is the same as the requested fingerprint!");
|
||||||
|
} else {
|
||||||
|
throw new PgpGeneralException("fingerprint of downloaded key is NOT the same as the requested fingerprint!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save key bytes in entry object for doing the
|
||||||
|
// actual import afterwards
|
||||||
|
entry.setBytes(downloadedKey.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Intent importIntent = new Intent(this, KeychainIntentService.class);
|
Intent importIntent = new Intent(this, KeychainIntentService.class);
|
||||||
importIntent.setAction(ACTION_IMPORT_KEYRING);
|
importIntent.setAction(ACTION_IMPORT_KEYRING);
|
||||||
Bundle importData = new Bundle();
|
Bundle importData = new Bundle();
|
||||||
@ -786,6 +806,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
/* Input */
|
/* Input */
|
||||||
long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
|
long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
|
||||||
long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
|
long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
|
||||||
|
ArrayList<String> userIds = data.getStringArrayList(CERTIFY_KEY_UIDS);
|
||||||
|
|
||||||
/* Operation */
|
/* Operation */
|
||||||
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
|
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
|
||||||
@ -793,7 +814,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
|
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
|
||||||
PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
|
PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
|
||||||
signaturePassPhrase);
|
userIds, signaturePassPhrase);
|
||||||
|
|
||||||
// store the signed key in our local cache
|
// store the signed key in our local cache
|
||||||
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
|
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
|
||||||
@ -811,10 +832,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
private void sendErrorToHandler(Exception e) {
|
private void sendErrorToHandler(Exception e) {
|
||||||
// Service was canceled. Do not send error to handler.
|
// Service was canceled. Do not send error to handler.
|
||||||
if (this.mIsCanceled)
|
if (this.mIsCanceled) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
Log.e(Constants.TAG, "ApgService Exception: ", e);
|
Log.e(Constants.TAG, "KeychainIntentService Exception: ", e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
@ -824,9 +845,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
|
|
||||||
private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) {
|
private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) {
|
||||||
// Service was canceled. Do not send message to handler.
|
// Service was canceled. Do not send message to handler.
|
||||||
if (this.mIsCanceled)
|
if (this.mIsCanceled) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
Message msg = Message.obtain();
|
Message msg = Message.obtain();
|
||||||
msg.arg1 = arg1;
|
msg.arg1 = arg1;
|
||||||
if (arg2 != null) {
|
if (arg2 != null) {
|
||||||
@ -877,4 +898,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
|||||||
public void setProgress(int progress, int max) {
|
public void setProgress(int progress, int max) {
|
||||||
setProgress(null, progress, max);
|
setProgress(null, progress, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasServiceStopped() {
|
||||||
|
return mIsCanceled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,7 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.service;
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.DialogInterface.OnCancelListener;
|
import android.content.DialogInterface.OnCancelListener;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -29,6 +25,8 @@ import android.os.Message;
|
|||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||||
|
|
||||||
public class KeychainIntentServiceHandler extends Handler {
|
public class KeychainIntentServiceHandler extends Handler {
|
||||||
|
|
||||||
@ -51,25 +49,31 @@ public class KeychainIntentServiceHandler extends Handler {
|
|||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeychainIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) {
|
public KeychainIntentServiceHandler(Activity activity,
|
||||||
|
ProgressDialogFragment progressDialogFragment) {
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
this.mProgressDialogFragment = progressDialogFragment;
|
this.mProgressDialogFragment = progressDialogFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) {
|
public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
|
||||||
this(activity, progressDialogMessageId, progressDialogStyle, false, null);
|
int progressDialogStyle) {
|
||||||
|
this(activity, progressDialogMessage, progressDialogStyle, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId,
|
public KeychainIntentServiceHandler(Activity activity, String progressDialogMessage,
|
||||||
int progressDialogStyle, boolean cancelable,
|
int progressDialogStyle, boolean cancelable,
|
||||||
OnCancelListener onCancelListener) {
|
OnCancelListener onCancelListener) {
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId,
|
this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
|
||||||
progressDialogStyle, cancelable, onCancelListener);
|
progressDialogMessage,
|
||||||
|
progressDialogStyle,
|
||||||
|
cancelable,
|
||||||
|
onCancelListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showProgressDialog(FragmentActivity activity) {
|
public void showProgressDialog(FragmentActivity activity) {
|
||||||
// TODO: This is a hack!, see http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
|
// TODO: This is a hack!, see
|
||||||
|
// http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
|
||||||
final FragmentManager manager = activity.getSupportFragmentManager();
|
final FragmentManager manager = activity.getSupportFragmentManager();
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@ -84,43 +88,43 @@ public class KeychainIntentServiceHandler extends Handler {
|
|||||||
Bundle data = message.getData();
|
Bundle data = message.getData();
|
||||||
|
|
||||||
switch (message.arg1) {
|
switch (message.arg1) {
|
||||||
case MESSAGE_OKAY:
|
case MESSAGE_OKAY:
|
||||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MESSAGE_EXCEPTION:
|
case MESSAGE_EXCEPTION:
|
||||||
mProgressDialogFragment.dismissAllowingStateLoss();
|
mProgressDialogFragment.dismissAllowingStateLoss();
|
||||||
|
|
||||||
// show error from service
|
// show error from service
|
||||||
if (data.containsKey(DATA_ERROR)) {
|
if (data.containsKey(DATA_ERROR)) {
|
||||||
Toast.makeText(mActivity,
|
Toast.makeText(mActivity,
|
||||||
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MESSAGE_UPDATE_PROGRESS:
|
|
||||||
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
|
|
||||||
|
|
||||||
// update progress from service
|
|
||||||
if (data.containsKey(DATA_MESSAGE)) {
|
|
||||||
mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
|
|
||||||
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
|
||||||
} else if (data.containsKey(DATA_MESSAGE_ID)) {
|
|
||||||
mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
|
|
||||||
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
|
||||||
} else {
|
|
||||||
mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
|
|
||||||
data.getInt(DATA_PROGRESS_MAX));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case MESSAGE_UPDATE_PROGRESS:
|
||||||
break;
|
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
|
||||||
|
|
||||||
|
// update progress from service
|
||||||
|
if (data.containsKey(DATA_MESSAGE)) {
|
||||||
|
mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
|
||||||
|
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
||||||
|
} else if (data.containsKey(DATA_MESSAGE_ID)) {
|
||||||
|
mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
|
||||||
|
data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
|
||||||
|
} else {
|
||||||
|
mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
|
||||||
|
data.getInt(DATA_PROGRESS_MAX));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,16 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.service;
|
package org.sufficientlysecure.keychain.service;
|
||||||
|
|
||||||
import java.util.Date;
|
import android.app.AlarmManager;
|
||||||
import java.util.HashMap;
|
import android.app.PendingIntent;
|
||||||
import java.util.Iterator;
|
import android.app.Service;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.*;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.support.v4.util.LongSparseArray;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
@ -33,28 +39,13 @@ import org.sufficientlysecure.keychain.helper.Preferences;
|
|||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
import java.util.Date;
|
||||||
import android.app.PendingIntent;
|
import java.util.Iterator;
|
||||||
import android.app.Service;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service runs in its own process, but is available to all other processes as the main
|
* This service runs in its own process, but is available to all other processes as the main
|
||||||
* passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
|
* passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
|
||||||
* convenience.
|
* convenience.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class PassphraseCacheService extends Service {
|
public class PassphraseCacheService extends Service {
|
||||||
public static final String TAG = Constants.TAG + ": PassphraseCacheService";
|
public static final String TAG = Constants.TAG + ": PassphraseCacheService";
|
||||||
@ -77,7 +68,7 @@ public class PassphraseCacheService extends Service {
|
|||||||
|
|
||||||
private BroadcastReceiver mIntentReceiver;
|
private BroadcastReceiver mIntentReceiver;
|
||||||
|
|
||||||
private HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
|
private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>();
|
||||||
|
|
||||||
Context mContext;
|
Context mContext;
|
||||||
|
|
||||||
@ -215,17 +206,17 @@ public class PassphraseCacheService extends Service {
|
|||||||
.getPGPSecretKeyRingByKeyId(context, secretKeyId);
|
.getPGPSecretKeyRingByKeyId(context, secretKeyId);
|
||||||
PGPSecretKey secretKey = null;
|
PGPSecretKey secretKey = null;
|
||||||
boolean foundValidKey = false;
|
boolean foundValidKey = false;
|
||||||
for (Iterator keys = secRing.getSecretKeys(); keys.hasNext();) {
|
for (Iterator keys = secRing.getSecretKeys(); keys.hasNext(); ) {
|
||||||
secretKey = (PGPSecretKey)keys.next();
|
secretKey = (PGPSecretKey) keys.next();
|
||||||
if (!secretKey.isPrivateKeyEmpty()) {
|
if (!secretKey.isPrivateKeyEmpty()) {
|
||||||
foundValidKey = true;
|
foundValidKey = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!foundValidKey)
|
if (!foundValidKey) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
"SC").build("".toCharArray());
|
"SC").build("".toCharArray());
|
||||||
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
|
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
|
||||||
@ -347,7 +338,7 @@ public class PassphraseCacheService extends Service {
|
|||||||
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
|
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
|
||||||
|
|
||||||
// stop whole service if no cached passphrases remaining
|
// stop whole service if no cached passphrases remaining
|
||||||
if (mPassphraseCache.isEmpty()) {
|
if (mPassphraseCache.size() == 0) {
|
||||||
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
|
Log.d(TAG, "No passphrases remaining in memory, stopping service!");
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
|
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
|
|
||||||
import org.sufficientlysecure.keychain.util.AlgorithmNames;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.animation.AlphaAnimation;
|
|
||||||
import android.view.animation.Animation;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
|
||||||
|
|
||||||
public class AppSettingsFragment extends Fragment implements
|
|
||||||
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
|
|
||||||
|
|
||||||
// model
|
|
||||||
private AppSettings appSettings;
|
|
||||||
|
|
||||||
// view
|
|
||||||
private LinearLayout mAdvancedSettingsContainer;
|
|
||||||
private BootstrapButton mAdvancedSettingsButton;
|
|
||||||
private TextView mAppNameView;
|
|
||||||
private ImageView mAppIconView;
|
|
||||||
private Spinner mEncryptionAlgorithm;
|
|
||||||
private Spinner mHashAlgorithm;
|
|
||||||
private Spinner mCompression;
|
|
||||||
private TextView mPackageName;
|
|
||||||
private TextView mPackageSignature;
|
|
||||||
|
|
||||||
private SelectSecretKeyLayoutFragment mSelectKeyFragment;
|
|
||||||
|
|
||||||
KeyValueSpinnerAdapter encryptionAdapter;
|
|
||||||
KeyValueSpinnerAdapter hashAdapter;
|
|
||||||
KeyValueSpinnerAdapter compressionAdapter;
|
|
||||||
|
|
||||||
public AppSettings getAppSettings() {
|
|
||||||
return appSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAppSettings(AppSettings appSettings) {
|
|
||||||
this.appSettings = appSettings;
|
|
||||||
setPackage(appSettings.getPackageName());
|
|
||||||
mPackageName.setText(appSettings.getPackageName());
|
|
||||||
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
||||||
md.update(appSettings.getPackageSignature());
|
|
||||||
byte[] digest = md.digest();
|
|
||||||
String signature = new String(Hex.encode(digest));
|
|
||||||
|
|
||||||
mPackageSignature.setText(signature);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
Log.e(Constants.TAG, "Should not happen!", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
mSelectKeyFragment.selectKey(appSettings.getKeyId());
|
|
||||||
mEncryptionAlgorithm.setSelection(encryptionAdapter.getPosition(appSettings
|
|
||||||
.getEncryptionAlgorithm()));
|
|
||||||
mHashAlgorithm.setSelection(hashAdapter.getPosition(appSettings.getHashAlgorithm()));
|
|
||||||
mCompression.setSelection(compressionAdapter.getPosition(appSettings.getCompression()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inflate the layout for this fragment
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
|
|
||||||
initView(view);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set error String on key selection
|
|
||||||
*
|
|
||||||
* @param error
|
|
||||||
*/
|
|
||||||
public void setErrorOnSelectKeyFragment(String error) {
|
|
||||||
mSelectKeyFragment.setError(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initView(View view) {
|
|
||||||
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
|
|
||||||
R.id.api_app_settings_select_key_fragment);
|
|
||||||
mSelectKeyFragment.setCallback(this);
|
|
||||||
|
|
||||||
mAdvancedSettingsButton = (BootstrapButton) view
|
|
||||||
.findViewById(R.id.api_app_settings_advanced_button);
|
|
||||||
mAdvancedSettingsContainer = (LinearLayout) view
|
|
||||||
.findViewById(R.id.api_app_settings_advanced);
|
|
||||||
|
|
||||||
mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
|
|
||||||
mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
|
|
||||||
mEncryptionAlgorithm = (Spinner) view
|
|
||||||
.findViewById(R.id.api_app_settings_encryption_algorithm);
|
|
||||||
mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
|
|
||||||
mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression);
|
|
||||||
mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
|
|
||||||
mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
|
|
||||||
|
|
||||||
AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
|
|
||||||
|
|
||||||
encryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
|
|
||||||
algorithmNames.getEncryptionNames());
|
|
||||||
mEncryptionAlgorithm.setAdapter(encryptionAdapter);
|
|
||||||
mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
appSettings.setEncryptionAlgorithm((int) id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
hashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
|
|
||||||
mHashAlgorithm.setAdapter(hashAdapter);
|
|
||||||
mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
appSettings.setHashAlgorithm((int) id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
compressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
|
|
||||||
algorithmNames.getCompressionNames());
|
|
||||||
mCompression.setAdapter(compressionAdapter);
|
|
||||||
mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
appSettings.setCompression((int) id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f);
|
|
||||||
visibleAnimation.setDuration(250);
|
|
||||||
final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f);
|
|
||||||
invisibleAnimation.setDuration(250);
|
|
||||||
|
|
||||||
// TODO: Better: collapse/expand animation
|
|
||||||
// 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);u
|
|
||||||
// animation2.setDuration(150);
|
|
||||||
|
|
||||||
mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
|
|
||||||
mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
|
|
||||||
mAdvancedSettingsContainer.setVisibility(View.GONE);
|
|
||||||
mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced));
|
|
||||||
mAdvancedSettingsButton.setLeftIcon("fa-caret-up");
|
|
||||||
} else {
|
|
||||||
mAdvancedSettingsContainer.startAnimation(visibleAnimation);
|
|
||||||
mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
|
|
||||||
mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced));
|
|
||||||
mAdvancedSettingsButton.setLeftIcon("fa-caret-down");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPackage(String packageName) {
|
|
||||||
PackageManager pm = getActivity().getApplicationContext().getPackageManager();
|
|
||||||
|
|
||||||
// get application name and icon from package manager
|
|
||||||
String appName = null;
|
|
||||||
Drawable appIcon = null;
|
|
||||||
try {
|
|
||||||
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
|
|
||||||
|
|
||||||
appName = (String) pm.getApplicationLabel(ai);
|
|
||||||
appIcon = pm.getApplicationIcon(ai);
|
|
||||||
} catch (final NameNotFoundException e) {
|
|
||||||
// fallback
|
|
||||||
appName = packageName;
|
|
||||||
}
|
|
||||||
mAppNameView.setText(appName);
|
|
||||||
mAppIconView.setImageDrawable(appIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback from select secret key fragment
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onKeySelected(long secretKeyId) {
|
|
||||||
appSettings.setKeyId(secretKeyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.service.remote;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.support.v4.widget.CursorAdapter;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
public class RegisteredAppsAdapter extends CursorAdapter {
|
|
||||||
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
private PackageManager pm;
|
|
||||||
|
|
||||||
public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
|
|
||||||
super(context, c, flags);
|
|
||||||
|
|
||||||
mInflater = LayoutInflater.from(context);
|
|
||||||
pm = context.getApplicationContext().getPackageManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
|
|
||||||
ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
|
|
||||||
|
|
||||||
String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
|
|
||||||
if (packageName != null) {
|
|
||||||
// get application name
|
|
||||||
try {
|
|
||||||
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
|
|
||||||
|
|
||||||
text.setText(pm.getApplicationLabel(ai));
|
|
||||||
icon.setImageDrawable(pm.getApplicationIcon(ai));
|
|
||||||
} catch (final NameNotFoundException e) {
|
|
||||||
// fallback
|
|
||||||
text.setText(packageName);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// fallback
|
|
||||||
text.setText(packageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
|
||||||
return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package org.sufficientlysecure.keychain.service.remote;
|
|
||||||
|
|
||||||
public class WrongPackageSignatureException extends Exception {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -8294642703122196028L;
|
|
||||||
|
|
||||||
public WrongPackageSignatureException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,47 +17,47 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
|
import android.support.v4.app.LoaderManager;
|
||||||
|
import android.support.v4.content.CursorLoader;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.*;
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||||
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
|
import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the specified public key with the specified secret master key
|
* Signs the specified public key with the specified secret master key
|
||||||
*/
|
*/
|
||||||
public class CertifyKeyActivity extends ActionBarActivity implements
|
public class CertifyKeyActivity extends ActionBarActivity implements
|
||||||
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
|
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
private BootstrapButton mSignButton;
|
private BootstrapButton mSignButton;
|
||||||
private CheckBox mUploadKeyCheckbox;
|
private CheckBox mUploadKeyCheckbox;
|
||||||
private Spinner mSelectKeyserverSpinner;
|
private Spinner mSelectKeyserverSpinner;
|
||||||
@ -68,6 +68,12 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
private long mPubKeyId = 0;
|
private long mPubKeyId = 0;
|
||||||
private long mMasterKeyId = 0;
|
private long mMasterKeyId = 0;
|
||||||
|
|
||||||
|
private ListView mUserIds;
|
||||||
|
private ViewKeyUserIdsAdapter mUserIdsAdapter;
|
||||||
|
|
||||||
|
private static final int LOADER_ID_KEYRING = 0;
|
||||||
|
private static final int LOADER_ID_USER_IDS = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -87,7 +93,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
|
mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
|
||||||
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
|
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
|
||||||
.getKeyServers());
|
.getKeyServers());
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
mSelectKeyserverSpinner.setAdapter(adapter);
|
mSelectKeyserverSpinner.setAdapter(adapter);
|
||||||
|
|
||||||
@ -131,9 +137,18 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Log.e(Constants.TAG, "uri: " + mDataUri);
|
||||||
|
|
||||||
PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri);
|
PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri);
|
||||||
|
|
||||||
|
mUserIds = (ListView) findViewById(R.id.user_ids);
|
||||||
|
|
||||||
|
mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true);
|
||||||
|
mUserIds.setAdapter(mUserIdsAdapter);
|
||||||
|
|
||||||
|
getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
|
||||||
|
getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
|
||||||
|
|
||||||
if (signKey != null) {
|
if (signKey != null) {
|
||||||
mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
|
mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
|
||||||
}
|
}
|
||||||
@ -144,6 +159,78 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final String[] KEYRING_PROJECTION =
|
||||||
|
new String[] {
|
||||||
|
KeychainContract.KeyRings._ID,
|
||||||
|
KeychainContract.KeyRings.MASTER_KEY_ID,
|
||||||
|
KeychainContract.Keys.FINGERPRINT,
|
||||||
|
KeychainContract.UserIds.USER_ID
|
||||||
|
};
|
||||||
|
static final int INDEX_MASTER_KEY_ID = 1;
|
||||||
|
static final int INDEX_FINGERPRINT = 2;
|
||||||
|
static final int INDEX_USER_ID = 3;
|
||||||
|
|
||||||
|
static final String[] USER_IDS_PROJECTION =
|
||||||
|
new String[]{
|
||||||
|
KeychainContract.UserIds._ID,
|
||||||
|
KeychainContract.UserIds.USER_ID,
|
||||||
|
KeychainContract.UserIds.RANK
|
||||||
|
};
|
||||||
|
static final String USER_IDS_SORT_ORDER =
|
||||||
|
KeychainContract.UserIds.RANK + " ASC";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
|
switch(id) {
|
||||||
|
case LOADER_ID_KEYRING:
|
||||||
|
return new CursorLoader(this, mDataUri, KEYRING_PROJECTION, null, null, null);
|
||||||
|
case LOADER_ID_USER_IDS: {
|
||||||
|
Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
|
||||||
|
return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||||
|
switch(loader.getId()) {
|
||||||
|
case LOADER_ID_KEYRING:
|
||||||
|
// the first key here is our master key
|
||||||
|
if (data.moveToFirst()) {
|
||||||
|
// TODO: put findViewById in onCreate!
|
||||||
|
|
||||||
|
long keyId = data.getLong(INDEX_MASTER_KEY_ID);
|
||||||
|
String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(keyId);
|
||||||
|
((TextView) findViewById(R.id.key_id)).setText(keyIdStr);
|
||||||
|
|
||||||
|
String mainUserId = data.getString(INDEX_USER_ID);
|
||||||
|
((TextView) findViewById(R.id.main_user_id)).setText(mainUserId);
|
||||||
|
|
||||||
|
byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
|
||||||
|
if (fingerprintBlob == null) {
|
||||||
|
// FALLBACK for old database entries
|
||||||
|
fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri);
|
||||||
|
}
|
||||||
|
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
|
||||||
|
((TextView) findViewById(R.id.fingerprint)).setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LOADER_ID_USER_IDS:
|
||||||
|
mUserIdsAdapter.swapCursor(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<Cursor> loader) {
|
||||||
|
switch(loader.getId()) {
|
||||||
|
case LOADER_ID_USER_IDS:
|
||||||
|
mUserIdsAdapter.swapCursor(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void showPassphraseDialog(final long secretKeyId) {
|
private void showPassphraseDialog(final long secretKeyId) {
|
||||||
// Message is received after passphrase is cached
|
// Message is received after passphrase is cached
|
||||||
Handler returnHandler = new Handler() {
|
Handler returnHandler = new Handler() {
|
||||||
@ -179,6 +266,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
// if we have already signed this key, dont bother doing it again
|
// if we have already signed this key, dont bother doing it again
|
||||||
boolean alreadySigned = false;
|
boolean alreadySigned = false;
|
||||||
|
|
||||||
|
/* todo: reconsider this at a later point when certs are in the db
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
|
Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
|
||||||
while (itr.hasNext()) {
|
while (itr.hasNext()) {
|
||||||
@ -188,6 +276,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (!alreadySigned) {
|
if (!alreadySigned) {
|
||||||
/*
|
/*
|
||||||
@ -215,6 +304,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
* kicks off the actual signing process on a background thread
|
* kicks off the actual signing process on a background thread
|
||||||
*/
|
*/
|
||||||
private void startSigning() {
|
private void startSigning() {
|
||||||
|
|
||||||
|
// Bail out if there is not at least one user id selected
|
||||||
|
ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();
|
||||||
|
if(userIds.isEmpty()) {
|
||||||
|
Toast.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
@ -225,14 +323,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId);
|
data.putLong(KeychainIntentService.CERTIFY_KEY_MASTER_KEY_ID, mMasterKeyId);
|
||||||
data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId);
|
data.putLong(KeychainIntentService.CERTIFY_KEY_PUB_KEY_ID, mPubKeyId);
|
||||||
|
data.putStringArrayList(KeychainIntentService.CERTIFY_KEY_UIDS, userIds);
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after signing is done in ApgService
|
// Message is received after signing is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_signing, ProgressDialog.STYLE_SPINNER) {
|
getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -249,7 +348,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -281,11 +380,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after uploading is done in ApgService
|
// Message is received after uploading is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -295,7 +394,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
|
|||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -17,37 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
|
|
||||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
|
||||||
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
|
||||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -59,16 +28,34 @@ import android.os.Messenger;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.animation.AnimationUtils;
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.CheckBox;
|
import android.widget.*;
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import android.widget.ViewFlipper;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
import com.devspark.appmsg.AppMsg;
|
import com.devspark.appmsg.AppMsg;
|
||||||
|
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
|
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
|
||||||
|
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
public class DecryptActivity extends DrawerActivity {
|
public class DecryptActivity extends DrawerActivity {
|
||||||
@ -364,7 +351,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG,
|
Log.e(Constants.TAG,
|
||||||
"Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!");
|
"Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!");
|
||||||
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
|
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
|
||||||
.show();
|
.show();
|
||||||
// end activity
|
// end activity
|
||||||
@ -383,7 +370,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
|
if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
|
||||||
filename = filename.substring(0, filename.length() - 4);
|
filename = filename.substring(0, filename.length() - 4);
|
||||||
}
|
}
|
||||||
mOutputFilename = Constants.path.APP_DIR + "/" + filename;
|
mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSource() {
|
private void updateSource() {
|
||||||
@ -456,8 +443,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
getDecryptionKeyFromInputStream();
|
getDecryptionKeyFromInputStream();
|
||||||
|
|
||||||
// if we need a symmetric passphrase or a passphrase to use a secret key ask for it
|
// if we need a symmetric passphrase or a passphrase to use a secret key ask for it
|
||||||
if (mSecretKeyId == Id.key.symmetric
|
if (mAssumeSymmetricEncryption || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
|
||||||
|| PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
|
|
||||||
showPassphraseDialog();
|
showPassphraseDialog();
|
||||||
} else {
|
} else {
|
||||||
if (mDecryptTarget == Id.target.file) {
|
if (mDecryptTarget == Id.target.file) {
|
||||||
@ -507,6 +493,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
* TODO: Rework function, remove global variables
|
* TODO: Rework function, remove global variables
|
||||||
*/
|
*/
|
||||||
private void getDecryptionKeyFromInputStream() {
|
private void getDecryptionKeyFromInputStream() {
|
||||||
|
mAssumeSymmetricEncryption = false;
|
||||||
InputStream inStream = null;
|
InputStream inStream = null;
|
||||||
if (mContentUri != null) {
|
if (mContentUri != null) {
|
||||||
try {
|
try {
|
||||||
@ -546,7 +533,6 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
if (mSecretKeyId == Id.key.none) {
|
if (mSecretKeyId == Id.key.none) {
|
||||||
throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
|
throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
|
||||||
}
|
}
|
||||||
mAssumeSymmetricEncryption = false;
|
|
||||||
} catch (NoAsymmetricEncryptionException e) {
|
} catch (NoAsymmetricEncryptionException e) {
|
||||||
if (inStream.markSupported()) {
|
if (inStream.markSupported()) {
|
||||||
inStream.reset();
|
inStream.reset();
|
||||||
@ -559,6 +545,7 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
mAssumeSymmetricEncryption = true;
|
mAssumeSymmetricEncryption = true;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Log.e(Constants.TAG, "error while reading decryption key from input stream", e);
|
||||||
AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
|
AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
|
||||||
AppMsg.STYLE_ALERT).show();
|
AppMsg.STYLE_ALERT).show();
|
||||||
}
|
}
|
||||||
@ -644,11 +631,11 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after encrypting is done in ApgService
|
// Message is received after encrypting is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -744,8 +731,6 @@ public class DecryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
|
@ -17,30 +17,21 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ActionBarDrawerToggle;
|
import android.support.v4.app.ActionBarDrawerToggle;
|
||||||
import android.support.v4.view.GravityCompat;
|
import android.support.v4.view.GravityCompat;
|
||||||
import android.support.v4.widget.DrawerLayout;
|
import android.support.v4.widget.DrawerLayout;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.*;
|
||||||
import android.view.Menu;
|
import android.widget.*;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.FontAwesomeText;
|
import com.beardedhen.androidbootstrap.FontAwesomeText;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
|
||||||
public class DrawerActivity extends ActionBarActivity {
|
public class DrawerActivity extends ActionBarActivity {
|
||||||
private DrawerLayout mDrawerLayout;
|
private DrawerLayout mDrawerLayout;
|
||||||
@ -49,10 +40,8 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
|
|
||||||
private CharSequence mDrawerTitle;
|
private CharSequence mDrawerTitle;
|
||||||
private CharSequence mTitle;
|
private CharSequence mTitle;
|
||||||
|
private boolean mIsDrawerLocked = false;
|
||||||
|
|
||||||
private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
|
|
||||||
EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
|
|
||||||
KeyListSecretActivity.class, RegisteredAppsListActivity.class };
|
|
||||||
private Class mSelectedItem;
|
private Class mSelectedItem;
|
||||||
|
|
||||||
private static final int MENU_ID_PREFERENCE = 222;
|
private static final int MENU_ID_PREFERENCE = 222;
|
||||||
@ -62,18 +51,29 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
mDrawerTitle = getString(R.string.app_name);
|
mDrawerTitle = getString(R.string.app_name);
|
||||||
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||||
mDrawerList = (ListView) findViewById(R.id.left_drawer);
|
mDrawerList = (ListView) findViewById(R.id.left_drawer);
|
||||||
|
ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame);
|
||||||
|
int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin;
|
||||||
|
int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size);
|
||||||
|
int errorInMarginAllowed = 5;
|
||||||
|
|
||||||
// set a custom shadow that overlays the main content when the drawer
|
// if the left margin of the loaded layout is close to the
|
||||||
// opens
|
// one used in tablets then set drawer as open and locked
|
||||||
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
|
if( Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) {
|
||||||
|
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList);
|
||||||
|
mDrawerLayout.setScrimColor(Color.TRANSPARENT);
|
||||||
|
mIsDrawerLocked = true;
|
||||||
|
} else {
|
||||||
|
// set a custom shadow that overlays the main content when the drawer opens
|
||||||
|
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
|
||||||
|
mIsDrawerLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
NavItem mItemIconTexts[] = new NavItem[] {
|
NavItem mItemIconTexts[] = new NavItem[]{
|
||||||
new NavItem("fa-user", getString(R.string.nav_contacts)),
|
new NavItem("fa-user", getString(R.string.nav_contacts)),
|
||||||
new NavItem("fa-lock", getString(R.string.nav_encrypt)),
|
new NavItem("fa-lock", getString(R.string.nav_encrypt)),
|
||||||
new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
|
new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
|
||||||
new NavItem("fa-download", getString(R.string.nav_import)),
|
new NavItem("fa-download", getString(R.string.nav_import)),
|
||||||
new NavItem("fa-key", getString(R.string.nav_secret_keys)),
|
new NavItem("fa-android", getString(R.string.nav_apps))};
|
||||||
new NavItem("fa-android", getString(R.string.nav_apps)) };
|
|
||||||
|
|
||||||
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
|
mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
|
||||||
mItemIconTexts));
|
mItemIconTexts));
|
||||||
@ -81,32 +81,24 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
|
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
|
||||||
|
|
||||||
// enable ActionBar app icon to behave as action to toggle nav drawer
|
// enable ActionBar app icon to behave as action to toggle nav drawer
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
// if the drawer is not locked
|
||||||
getSupportActionBar().setHomeButtonEnabled(true);
|
if ( !mIsDrawerLocked ) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setHomeButtonEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
// ActionBarDrawerToggle ties together the the proper interactions
|
// ActionBarDrawerToggle ties together the the proper interactions
|
||||||
// between the sliding drawer and the action bar app icon
|
// between the sliding drawer and the action bar app icon
|
||||||
mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
|
mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
|
||||||
mDrawerLayout, /* DrawerLayout object */
|
mDrawerLayout, /* DrawerLayout object */
|
||||||
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
|
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
|
||||||
R.string.drawer_open, /* "open drawer" description for accessibility */
|
R.string.drawer_open, /* "open drawer" description for accessibility */
|
||||||
R.string.drawer_close /* "close drawer" description for accessibility */
|
R.string.drawer_close /* "close drawer" description for accessibility */
|
||||||
) {
|
) {
|
||||||
public void onDrawerClosed(View view) {
|
public void onDrawerClosed(View view) {
|
||||||
getSupportActionBar().setTitle(mTitle);
|
getSupportActionBar().setTitle(mTitle);
|
||||||
// creates call to onPrepareOptionsMenu()
|
|
||||||
supportInvalidateOptionsMenu();
|
|
||||||
|
|
||||||
// call intent activity if selected
|
callIntentForDrawerItem(mSelectedItem);
|
||||||
if(mSelectedItem != null) {
|
|
||||||
finish();
|
|
||||||
overridePendingTransition(0, 0);
|
|
||||||
|
|
||||||
Intent intent = new Intent(DrawerActivity.this, mSelectedItem);
|
|
||||||
startActivity(intent);
|
|
||||||
// disable animation of activity start
|
|
||||||
overridePendingTransition(0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDrawerOpened(View drawerView) {
|
public void onDrawerOpened(View drawerView) {
|
||||||
@ -116,13 +108,40 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
supportInvalidateOptionsMenu();
|
supportInvalidateOptionsMenu();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
|
||||||
|
|
||||||
|
if ( !mIsDrawerLocked ) {
|
||||||
|
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||||
|
} else {
|
||||||
|
// If the drawer is locked open make it un-focusable
|
||||||
|
// so that it doesn't consume all the Back button presses
|
||||||
|
mDrawerLayout.setFocusableInTouchMode(false);
|
||||||
|
}
|
||||||
// if (savedInstanceState == null) {
|
// if (savedInstanceState == null) {
|
||||||
// selectItem(0);
|
// selectItem(0);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses startActivity to call the Intent of the given class
|
||||||
|
* @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.*
|
||||||
|
*/
|
||||||
|
public void callIntentForDrawerItem(Class drawerItem) {
|
||||||
|
// creates call to onPrepareOptionsMenu()
|
||||||
|
supportInvalidateOptionsMenu();
|
||||||
|
|
||||||
|
// call intent activity if selected
|
||||||
|
if (drawerItem != null) {
|
||||||
|
finish();
|
||||||
|
overridePendingTransition(0, 0);
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, drawerItem);
|
||||||
|
startActivity(intent);
|
||||||
|
|
||||||
|
// disable animation of activity start
|
||||||
|
overridePendingTransition(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
|
menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
|
||||||
@ -150,18 +169,18 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case MENU_ID_PREFERENCE: {
|
case MENU_ID_PREFERENCE: {
|
||||||
Intent intent = new Intent(this, PreferencesActivity.class);
|
Intent intent = new Intent(this, PreferencesActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case MENU_ID_HELP: {
|
case MENU_ID_HELP: {
|
||||||
Intent intent = new Intent(this, HelpActivity.class);
|
Intent intent = new Intent(this, HelpActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle action buttons
|
// Handle action buttons
|
||||||
@ -193,10 +212,18 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
private void selectItem(int position) {
|
private void selectItem(int position) {
|
||||||
// update selected item and title, then close the drawer
|
// update selected item and title, then close the drawer
|
||||||
mDrawerList.setItemChecked(position, true);
|
mDrawerList.setItemChecked(position, true);
|
||||||
// setTitle(mDrawerTitles[position]);
|
|
||||||
mDrawerLayout.closeDrawer(mDrawerList);
|
|
||||||
// set selected class
|
// set selected class
|
||||||
mSelectedItem = mItemsClass[position];
|
mSelectedItem = Constants.DrawerItems.ARRAY[position];
|
||||||
|
|
||||||
|
// setTitle(mDrawerTitles[position]);
|
||||||
|
// If drawer isn't locked just close the drawer and
|
||||||
|
// it will move to the selected item by itself (via drawer toggle listener)
|
||||||
|
if ( !mIsDrawerLocked ) {
|
||||||
|
mDrawerLayout.closeDrawer(mDrawerList);
|
||||||
|
// else move to the selected item yourself
|
||||||
|
} else {
|
||||||
|
callIntentForDrawerItem(mSelectedItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,15 +256,15 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> {
|
private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> {
|
||||||
Context context;
|
Context mContext;
|
||||||
int layoutResourceId;
|
int mLayoutResourceId;
|
||||||
NavItem data[] = null;
|
NavItem mData[] = null;
|
||||||
|
|
||||||
public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) {
|
public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) {
|
||||||
super(context, layoutResourceId, data);
|
super(context, layoutResourceId, data);
|
||||||
this.layoutResourceId = layoutResourceId;
|
this.mLayoutResourceId = layoutResourceId;
|
||||||
this.context = context;
|
this.mContext = context;
|
||||||
this.data = data;
|
this.mData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -246,21 +273,21 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
NavItemHolder holder = null;
|
NavItemHolder holder = null;
|
||||||
|
|
||||||
if (row == null) {
|
if (row == null) {
|
||||||
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
|
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
|
||||||
row = inflater.inflate(layoutResourceId, parent, false);
|
row = inflater.inflate(mLayoutResourceId, parent, false);
|
||||||
|
|
||||||
holder = new NavItemHolder();
|
holder = new NavItemHolder();
|
||||||
holder.img = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon);
|
holder.mImg = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon);
|
||||||
holder.txtTitle = (TextView) row.findViewById(R.id.drawer_item_text);
|
holder.mTxtTitle = (TextView) row.findViewById(R.id.drawer_item_text);
|
||||||
|
|
||||||
row.setTag(holder);
|
row.setTag(holder);
|
||||||
} else {
|
} else {
|
||||||
holder = (NavItemHolder) row.getTag();
|
holder = (NavItemHolder) row.getTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
NavItem item = data[position];
|
NavItem item = mData[position];
|
||||||
holder.txtTitle.setText(item.title);
|
holder.mTxtTitle.setText(item.title);
|
||||||
holder.img.setIcon(item.icon);
|
holder.mImg.setIcon(item.icon);
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
@ -268,8 +295,8 @@ public class DrawerActivity extends ActionBarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class NavItemHolder {
|
static class NavItemHolder {
|
||||||
FontAwesomeText img;
|
FontAwesomeText mImg;
|
||||||
TextView txtTitle;
|
TextView mTxtTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -23,6 +23,25 @@ import java.util.List;
|
|||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
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.*;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
@ -32,6 +51,7 @@ import org.sufficientlysecure.keychain.helper.ExportHelper;
|
|||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||||
@ -49,31 +69,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
|
|||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.support.v7.app.ActionBarActivity;
|
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
|
||||||
|
|
||||||
public class EditKeyActivity extends ActionBarActivity implements EditorListener {
|
public class EditKeyActivity extends ActionBarActivity implements EditorListener {
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
Vector<String> mUserIds;
|
Vector<String> mUserIds;
|
||||||
Vector<PGPSecretKey> mKeys;
|
Vector<PGPSecretKey> mKeys;
|
||||||
Vector<Integer> mKeysUsages;
|
Vector<Integer> mKeysUsages;
|
||||||
boolean masterCanSign = true;
|
boolean mMasterCanSign = true;
|
||||||
|
|
||||||
ExportHelper mExportHelper;
|
ExportHelper mExportHelper;
|
||||||
|
|
||||||
@ -211,9 +212,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
|
|
||||||
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after generating is done in ApgService
|
// Message is received after generating is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||||
this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER, true,
|
this, getResources().getQuantityString(R.plurals.progress_generating, 1),
|
||||||
|
ProgressDialog.STYLE_HORIZONTAL, true,
|
||||||
|
|
||||||
new DialogInterface.OnCancelListener() {
|
new DialogInterface.OnCancelListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -227,7 +229,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -283,12 +285,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
} else {
|
} else {
|
||||||
Log.d(Constants.TAG, "uri: " + mDataUri);
|
Log.d(Constants.TAG, "uri: " + mDataUri);
|
||||||
|
|
||||||
long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
|
|
||||||
|
|
||||||
// get master key id using row id
|
// get master key id using row id
|
||||||
long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId);
|
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
|
||||||
|
|
||||||
masterCanSign = ProviderHelper.getSecretMasterKeyCanCertify(this, keyRingRowId);
|
mMasterCanSign = ProviderHelper.getMasterKeyCanCertify(this, mDataUri);
|
||||||
finallyEdit(masterKeyId);
|
finallyEdit(masterKeyId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,11 +350,16 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
if (needsSaving()) {
|
if (needsSaving()) {
|
||||||
Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
|
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
|
||||||
+ "/secexport.asc");
|
long[] ids = new long[]{masterKeyId};
|
||||||
|
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC,
|
||||||
|
null);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_key_edit_delete: {
|
case R.id.menu_key_edit_delete:
|
||||||
|
long rowId= ProviderHelper.getRowId(this,mDataUri);
|
||||||
|
Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
|
||||||
// Message is received after key is deleted
|
// Message is received after key is deleted
|
||||||
Handler returnHandler = new Handler() {
|
Handler returnHandler = new Handler() {
|
||||||
@Override
|
@Override
|
||||||
@ -363,12 +368,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}};
|
||||||
};
|
mExportHelper.deleteKey(convertUri, returnHandler);
|
||||||
|
|
||||||
mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
case R.id.menu_key_edit_save:
|
case R.id.menu_key_edit_save:
|
||||||
saveClicked();
|
saveClicked();
|
||||||
return true;
|
return true;
|
||||||
@ -407,8 +410,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
mCurrentPassphrase = "";
|
mCurrentPassphrase = "";
|
||||||
|
|
||||||
buildLayout(false);
|
buildLayout(false);
|
||||||
|
|
||||||
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
|
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
|
||||||
if (!mIsPassPhraseSet) {
|
if (!mIsPassPhraseSet) {
|
||||||
// check "no passphrase" checkbox and remove button
|
// check "no passphrase" checkbox and remove button
|
||||||
@ -466,20 +469,23 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
// find views
|
// find views
|
||||||
mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase);
|
mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase);
|
||||||
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
|
mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
|
||||||
|
|
||||||
// Build layout based on given userIds and keys
|
// Build layout based on given userIds and keys
|
||||||
|
|
||||||
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
|
||||||
LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container);
|
LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container);
|
||||||
|
if(mIsPassPhraseSet){
|
||||||
|
mChangePassphrase.setText(getString(R.string.btn_change_passphrase));
|
||||||
|
}
|
||||||
mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
|
mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
|
||||||
mUserIdsView.setType(Id.type.user_id);
|
mUserIdsView.setType(Id.type.user_id);
|
||||||
mUserIdsView.setCanEdit(masterCanSign);
|
mUserIdsView.setCanEdit(mMasterCanSign);
|
||||||
mUserIdsView.setUserIds(mUserIds);
|
mUserIdsView.setUserIds(mUserIds);
|
||||||
mUserIdsView.setEditorListener(this);
|
mUserIdsView.setEditorListener(this);
|
||||||
container.addView(mUserIdsView);
|
container.addView(mUserIdsView);
|
||||||
mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
|
mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
|
||||||
mKeysView.setType(Id.type.key);
|
mKeysView.setType(Id.type.key);
|
||||||
mKeysView.setCanEdit(masterCanSign);
|
mKeysView.setCanEdit(mMasterCanSign);
|
||||||
mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
|
mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
|
||||||
mKeysView.setEditorListener(this);
|
mKeysView.setEditorListener(this);
|
||||||
container.addView(mKeysView);
|
container.addView(mKeysView);
|
||||||
@ -602,17 +608,16 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
|
|
||||||
// fill values for this action
|
// fill values for this action
|
||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
|
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
|
||||||
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign);
|
|
||||||
data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
|
data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after saving is done in ApgService
|
// Message is received after saving is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -640,8 +645,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
// start service with intent
|
// start service with intent
|
||||||
startService(intent);
|
startService(intent);
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
|
Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
|
||||||
Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
|
Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,11 +698,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
|
|||||||
for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
|
for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
|
||||||
UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
|
UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
|
||||||
String userId;
|
String userId;
|
||||||
try {
|
userId = editor.getValue();
|
||||||
userId = editor.getValue();
|
|
||||||
} catch (UserIdEditor.InvalidEmailException e) {
|
|
||||||
throw new PgpGeneralException(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editor.isMainUserId()) {
|
if (editor.isMainUserId()) {
|
||||||
userIds.add(0, userId);
|
userIds.add(0, userId);
|
||||||
|
@ -17,9 +17,22 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import java.io.File;
|
import android.app.ProgressDialog;
|
||||||
import java.util.Vector;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
|
import android.widget.*;
|
||||||
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
import com.beardedhen.androidbootstrap.FontAwesomeText;
|
||||||
|
import com.devspark.appmsg.AppMsg;
|
||||||
import org.spongycastle.openpgp.PGPPublicKey;
|
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;
|
||||||
@ -43,27 +56,8 @@ import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
|||||||
import org.sufficientlysecure.keychain.util.Choice;
|
import org.sufficientlysecure.keychain.util.Choice;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
import java.io.File;
|
||||||
import android.content.Intent;
|
import java.util.Vector;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.OnClickListener;
|
|
||||||
import android.view.animation.AnimationUtils;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import android.widget.ViewFlipper;
|
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
|
||||||
import com.devspark.appmsg.AppMsg;
|
|
||||||
|
|
||||||
public class EncryptActivity extends DrawerActivity {
|
public class EncryptActivity extends DrawerActivity {
|
||||||
|
|
||||||
@ -108,6 +102,7 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
private EditText mFilename = null;
|
private EditText mFilename = null;
|
||||||
private CheckBox mDeleteAfter = null;
|
private CheckBox mDeleteAfter = null;
|
||||||
|
private CheckBox mShareAfter = null;
|
||||||
private BootstrapButton mBrowse = null;
|
private BootstrapButton mBrowse = null;
|
||||||
|
|
||||||
private String mInputFilename = null;
|
private String mInputFilename = null;
|
||||||
@ -602,11 +597,11 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Message is received after encrypting is done in ApgService
|
// Message is received after encrypting is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
||||||
R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) {
|
getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
public void handleMessage(Message message) {
|
public void handleMessage(Message message) {
|
||||||
// handle messages by standard ApgHandler first
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
super.handleMessage(message);
|
super.handleMessage(message);
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
@ -650,6 +645,15 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
.newInstance(mInputFilename);
|
.newInstance(mInputFilename);
|
||||||
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
|
deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mShareAfter.isChecked()) {
|
||||||
|
// Share encrypted file
|
||||||
|
Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendFileIntent.setType("*/*");
|
||||||
|
sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
|
||||||
|
startActivity(Intent.createChooser(sendFileIntent,
|
||||||
|
getString(R.string.title_send_file)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -659,8 +663,6 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// Create a new Messenger for the communication back
|
||||||
@ -785,6 +787,11 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mFileCompression = (Spinner) findViewById(R.id.fileCompression);
|
mFileCompression = (Spinner) findViewById(R.id.fileCompression);
|
||||||
Choice[] choices = new Choice[]{
|
Choice[] choices = new Choice[]{
|
||||||
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
|
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
|
||||||
@ -794,7 +801,7 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
new Choice(Id.choice.compression.zlib, "ZLIB ("
|
new Choice(Id.choice.compression.zlib, "ZLIB ("
|
||||||
+ getString(R.string.compression_fast) + ")"),
|
+ getString(R.string.compression_fast) + ")"),
|
||||||
new Choice(Id.choice.compression.bzip2, "BZIP2 ("
|
new Choice(Id.choice.compression.bzip2, "BZIP2 ("
|
||||||
+ getString(R.string.compression_very_slow) + ")"),};
|
+ getString(R.string.compression_very_slow) + ")"), };
|
||||||
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
|
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
|
||||||
android.R.layout.simple_spinner_item, choices);
|
android.R.layout.simple_spinner_item, choices);
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
@ -809,6 +816,7 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
|
mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
|
||||||
|
mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption);
|
||||||
|
|
||||||
mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
|
mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
|
||||||
mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
|
mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
|
||||||
@ -947,8 +955,8 @@ public class EncryptActivity extends DrawerActivity {
|
|||||||
|
|
||||||
case Id.request.secret_keys: {
|
case Id.request.secret_keys: {
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
Bundle bundle = data.getExtras();
|
Uri uri_master_key = data.getData();
|
||||||
mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
|
mSecretKeyId = Long.valueOf(uri_master_key.getLastPathSegment());
|
||||||
} else {
|
} else {
|
||||||
mSecretKeyId = Id.key.none;
|
mSecretKeyId = Id.key.none;
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
@ -31,6 +26,10 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
|
|
||||||
public class HelpAboutFragment extends Fragment {
|
public class HelpAboutFragment extends Fragment {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2012-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,24 +17,16 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
|
||||||
import android.support.v4.app.FragmentTransaction;
|
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.widget.TextView;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
|
||||||
|
|
||||||
public class HelpActivity extends ActionBarActivity {
|
public class HelpActivity extends ActionBarActivity {
|
||||||
public static final String EXTRA_SELECTED_TAB = "selectedTab";
|
public static final String EXTRA_SELECTED_TAB = "selected_tab";
|
||||||
|
|
||||||
ViewPager mViewPager;
|
ViewPager mViewPager;
|
||||||
TabsAdapter mTabsAdapter;
|
TabsAdapter mTabsAdapter;
|
||||||
@ -64,19 +56,24 @@ public class HelpActivity extends ActionBarActivity {
|
|||||||
Bundle startBundle = new Bundle();
|
Bundle startBundle = new Bundle();
|
||||||
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
|
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
|
||||||
HelpHtmlFragment.class, startBundle, (selectedTab == 0 ? true : false));
|
HelpHtmlFragment.class, startBundle, (selectedTab == 0));
|
||||||
|
|
||||||
|
Bundle faqBundle = new Bundle();
|
||||||
|
faqBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_faq);
|
||||||
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_faq)),
|
||||||
|
HelpHtmlFragment.class, faqBundle, (selectedTab == 1));
|
||||||
|
|
||||||
Bundle nfcBundle = new Bundle();
|
Bundle nfcBundle = new Bundle();
|
||||||
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
|
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
|
||||||
HelpHtmlFragment.class, nfcBundle, (selectedTab == 1 ? true : false));
|
HelpHtmlFragment.class, nfcBundle, (selectedTab == 2));
|
||||||
|
|
||||||
Bundle changelogBundle = new Bundle();
|
Bundle changelogBundle = new Bundle();
|
||||||
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
|
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
|
||||||
HelpHtmlFragment.class, changelogBundle, (selectedTab == 2 ? true : false));
|
HelpHtmlFragment.class, changelogBundle, (selectedTab == 3));
|
||||||
|
|
||||||
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
|
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
|
||||||
HelpAboutFragment.class, null, (selectedTab == 3 ? true : false));
|
HelpAboutFragment.class, null, (selectedTab == 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
@ -27,11 +25,12 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
|
import org.sufficientlysecure.htmltextview.HtmlTextView;
|
||||||
|
|
||||||
public class HelpHtmlFragment extends Fragment {
|
public class HelpHtmlFragment extends Fragment {
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
|
|
||||||
private int htmlFile;
|
private int mHtmlFile;
|
||||||
|
|
||||||
public static final String ARG_HTML_FILE = "htmlFile";
|
public static final String ARG_HTML_FILE = "htmlFile";
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ public class HelpHtmlFragment extends Fragment {
|
|||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
mActivity = getActivity();
|
mActivity = getActivity();
|
||||||
|
|
||||||
htmlFile = getArguments().getInt(ARG_HTML_FILE);
|
mHtmlFile = getArguments().getInt(ARG_HTML_FILE);
|
||||||
|
|
||||||
ScrollView scroller = new ScrollView(mActivity);
|
ScrollView scroller = new ScrollView(mActivity);
|
||||||
HtmlTextView text = new HtmlTextView(mActivity);
|
HtmlTextView text = new HtmlTextView(mActivity);
|
||||||
@ -66,7 +65,7 @@ public class HelpHtmlFragment extends Fragment {
|
|||||||
scroller.addView(text);
|
scroller.addView(text);
|
||||||
|
|
||||||
// load html from raw resource (Parsing handled by HtmlTextView library)
|
// load html from raw resource (Parsing handled by HtmlTextView library)
|
||||||
text.setHtmlFromRawResource(getActivity(), htmlFile);
|
text.setHtmlFromRawResource(getActivity(), mHtmlFile);
|
||||||
|
|
||||||
// no flickering when clicking textview for Android < 4
|
// no flickering when clicking textview for Android < 4
|
||||||
text.setTextColor(getResources().getColor(android.R.color.black));
|
text.setTextColor(getResources().getColor(android.R.color.black));
|
||||||
|
@ -34,10 +34,8 @@ import android.support.v7.app.ActionBar;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
import com.devspark.appmsg.AppMsg;
|
import com.devspark.appmsg.AppMsg;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
@ -161,7 +159,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
|||||||
} else if (extras.containsKey(EXTRA_KEY_ID)) {
|
} else if (extras.containsKey(EXTRA_KEY_ID)) {
|
||||||
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
|
long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
|
||||||
if (keyId != 0) {
|
if (keyId != 0) {
|
||||||
query = "0x" + PgpKeyHelper.convertKeyToHex(keyId);
|
query = PgpKeyHelper.convertKeyIdToHex(keyId);
|
||||||
}
|
}
|
||||||
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
|
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
|
||||||
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
|
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
|
||||||
@ -169,7 +167,8 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
|||||||
query = "0x" + fingerprint;
|
query = "0x" + fingerprint;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e(Constants.TAG, "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!");
|
Log.e(Constants.TAG,
|
||||||
|
"IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +337,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
|||||||
// } else {
|
// } else {
|
||||||
// status.putString(
|
// status.putString(
|
||||||
// EXTRA_ERROR,
|
// EXTRA_ERROR,
|
||||||
// "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
|
// "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// } catch (QueryException e) {
|
// } catch (QueryException e) {
|
||||||
@ -360,51 +359,54 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
// Message is received after importing is done in ApgService
|
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
|
|
||||||
R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) {
|
|
||||||
public void handleMessage(Message message) {
|
|
||||||
// handle messages by standard ApgHandler first
|
|
||||||
super.handleMessage(message);
|
|
||||||
|
|
||||||
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
|
||||||
// get returned data bundle
|
|
||||||
Bundle returnData = message.getData();
|
|
||||||
|
|
||||||
int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED);
|
|
||||||
int updated = returnData
|
|
||||||
.getInt(KeychainIntentService.RESULT_IMPORT_UPDATED);
|
|
||||||
int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
|
|
||||||
String toastMessage;
|
|
||||||
if (added > 0 && updated > 0) {
|
|
||||||
String addedStr = getResources().getQuantityString(
|
|
||||||
R.plurals.keys_added_and_updated_1, added, added);
|
|
||||||
String updatedStr = getResources().getQuantityString(
|
|
||||||
R.plurals.keys_added_and_updated_2, updated, updated);
|
|
||||||
toastMessage = addedStr + updatedStr;
|
|
||||||
} else if (added > 0) {
|
|
||||||
toastMessage = getResources().getQuantityString(R.plurals.keys_added,
|
|
||||||
added, added);
|
|
||||||
} else if (updated > 0) {
|
|
||||||
toastMessage = getResources().getQuantityString(R.plurals.keys_updated,
|
|
||||||
updated, updated);
|
|
||||||
} else {
|
|
||||||
toastMessage = getString(R.string.no_keys_added_or_updated);
|
|
||||||
}
|
|
||||||
AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
|
|
||||||
.show();
|
|
||||||
if (bad > 0) {
|
|
||||||
BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad);
|
|
||||||
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import keys with mImportData
|
* Import keys with mImportData
|
||||||
*/
|
*/
|
||||||
public void importKeys() {
|
public void importKeys() {
|
||||||
|
// Message is received after importing is done in KeychainIntentService
|
||||||
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||||
|
this,
|
||||||
|
getString(R.string.progress_importing),
|
||||||
|
ProgressDialog.STYLE_HORIZONTAL) {
|
||||||
|
public void handleMessage(Message message) {
|
||||||
|
// handle messages by standard KeychainIntentServiceHandler first
|
||||||
|
super.handleMessage(message);
|
||||||
|
|
||||||
|
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
|
||||||
|
// get returned data bundle
|
||||||
|
Bundle returnData = message.getData();
|
||||||
|
|
||||||
|
int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED);
|
||||||
|
int updated = returnData
|
||||||
|
.getInt(KeychainIntentService.RESULT_IMPORT_UPDATED);
|
||||||
|
int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
|
||||||
|
String toastMessage;
|
||||||
|
if (added > 0 && updated > 0) {
|
||||||
|
String addedStr = getResources().getQuantityString(
|
||||||
|
R.plurals.keys_added_and_updated_1, added, added);
|
||||||
|
String updatedStr = getResources().getQuantityString(
|
||||||
|
R.plurals.keys_added_and_updated_2, updated, updated);
|
||||||
|
toastMessage = addedStr + updatedStr;
|
||||||
|
} else if (added > 0) {
|
||||||
|
toastMessage = getResources().getQuantityString(R.plurals.keys_added,
|
||||||
|
added, added);
|
||||||
|
} else if (updated > 0) {
|
||||||
|
toastMessage = getResources().getQuantityString(R.plurals.keys_updated,
|
||||||
|
updated, updated);
|
||||||
|
} else {
|
||||||
|
toastMessage = getString(R.string.no_keys_added_or_updated);
|
||||||
|
}
|
||||||
|
AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO)
|
||||||
|
.show();
|
||||||
|
if (bad > 0) {
|
||||||
|
BadImportKeyDialogFragment badImportKeyDialogFragment =
|
||||||
|
BadImportKeyDialogFragment.newInstance(bad);
|
||||||
|
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (mListFragment.getKeyBytes() != null || mListFragment.getDataUri() != null) {
|
if (mListFragment.getKeyBytes() != null || mListFragment.getDataUri() != null) {
|
||||||
Log.d(Constants.TAG, "importKeys started");
|
Log.d(Constants.TAG, "importKeys started");
|
||||||
|
|
||||||
|
@ -17,17 +17,15 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
|
|
||||||
public class ImportKeysClipboardFragment extends Fragment {
|
public class ImportKeysClipboardFragment extends Fragment {
|
||||||
|
|
||||||
@ -60,8 +58,9 @@ public class ImportKeysClipboardFragment extends Fragment {
|
|||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
|
CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
|
||||||
String sendText = "";
|
String sendText = "";
|
||||||
if (clipboardText != null)
|
if (clipboardText != null) {
|
||||||
sendText = clipboardText.toString();
|
sendText = clipboardText.toString();
|
||||||
|
}
|
||||||
mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
|
mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -17,11 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -29,8 +24,11 @@ import android.support.v4.app.Fragment;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.beardedhen.androidbootstrap.BootstrapButton;
|
import com.beardedhen.androidbootstrap.BootstrapButton;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Id;
|
||||||
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||||
|
|
||||||
public class ImportKeysFileFragment extends Fragment {
|
public class ImportKeysFileFragment extends Fragment {
|
||||||
private ImportKeysActivity mImportActivity;
|
private ImportKeysActivity mImportActivity;
|
||||||
@ -62,7 +60,7 @@ public class ImportKeysFileFragment extends Fragment {
|
|||||||
// open .asc or .gpg files
|
// open .asc or .gpg files
|
||||||
// setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
|
// setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
|
||||||
// or gpg types!
|
// or gpg types!
|
||||||
FileHelper.openFile(ImportKeysFileFragment.this, Constants.path.APP_DIR + "/",
|
FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/",
|
||||||
"*/*", Id.request.filename);
|
"*/*", Id.request.filename);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|