From d06ba72edc3136ef2ff8a758e8d53fa13347c778 Mon Sep 17 00:00:00 2001 From: Art O Cathain Date: Sun, 15 Jun 2014 16:08:41 +0100 Subject: [PATCH 01/22] Start on Robolectric integration test --- OpenKeychain/build.gradle | 53 +++++------ .../src/androidTest/java/tests/SomeTest.java | 12 --- .../testsupport/PgpVerifyTestingHelper.java | 82 ++++++++++++++++++ .../keychain/testsupport/package-info.java | 7 ++ .../src/test/java/tests/RoboTest.java | 37 ++++++++ .../test/resources/public-key-for-sample.blob | Bin 0 -> 35198 bytes .../src/test/resources/sample-altered.txt | 26 ++++++ OpenKeychain/src/test/resources/sample.txt | 26 ++++++ build.gradle | 2 +- 9 files changed, 202 insertions(+), 43 deletions(-) delete mode 100644 OpenKeychain/src/androidTest/java/tests/SomeTest.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java create mode 100644 OpenKeychain/src/test/java/tests/RoboTest.java create mode 100644 OpenKeychain/src/test/resources/public-key-for-sample.blob create mode 100644 OpenKeychain/src/test/resources/sample-altered.txt create mode 100644 OpenKeychain/src/test/resources/sample.txt diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index ba527948a..b39c19bfa 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -1,13 +1,5 @@ apply plugin: 'android' -//apply plugin: 'android-test' - -sourceSets { - //androidTest { - //java.srcDir file('src/test/java') - // configure the set of classes for JUnit tests - // include '**/*Test.class' - //} -} +apply plugin: 'robolectric' dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information @@ -29,26 +21,27 @@ dependencies { compile project(':extern:SuperToasts:supertoasts') compile project(':extern:dnsjava') - // Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well - androidTestCompile 'junit:junit:4.11' - androidTestCompile 'org.robolectric:robolectric:2.3' - androidTestCompile 'com.squareup:fest-android:1.0.8' - androidTestCompile 'com.google.android:android:4.1.1.4' - androidTestCompile 'com.android.support:support-v4:19.1.0' - androidTestCompile 'com.android.support:appcompat-v7:19.1.0' - androidTestCompile project(':extern:openpgp-api-lib') - androidTestCompile project(':extern:openkeychain-api-lib') - androidTestCompile project(':extern:html-textview') - androidTestCompile project(':extern:StickyListHeaders:library') - androidTestCompile project(':extern:AndroidBootstrap:AndroidBootstrap') - androidTestCompile project(':extern:zxing-qr-code') - androidTestCompile project(':extern:zxing-android-integration') - androidTestCompile project(':extern:spongycastle:core') - androidTestCompile project(':extern:spongycastle:pg') - androidTestCompile project(':extern:spongycastle:pkix') - androidTestCompile project(':extern:spongycastle:prov') - androidTestCompile project(':extern:AppMsg:library') - androidTestCompile project(':extern:SuperToasts:supertoasts') + + // Unit tests are run with Robolectric + testCompile 'junit:junit:4.11' + testCompile 'org.robolectric:robolectric:2.3' + testCompile 'com.squareup:fest-android:1.0.8' + testCompile 'com.google.android:android:4.1.1.4' + testCompile 'com.android.support:support-v4:19.1.0' + testCompile 'com.android.support:appcompat-v7:19.1.0' + testCompile project(':extern:openpgp-api-lib') + testCompile project(':extern:openkeychain-api-lib') + testCompile project(':extern:html-textview') + testCompile project(':extern:StickyListHeaders:library') + testCompile project(':extern:AndroidBootstrap:AndroidBootstrap') + testCompile project(':extern:zxing-qr-code') + testCompile project(':extern:zxing-android-integration') + testCompile project(':extern:spongycastle:core') + testCompile project(':extern:spongycastle:pg') + testCompile project(':extern:spongycastle:pkix') + testCompile project(':extern:spongycastle:prov') + testCompile project(':extern:AppMsg:library') + testCompile project(':extern:SuperToasts:supertoasts') } @@ -57,7 +50,7 @@ android { buildToolsVersion "19.1" defaultConfig { - minSdkVersion 9 + minSdkVersion 15 targetSdkVersion 19 } diff --git a/OpenKeychain/src/androidTest/java/tests/SomeTest.java b/OpenKeychain/src/androidTest/java/tests/SomeTest.java deleted file mode 100644 index edf5f7bcc..000000000 --- a/OpenKeychain/src/androidTest/java/tests/SomeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package tests; - -import org.junit.Assert; -import org.junit.Test; - -public class SomeTest { - @Test - public void willFail() { - // stub - // Assert.assertThat(); - } -} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java new file mode 100644 index 000000000..d5edae5f0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java @@ -0,0 +1,82 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; +import android.net.Uri; + +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.InputData; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * For functional tests of PgpDecryptVerify + */ +public class PgpVerifyTestingHelper { + + private final Context context; + + public PgpVerifyTestingHelper(Context robolectricContext) { + this.context=robolectricContext; + } + + public int doTestFile(String testFileName) throws Exception { + ProviderHelper providerHelper = new ProviderHelperStub(context); + + PgpDecryptVerify.PassphraseCache passphraseCache = new PgpDecryptVerify.PassphraseCache() { + public String getCachedPassphrase(long masterKeyId) { + return "I am a passphrase"; + } + }; + + InputStream sampleInput = getClass().getResourceAsStream(testFileName); + assert null != sampleInput; + InputData data = new InputData(sampleInput, 705); + OutputStream outStream = new ByteArrayOutputStream(); + + PgpDecryptVerify verify = new PgpDecryptVerify.Builder(providerHelper, passphraseCache, data, outStream).build(); + PgpDecryptVerifyResult result = verify.execute(); + + return result.getSignatureResult().getStatus(); + } + + + static class ProviderHelperStub extends ProviderHelper { + public ProviderHelperStub(Context context) { + super(context); + } + + @Override + public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException { + try { + byte[] data = readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); + return new WrappedPublicKeyRing(data, false, 0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public static byte[] readFully(InputStream input) throws IOException + { + byte[] buffer = new byte[8192]; + int bytesRead; + ByteArrayOutputStream output = new ByteArrayOutputStream(); + while ((bytesRead = input.read(buffer)) != -1) + { + output.write(buffer, 0, bytesRead); + } + return output.toByteArray(); + } + + + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java new file mode 100644 index 000000000..1cc0f9a95 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java @@ -0,0 +1,7 @@ +/** + * Test support classes. + * This is only in main code because of gradle-Android Studio-robolectric issues. Having + * classes in main code means IDE autocomplete, class detection, etc., all works. + * TODO Move into test package when possible + */ +package org.sufficientlysecure.keychain.testsupport; diff --git a/OpenKeychain/src/test/java/tests/RoboTest.java b/OpenKeychain/src/test/java/tests/RoboTest.java new file mode 100644 index 000000000..1a4f6b4aa --- /dev/null +++ b/OpenKeychain/src/test/java/tests/RoboTest.java @@ -0,0 +1,37 @@ +package tests; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.*; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class RoboTest { + + @Test + public void testVerifySuccess() throws Exception { + + String testFileName = "/sample.txt"; + int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED; + + int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); + + Assert.assertEquals(expectedSignatureResult, status); + } + + + @Test + public void testVerifyFailure() throws Exception { + + String testFileName = "/sample-altered.txt"; + int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_ERROR; + + int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); + + Assert.assertEquals(expectedSignatureResult, status); + } + +} diff --git a/OpenKeychain/src/test/resources/public-key-for-sample.blob b/OpenKeychain/src/test/resources/public-key-for-sample.blob new file mode 100644 index 0000000000000000000000000000000000000000..4aa91510bb558e243f773771960c528fac5ac05b GIT binary patch literal 35198 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 + +iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC +CDxKAFyaaGa835l1vpbFkAJk +=3r/N +-----END PGP SIGNATURE----- diff --git a/OpenKeychain/src/test/resources/sample.txt b/OpenKeychain/src/test/resources/sample.txt new file mode 100644 index 000000000..c0065f78d --- /dev/null +++ b/OpenKeychain/src/test/resources/sample.txt @@ -0,0 +1,26 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +This is a simple text document, which is used to illustrate +the concept of signing simple text files. There are no +control characters or special formatting commands in this +text, just simple printable ASCII characters. + +To make this a slightly less uninteresting document, there +follows a short shopping list. + + eggs, 1 doz + milk, 1 gal + bacon, 1 lb + olive oil + bread, 1 loaf + +That's all there is to this document. + +-----BEGIN PGP SIGNATURE----- +Version: PGPfreeware 5.5.5 for non-commercial use + +iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC +CDxKAFyaaGa835l1vpbFkAJk +=3r/N +-----END PGP SIGNATURE----- diff --git a/build.gradle b/build.gradle index e3cafe3ce..e0da6b52f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information classpath 'com.android.tools.build:gradle:0.11.1' - //classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.10.0' + classpath 'org.robolectric:robolectric-gradle-plugin:0.11.0' } } From 7ea57b7c896bed9601e377be1c2bbdfb78e27dc9 Mon Sep 17 00:00:00 2001 From: Art O Cathain Date: Mon, 16 Jun 2014 19:12:26 +0100 Subject: [PATCH 02/22] Fix hard-coded size --- .../keychain/testsupport/PgpVerifyTestingHelper.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java index d5edae5f0..e27ca578d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java @@ -9,6 +9,7 @@ import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.InputData; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -36,9 +37,11 @@ public class PgpVerifyTestingHelper { } }; - InputStream sampleInput = getClass().getResourceAsStream(testFileName); - assert null != sampleInput; - InputData data = new InputData(sampleInput, 705); + byte[] sampleInputBytes = readFully(getClass().getResourceAsStream(testFileName)); + + InputStream sampleInput = new ByteArrayInputStream(sampleInputBytes); + + InputData data = new InputData(sampleInput, sampleInputBytes.length); OutputStream outStream = new ByteArrayOutputStream(); PgpDecryptVerify verify = new PgpDecryptVerify.Builder(providerHelper, passphraseCache, data, outStream).build(); From b1815ef24a049953845f1175b5d1e1b45d512c01 Mon Sep 17 00:00:00 2001 From: Art O Cathain Date: Thu, 19 Jun 2014 07:52:37 +0100 Subject: [PATCH 03/22] revert to previous min version --- OpenKeychain/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index b39c19bfa..f8bf79d59 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -50,7 +50,7 @@ android { buildToolsVersion "19.1" defaultConfig { - minSdkVersion 15 + minSdkVersion 9 targetSdkVersion 19 } From 4aec28c2377faf7f25ff3598eccbb1b74a45b792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 20 Jun 2014 15:14:34 +0200 Subject: [PATCH 04/22] Fix update from keyserver flow --- .../keychain/ui/ImportKeysActivity.java | 1 + .../keychain/ui/ImportKeysListFragment.java | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index e829df7a0..07587b599 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -212,6 +212,7 @@ public class ImportKeysActivity extends ActionBarActivity { serverBundle = new Bundle(); serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); + // display server tab only serverOnly = true; mSwitchToTab = NAV_SERVER; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index cba6dd78f..45c44803c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -149,12 +149,13 @@ public class ImportKeysListFragment extends ListFragment implements mAdapter = new ImportKeysAdapter(mActivity); setListAdapter(mAdapter); - if (getArguments().containsKey(ARG_DATA_URI) || getArguments().containsKey(ARG_BYTES)) { - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - byte[] bytes = getArguments().getByteArray(ARG_BYTES); + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + byte[] bytes = getArguments().getByteArray(ARG_BYTES); + String query = getArguments().getString(ARG_SERVER_QUERY); + + if (dataUri != null || bytes != null) { mLoaderState = new BytesLoaderState(bytes, dataUri); - } else if (getArguments().containsKey(ARG_SERVER_QUERY)) { - String query = getArguments().getString(ARG_SERVER_QUERY); + } else if (query != null) { // TODO: this is used when scanning QR Code or updating a key. // Currently it simply uses keyserver nr 0 String keyserver = Preferences.getPreferences(getActivity()) From 717d66961b0665d6a2fd5f81a43ff0f19ebc8343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 20 Jun 2014 15:39:27 +0200 Subject: [PATCH 05/22] Fix onTouchEvent delegation, with workaround for Android bug --- .../keychain/ui/ImportKeysActivity.java | 13 +++++++------ OpenKeychain/src/main/res/values/arrays.xml | 8 -------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 07587b599..8f3851e84 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -28,7 +28,6 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.Parcelable; -import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBarActivity; import android.view.MotionEvent; @@ -88,8 +87,6 @@ public class ImportKeysActivity extends ActionBarActivity { // view private ImportKeysListFragment mListFragment; - private String[] mNavigationStrings; - private Fragment mCurrentFragment; private View mImportButton; private ViewPager mViewPager; private SlidingTabLayout mSlidingTabLayout; @@ -121,8 +118,6 @@ public class ImportKeysActivity extends ActionBarActivity { } }); - mNavigationStrings = getResources().getStringArray(R.array.import_action_list); - // TODO: add actionbar button for this action? // if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { // } @@ -373,7 +368,13 @@ public class ImportKeysActivity extends ActionBarActivity { boolean result = super.onTouchEvent(event); if (!result) { - mViewPager.onTouchEvent(event); + try { + mViewPager.onTouchEvent(event); + } catch (IllegalArgumentException e) { + // workaround for Android bug? + // http://stackoverflow.com/q/16459196 + Log.d(Constants.TAG, "Workaround: Catched IllegalArgumentException"); + } } return result; diff --git a/OpenKeychain/src/main/res/values/arrays.xml b/OpenKeychain/src/main/res/values/arrays.xml index 699c02aff..44bbe00cc 100644 --- a/OpenKeychain/src/main/res/values/arrays.xml +++ b/OpenKeychain/src/main/res/values/arrays.xml @@ -48,13 +48,5 @@ @string/key_size_1024 @string/key_size_custom - - @string/menu_import_from_key_server - @string/menu_import_from_file - @string/menu_import_from_qr_code - @string/import_from_clipboard - @string/menu_import_from_nfc - @string/menu_import_from_keybase - From f0b8261ad34ec9776e7ff632356b19e4f285096a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 20 Jun 2014 15:49:54 +0200 Subject: [PATCH 06/22] Use actionbar button for log activity --- .../keychain/ui/LogDisplayActivity.java | 35 ++++++++++++++++--- .../keychain/ui/LogDisplayFragment.java | 17 +++++++++ .../main/res/layout/log_display_activity.xml | 30 ---------------- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index a0d449195..b8386b144 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,13 +1,28 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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 . + */ + package org.sufficientlysecure.keychain.ui; import android.os.Bundle; -import android.support.v4.view.GestureDetectorCompat; import android.support.v7.app.ActionBarActivity; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.MotionEvent; +import android.view.View; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; public class LogDisplayActivity extends ActionBarActivity { @@ -15,6 +30,18 @@ public class LogDisplayActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Inflate a "Done" custom action bar + ActionBarHelper.setOneButtonView(getSupportActionBar(), + R.string.btn_okay, R.drawable.ic_action_done, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + finish(); + } + } + ); + setContentView(R.layout.log_display_activity); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 496e98c18..e42d9d00b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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 . + */ + package org.sufficientlysecure.keychain.ui; import android.content.Context; diff --git a/OpenKeychain/src/main/res/layout/log_display_activity.xml b/OpenKeychain/src/main/res/layout/log_display_activity.xml index 591e2650c..1dc45a9a8 100644 --- a/OpenKeychain/src/main/res/layout/log_display_activity.xml +++ b/OpenKeychain/src/main/res/layout/log_display_activity.xml @@ -13,34 +13,4 @@ android:layout_marginRight="8dp" android:layout_marginLeft="8dp" /> - - - - - - - - \ No newline at end of file From 6b7aa2767a07fae02538b5b02bee9698fa1d3b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 20 Jun 2014 15:58:32 +0200 Subject: [PATCH 07/22] Fix keybase import --- .../keychain/keyimport/KeybaseKeyserver.java | 3 ++- .../org/sufficientlysecure/keychain/pgp/PgpImportExport.java | 2 ++ .../java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 2f14d77a8..bf886cc6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -29,6 +29,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Locale; public class KeybaseKeyserver extends Keyserver { public static final String ORIGIN = "keybase:keybase.io"; @@ -94,7 +95,7 @@ public class KeybaseKeyserver extends Keyserver { String keybaseId = JWalk.getString(match, "components", "username", "val"); String fullName = JWalk.getString(match, "components", "full_name", "val"); String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); - fingerprint = fingerprint.replace(" ", "").toUpperCase(); // not strictly necessary but doesn't hurt + fingerprint = fingerprint.replace(" ", "").toLowerCase(Locale.US); // not strictly necessary but doesn't hurt entry.setFingerprintHex(fingerprint); entry.setKeyIdHex("0x" + fingerprint.substring(Math.max(0, fingerprint.length() - 16))); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 7544f7b86..c50d92f7a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -139,6 +139,8 @@ public class PgpImportExport { String expectedFp = entry.getExpectedFingerprint(); if(expectedFp != null) { if(!PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()).equals(expectedFp)) { + Log.d(Constants.TAG, "fingerprint: " + PgpKeyHelper.convertFingerprintToHex(key.getFingerprint())); + Log.d(Constants.TAG, "expected fingerprint: " + expectedFp); Log.e(Constants.TAG, "Actual key fingerprint is not the same as expected!"); badKeys += 1; continue; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 291b07639..1ba028006 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -104,7 +104,7 @@ public class PgpKeyHelper { * @return */ public static String convertFingerprintToHex(byte[] fingerprint) { - String hexString = Hex.toHexString(fingerprint); + String hexString = Hex.toHexString(fingerprint).toLowerCase(Locale.US); return hexString; } From 17f43ad21bde79caf1b815c89fd5a95f1f959ca5 Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Fri, 20 Jun 2014 09:26:04 -0700 Subject: [PATCH 08/22] Moved Keybase stuff into KeybaseLib submodule --- .gitmodules | 3 + OpenKeychain/build.gradle | 1 + .../keychain/keyimport/KeybaseKeyserver.java | 130 ++++-------------- .../keychain/util/JWalk.java | 121 ---------------- OpenKeychain/src/main/res/values/strings.xml | 2 +- README.md | 2 +- extern/KeybaseLib | 1 + settings.gradle | 1 + 8 files changed, 37 insertions(+), 224 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java create mode 160000 extern/KeybaseLib diff --git a/.gitmodules b/.gitmodules index a549f6cec..6b1646b89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "extern/dnsjava"] path = extern/dnsjava url = https://github.com/open-keychain/dnsjava.git +[submodule "extern/KeybaseLib"] + path = extern/KeybaseLib + url = https://github.com/timbray/KeybaseLib.git diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index f8bf79d59..fb79d2163 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -20,6 +20,7 @@ dependencies { compile project(':extern:AppMsg:library') compile project(':extern:SuperToasts:supertoasts') compile project(':extern:dnsjava') + compile project(':extern:KeybaseLib:Lib') // Unit tests are run with Robolectric diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index bf886cc6c..98e5111f7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -17,19 +17,17 @@ package org.sufficientlysecure.keychain.keyimport; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import com.textuality.keybase.lib.KeybaseException; +import com.textuality.keybase.lib.Match; +import com.textuality.keybase.lib.Search; +import com.textuality.keybase.lib.User; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.util.JWalk; import org.sufficientlysecure.keychain.util.Log; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; import java.util.ArrayList; -import java.util.Locale; +import java.util.List; public class KeybaseKeyserver extends Keyserver { public static final String ORIGIN = "keybase:keybase.io"; @@ -44,29 +42,14 @@ public class KeybaseKeyserver extends Keyserver { // cut off "0x" if a user is searching for a key id query = query.substring(2); } + mQuery = query; - JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query); try { - - JSONArray matches = JWalk.getArray(fromQuery, "completions"); - for (int i = 0; i < matches.length(); i++) { - JSONObject match = matches.getJSONObject(i); - - // only list them if they have a key - if (JWalk.optObject(match, "components", "key_fingerprint") != null) { - // TODO: needed anymore? -// String keybaseId = JWalk.getString(match, "components", "username", "val"); -// String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); -// fingerprint = fingerprint.replace(" ", "").toUpperCase(); -// if (keybaseId.equals(query) || fingerprint.startsWith(query.toUpperCase())) { -// results.add(makeEntry(match)); -// } else { -// results.add(makeEntry(match)); -// } - results.add(makeEntry(match)); - } + Iterable matches = Search.search(query); + for (Match match : matches) { + results.add(makeEntry(match)); } - } catch (Exception e) { + } catch (KeybaseException e) { Log.e(Constants.TAG, "keybase result parsing error", e); throw new QueryFailedException("Unexpected structure in keybase search result: " + e.getMessage()); } @@ -74,103 +57,48 @@ public class KeybaseKeyserver extends Keyserver { return results; } - private JSONObject getUser(String keybaseId) throws QueryFailedException { - try { - return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseId); - } catch (Exception e) { - String detail = ""; - if (keybaseId != null) { - detail = ". Query was for user '" + keybaseId + "'"; - } - throw new QueryFailedException(e.getMessage() + detail); - } - } - - private ImportKeysListEntry makeEntry(JSONObject match) throws QueryFailedException, JSONException { - + private ImportKeysListEntry makeEntry(Match match) throws KeybaseException { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(mQuery); entry.setOrigin(ORIGIN); - String keybaseId = JWalk.getString(match, "components", "username", "val"); - String fullName = JWalk.getString(match, "components", "full_name", "val"); - String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); - fingerprint = fingerprint.replace(" ", "").toLowerCase(Locale.US); // not strictly necessary but doesn't hurt + String username = null; + username = match.getUsername(); + String fullName = match.getFullName(); + String fingerprint = match.getFingerprint(); entry.setFingerprintHex(fingerprint); - entry.setKeyIdHex("0x" + fingerprint.substring(Math.max(0, fingerprint.length() - 16))); + entry.setKeyIdHex("0x" + match.getKeyID()); // store extra info, so we can query for the keybase id directly - entry.setExtraData(keybaseId); + entry.setExtraData(username); - final int algorithmId = JWalk.getInt(match, "components", "key_fingerprint", "algo"); + final int algorithmId = match.getAlgorithmId(); entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); - final int bitStrength = JWalk.getInt(match, "components", "key_fingerprint", "nbits"); + final int bitStrength = match.getBitStrength(); entry.setBitStrength(bitStrength); ArrayList userIds = new ArrayList(); - String name = fullName + " "; + String name = fullName + " "; userIds.add(name); - try { - userIds.add("github.com/" + JWalk.getString(match, "components", "github", "val")); - } catch (JSONException e) { - // ignore - } - try { - userIds.add("twitter.com/" + JWalk.getString(match, "components", "twitter", "val")); - } catch (JSONException e) { - // ignore - } - try { - JSONArray array = JWalk.getArray(match, "components", "websites"); - JSONObject website = array.getJSONObject(0); - userIds.add(JWalk.getString(website, "val")); - } catch (JSONException e) { - // ignore + + List proofLabels = match.getProofLabels(); + for (String proofLabel : proofLabels) { + userIds.add(proofLabel); } entry.setUserIds(userIds); entry.setPrimaryUserId(name); return entry; } - private JSONObject getFromKeybase(String path, String query) throws QueryFailedException { - try { - String url = "https://keybase.io/" + path + URLEncoder.encode(query, "utf8"); - Log.d(Constants.TAG, "keybase query: " + url); - - URL realUrl = new URL(url); - HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); - conn.setConnectTimeout(5000); // TODO: Reasonable values for keybase - conn.setReadTimeout(25000); - conn.connect(); - int response = conn.getResponseCode(); - if (response >= 200 && response < 300) { - String text = readAll(conn.getInputStream(), conn.getContentEncoding()); - try { - JSONObject json = new JSONObject(text); - if (JWalk.getInt(json, "status", "code") != 0) { - throw new QueryFailedException("Keybase.io query failed: " + path + "?" + - query); - } - return json; - } catch (JSONException e) { - throw new QueryFailedException("Keybase.io query returned broken JSON"); - } - } else { - String message = readAll(conn.getErrorStream(), conn.getContentEncoding()); - throw new QueryFailedException("Keybase.io query error (status=" + response + - "): " + message); - } - } catch (Exception e) { - throw new QueryFailedException("Keybase.io query error"); - } - } - @Override public String get(String id) throws QueryFailedException { try { + /* JSONObject user = getUser(id); return JWalk.getString(user, "them", "public_keys", "primary", "bundle"); - } catch (Exception e) { + */ + return User.keyForUsername(id); + } catch (KeybaseException e) { throw new QueryFailedException(e.getMessage()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java deleted file mode 100644 index 76797811d..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2014 Tim Bray - * - * 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 . - */ - -package org.sufficientlysecure.keychain.util; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Minimal hierarchy selector - * - * This is for picking out an item in a large multilevel JSON object, for example look at - * the Keybase.io User object, documentation at https://keybase.io/__/api-docs/1.0#user-objects - * an example available via - * curl https://keybase.io/_/api/1.0/user/lookup.json?username=timbray - * - * If you want to retrieve the ascii-armored key, you'd say - * String key = JWalk.getString(match,"them", "public_keys", "primary", "bundle"); - */ -public class JWalk { - - /** - * Returns an int member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static int getInt(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getInt(path[path.length - 1]); - } - - /** - * Returns a long member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static long getLong(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getLong(path[path.length - 1]); - } - - /** - * Returns a String member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static String getString(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getString(path[path.length - 1]); - } - - /** - * Returns a JSONArray member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static JSONArray getArray(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getJSONArray(path[path.length - 1]); - } - - /** - * Returns a JSONObject member value from the JSON sub-object addressed by the path, or null - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path, except for the last, doesn’t work - */ - public static JSONObject optObject(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.optJSONObject(path[path.length - 1]); - } - - private static JSONObject walk(JSONObject json, String... path) throws JSONException { - int len = path.length - 1; - int pathIndex = 0; - try { - while (pathIndex < len) { - json = json.getJSONObject(path[pathIndex]); - pathIndex++; - } - } catch (JSONException e) { - // try to give ’em a nice-looking error - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < len; i++) { - sb.append(path[i]).append('.'); - } - sb.append(path[len]); - throw new JSONException("JWalk error at step " + pathIndex + " of " + sb); - } - return json; - } -} diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index e24ac6925..91676f09c 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -332,7 +332,7 @@ Name/Email/Key ID… Search Secret Keys Share Key with… - Name/Keybase.io username… + Search Keybase.io for… 512 diff --git a/README.md b/README.md index f940e04f6..9a80eaf07 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Expand the Extras directory and install "Android Support Repository" Select everything for the newest SDK Platform (API-Level 19) 4. Export ANDROID_HOME pointing to your Android SDK 5. Execute ``./gradlew build`` -6. You can install the app with ``adb install -r OpenKeychain/build/apk/OpenKeychain-debug-unaligned.apk`` +6. You can install the app with ``adb install -r OpenKeychain/build/outputs/apk/OpenKeychain-debug-unaligned.apk`` ### Build API Demo with Gradle diff --git a/extern/KeybaseLib b/extern/KeybaseLib new file mode 160000 index 000000000..e928c7559 --- /dev/null +++ b/extern/KeybaseLib @@ -0,0 +1 @@ +Subproject commit e928c7559672f301354bbf74f47e19aa24e4d2d9 diff --git a/settings.gradle b/settings.gradle index 282c8a234..24eb03490 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,3 +13,4 @@ include ':extern:spongycastle:prov' include ':extern:AppMsg:library' include ':extern:SuperToasts:supertoasts' include ':extern:dnsjava' +include ':extern:KeybaseLib:Lib' From 0a790c7aa2305c8b001e4505692a9d357a9068fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 20 Jun 2014 22:28:08 +0200 Subject: [PATCH 09/22] Work on Qr code import --- .../keychain/ui/ImportKeysActivity.java | 66 +++++++++++-------- .../keychain/ui/ImportKeysListFragment.java | 21 ++++-- .../keychain/ui/ImportKeysServerFragment.java | 23 ++++--- .../ui/adapter/ImportKeysAdapter.java | 2 +- .../adapter/ImportKeysListServerLoader.java | 1 - 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 8f3851e84..6d3b7c340 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.Parcelable; +import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBarActivity; import android.view.MotionEvent; @@ -43,6 +44,7 @@ import com.github.johnpersano.supertoasts.util.Style; 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.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -94,12 +96,12 @@ public class ImportKeysActivity extends ActionBarActivity { public static final int VIEW_PAGER_HEIGHT = 64; // dp - private static final int NAV_SERVER = 0; - private static final int NAV_QR_CODE = 1; - private static final int NAV_FILE = 2; - private static final int NAV_KEYBASE = 3; + private static final int TAB_KEYSERVER = 0; + private static final int TAB_QR_CODE = 1; + private static final int TAB_FILE = 2; + private static final int TAB_KEYBASE = 3; - private int mSwitchToTab = NAV_SERVER; + private int mSwitchToTab = TAB_KEYSERVER; @Override protected void onCreate(Bundle savedInstanceState) { @@ -118,10 +120,6 @@ public class ImportKeysActivity extends ActionBarActivity { } }); - // TODO: add actionbar button for this action? -// if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { -// } - handleActions(savedInstanceState, getIntent()); } @@ -150,7 +148,7 @@ public class ImportKeysActivity extends ActionBarActivity { /* Keychain's own Actions */ // display file fragment - mViewPager.setCurrentItem(NAV_FILE); + mViewPager.setCurrentItem(TAB_FILE); if (dataUri != null) { // action: directly load data @@ -185,7 +183,7 @@ public class ImportKeysActivity extends ActionBarActivity { // display keyserver fragment with query serverBundle = new Bundle(); serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); - mSwitchToTab = NAV_SERVER; + mSwitchToTab = TAB_KEYSERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -209,7 +207,7 @@ public class ImportKeysActivity extends ActionBarActivity { serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); // display server tab only serverOnly = true; - mSwitchToTab = NAV_SERVER; + mSwitchToTab = TAB_KEYSERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -223,7 +221,7 @@ public class ImportKeysActivity extends ActionBarActivity { } } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_FILE; + mSwitchToTab = TAB_FILE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -231,20 +229,20 @@ public class ImportKeysActivity extends ActionBarActivity { // also exposed in AndroidManifest // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_QR_CODE; + mSwitchToTab = TAB_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_QR_CODE; + mSwitchToTab = TAB_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_KEYBASE; + mSwitchToTab = TAB_KEYBASE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -269,6 +267,8 @@ public class ImportKeysActivity extends ActionBarActivity { @Override public void onPageSelected(int position) { + // cancel loader and clear list + mListFragment.destroyLoader(); } @Override @@ -330,18 +330,32 @@ public class ImportKeysActivity extends ActionBarActivity { Log.d(Constants.TAG, "fingerprint: " + fingerprint); - // TODO: reload fragment when coming from qr code! -// loadFromFingerprint(savedInstanceState, fingerprint); + final String query = "0x" + fingerprint; + mViewPager.setCurrentItem(TAB_KEYSERVER); -// String query = "0x" + fingerprint; -// -// // display keyserver fragment with query -// Bundle serverBundle = new Bundle(); -// serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); -// serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); -// -// return serverBundle; + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); + + ImportKeysServerFragment f = (ImportKeysServerFragment) + getActiveFragment(mViewPager, TAB_KEYSERVER); + + // TODO: Currently it simply uses keyserver nr 0 + String keyserver = Preferences.getPreferences(ImportKeysActivity.this) + .getKeyServers()[0]; + + f.searchCallback(query, keyserver); + } + + // http://stackoverflow.com/a/9293207 + public Fragment getActiveFragment(ViewPager container, int position) { + String name = makeFragmentName(container.getId(), position); + return getSupportFragmentManager().findFragmentByTag(name); + } + + // http://stackoverflow.com/a/9293207 + private static String makeFragmentName(int viewId, int index) { + return "android:switcher:" + viewId + ":" + index; } private boolean isFingerprintValid(String fingerprint) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index 45c44803c..e7bb38514 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -156,7 +156,7 @@ public class ImportKeysListFragment extends ListFragment implements if (dataUri != null || bytes != null) { mLoaderState = new BytesLoaderState(bytes, dataUri); } else if (query != null) { - // TODO: this is used when scanning QR Code or updating a key. + // TODO: this is used when updating a key. // Currently it simply uses keyserver nr 0 String keyserver = Preferences.getPreferences(getActivity()) .getKeyServers()[0]; @@ -185,27 +185,34 @@ public class ImportKeysListFragment extends ListFragment implements restartLoaders(); } + public void destroyLoader() { + if (getLoaderManager().getLoader(LOADER_ID_BYTES) != null) { + getLoaderManager().destroyLoader(LOADER_ID_BYTES); + } + if (getLoaderManager().getLoader(LOADER_ID_SERVER_QUERY) != null) { + getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); + } + if (getLoaderManager().getLoader(LOADER_ID_KEYBASE) != null) { + getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); + } + setListShown(true); + } + private void restartLoaders() { if (mLoaderState instanceof BytesLoaderState) { // Start out with a progress indicator. setListShown(false); getLoaderManager().restartLoader(LOADER_ID_BYTES, null, this); - getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); - getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); } else if (mLoaderState instanceof KeyserverLoaderState) { // Start out with a progress indicator. setListShown(false); - getLoaderManager().destroyLoader(LOADER_ID_BYTES); getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this); - getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); } else if (mLoaderState instanceof KeybaseLoaderState) { // Start out with a progress indicator. setListShown(false); - getLoaderManager().destroyLoader(LOADER_ID_BYTES); - getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); getLoaderManager().restartLoader(LOADER_ID_KEYBASE, null, this); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index c7467d789..ff694cb4f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -40,7 +40,7 @@ import org.sufficientlysecure.keychain.util.Log; public class ImportKeysServerFragment extends Fragment { public static final String ARG_QUERY = "query"; - public static final String ARG_KEY_SERVER = "key_server"; + public static final String ARG_KEYSERVER = "keyserver"; public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit"; private ImportKeysActivity mImportActivity; @@ -55,12 +55,12 @@ public class ImportKeysServerFragment extends Fragment { /** * Creates new instance of this fragment */ - public static ImportKeysServerFragment newInstance(String query, String keyServer) { + public static ImportKeysServerFragment newInstance(String query, String keyserver) { ImportKeysServerFragment frag = new ImportKeysServerFragment(); Bundle args = new Bundle(); args.putString(ARG_QUERY, query); - args.putString(ARG_KEY_SERVER, keyServer); + args.putString(ARG_KEYSERVER, keyserver); frag.setArguments(args); @@ -149,12 +149,12 @@ public class ImportKeysServerFragment extends Fragment { Log.d(Constants.TAG, "query: " + query); } - if (getArguments().containsKey(ARG_KEY_SERVER)) { - String keyServer = getArguments().getString(ARG_KEY_SERVER); - int keyServerPos = mServerAdapter.getPosition(keyServer); - mServerSpinner.setSelection(keyServerPos); + if (getArguments().containsKey(ARG_KEYSERVER)) { + String keyserver = getArguments().getString(ARG_KEYSERVER); + int keyserverPos = mServerAdapter.getPosition(keyserver); + mServerSpinner.setSelection(keyserverPos); - Log.d(Constants.TAG, "keyServer: " + keyServer); + Log.d(Constants.TAG, "keyserver: " + keyserver); } if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) { @@ -174,4 +174,11 @@ public class ImportKeysServerFragment extends Fragment { mImportActivity.loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); } + public void searchCallback(String query, String keyserver) { + mQueryEditText.setText(query, TextView.BufferType.EDITABLE); + int keyServerPos = mServerAdapter.getPosition(keyserver); + mServerSpinner.setSelection(keyServerPos); + search(query, keyserver); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 233b1fca8..a44d32e5b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -170,7 +170,7 @@ public class ImportKeysAdapter extends ArrayAdapter { } else { holder.userIdsList.setVisibility(View.VISIBLE); - // clear view from holder + // destroyLoader view from holder holder.userIdsList.removeAllViews(); Iterator it = entry.getUserIds().iterator(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java index 4eb6f158b..d62c2db43 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -47,7 +47,6 @@ public class ImportKeysListServerLoader @Override public AsyncTaskResultWrapper> loadInBackground() { - mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, null); if (mServerQuery == null) { From 0d644e37b1ca69a21384ec1590ce45dfd23a6ada Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 20 Jun 2014 22:25:13 +0200 Subject: [PATCH 10/22] import: forward motion events to activity regardless --- .../keychain/ui/ImportKeysListFragment.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index e7bb38514..d77e3a452 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -24,7 +24,9 @@ import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.util.LongSparseArray; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.widget.ListView; import org.sufficientlysecure.keychain.Constants; @@ -163,6 +165,16 @@ public class ImportKeysListFragment extends ListFragment implements mLoaderState = new KeyserverLoaderState(query, keyserver); } + getListView().setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (!mAdapter.isEmpty()) { + mActivity.onTouchEvent(event); + } + return false; + } + }); + restartLoaders(); } From a1525bffe23fd0f86e77250f894eacbc481aed77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 20 Jun 2014 23:01:35 +0200 Subject: [PATCH 11/22] Fix qr code finally --- .../keychain/ui/ImportKeysActivity.java | 43 ++++++++++++------- .../keychain/ui/ImportKeysFileFragment.java | 2 +- .../keychain/ui/ImportKeysQrCodeFragment.java | 2 +- .../keychain/ui/ImportKeysServerFragment.java | 3 +- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 6d3b7c340..f7455905d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -135,16 +135,21 @@ public class ImportKeysActivity extends ActionBarActivity { if (Intent.ACTION_VIEW.equals(action)) { // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) - // override action to delegate it to Keychain's ACTION_IMPORT_KEY + // delegate action to ACTION_IMPORT_KEY action = ACTION_IMPORT_KEY; } + if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { + /* Scanning a fingerprint directly with Barcode Scanner */ + // delegate action to ACTION_IMPORT_KEY_FROM_KEYSERVER + String fingerprint = getFingerprintFromUri(dataUri); + action = ACTION_IMPORT_KEY_FROM_KEYSERVER; + extras.putString(EXTRA_FINGERPRINT, fingerprint); + } + Bundle serverBundle = null; boolean serverOnly = false; - if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { - /* Scanning a fingerprint directly with Barcode Scanner */ - loadFromFingerprintUri(savedInstanceState, dataUri); - } else if (ACTION_IMPORT_KEY.equals(action)) { + if (ACTION_IMPORT_KEY.equals(action)) { /* Keychain's own Actions */ // display file fragment @@ -154,7 +159,7 @@ public class ImportKeysActivity extends ActionBarActivity { // action: directly load data startListFragment(savedInstanceState, null, dataUri, null); } else if (extras.containsKey(EXTRA_KEY_BYTES)) { - byte[] importData = intent.getByteArrayExtra(EXTRA_KEY_BYTES); + byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES); // action: directly load data startListFragment(savedInstanceState, importData, null, null); @@ -173,7 +178,7 @@ public class ImportKeysActivity extends ActionBarActivity { if (extras.containsKey(EXTRA_QUERY)) { query = extras.getString(EXTRA_QUERY); } else if (extras.containsKey(EXTRA_KEY_ID)) { - long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); + long keyId = extras.getLong(EXTRA_KEY_ID, 0); if (keyId != 0) { query = PgpKeyHelper.convertKeyIdToHex(keyId); } @@ -197,7 +202,7 @@ public class ImportKeysActivity extends ActionBarActivity { * if the right key has been downloaded */ - String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT); + String fingerprint = extras.getString(EXTRA_FINGERPRINT); if (isFingerprintValid(fingerprint)) { String query = "0x" + fingerprint; @@ -325,18 +330,23 @@ public class ImportKeysActivity extends ActionBarActivity { return OtherHelper.pxToDp(this, params.height); } - public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) { + private String getFingerprintFromUri(Uri dataUri) { String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH); - Log.d(Constants.TAG, "fingerprint: " + fingerprint); + return fingerprint; + } - final String query = "0x" + fingerprint; + public void loadFromFingerprintUri(Uri dataUri) { + String query = "0x" + getFingerprintFromUri(dataUri); + // setCurrentItem does not work directly after onResume (from qr code scanner) + // see http://stackoverflow.com/q/19316729 + // so, reset adapter completely! + if (mViewPager.getAdapter() != null) + mViewPager.setAdapter(null); + mViewPager.setAdapter(mTabsAdapter); mViewPager.setCurrentItem(TAB_KEYSERVER); - // update layout after operations - mSlidingTabLayout.setViewPager(mViewPager); - ImportKeysServerFragment f = (ImportKeysServerFragment) getActiveFragment(mViewPager, TAB_KEYSERVER); @@ -344,7 +354,10 @@ public class ImportKeysActivity extends ActionBarActivity { String keyserver = Preferences.getPreferences(ImportKeysActivity.this) .getKeyServers()[0]; - f.searchCallback(query, keyserver); + // set fields of ImportKeysServerFragment + f.setQueryAndKeyserver(query, keyserver); + // search directly + loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); } // http://stackoverflow.com/a/9293207 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index 60e5324c5..ce885c419 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -81,7 +81,7 @@ public class ImportKeysFileFragment extends Fragment { if (clipboardText != null) { sendText = clipboardText.toString(); if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { - mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); + mImportActivity.loadFromFingerprintUri(Uri.parse(sendText)); return; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index 5766fc189..5f54daa3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -132,7 +132,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } public void importFingerprint(Uri dataUri) { - mImportActivity.loadFromFingerprintUri(null, dataUri); + mImportActivity.loadFromFingerprintUri(dataUri); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index ff694cb4f..d339bc132 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -174,11 +174,10 @@ public class ImportKeysServerFragment extends Fragment { mImportActivity.loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); } - public void searchCallback(String query, String keyserver) { + public void setQueryAndKeyserver(String query, String keyserver) { mQueryEditText.setText(query, TextView.BufferType.EDITABLE); int keyServerPos = mServerAdapter.getPosition(keyserver); mServerSpinner.setSelection(keyServerPos); - search(query, keyserver); } } From 0af2b27cb3d286f3f4e2f830c78b307ca222ff31 Mon Sep 17 00:00:00 2001 From: Art O Cathain Date: Sat, 21 Jun 2014 10:47:07 +0100 Subject: [PATCH 12/22] VERY basic test for save keyring --- OpenKeychain/build.gradle | 16 +----- .../keychain/pgp/NullProgressable.java | 19 +++++++ .../keychain/provider/ProviderHelper.java | 15 +---- .../testsupport/KeyringTestingHelper.java | 56 +++++++++++++++++++ .../testsupport/PgpVerifyTestingHelper.java | 40 +------------ .../testsupport/ProviderHelperStub.java | 22 ++++++++ .../keychain/testsupport/TestDataUtil.java | 25 +++++++++ ...oboTest.java => PgpDecryptVerifyTest.java} | 2 +- .../java/tests/ProviderHelperKeyringTest.java | 20 +++++++ 9 files changed, 148 insertions(+), 67 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java rename OpenKeychain/src/test/java/tests/{RoboTest.java => PgpDecryptVerifyTest.java} (96%) create mode 100644 OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index f8bf79d59..70304ef25 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -27,21 +27,7 @@ dependencies { testCompile 'org.robolectric:robolectric:2.3' testCompile 'com.squareup:fest-android:1.0.8' testCompile 'com.google.android:android:4.1.1.4' - testCompile 'com.android.support:support-v4:19.1.0' - testCompile 'com.android.support:appcompat-v7:19.1.0' - testCompile project(':extern:openpgp-api-lib') - testCompile project(':extern:openkeychain-api-lib') - testCompile project(':extern:html-textview') - testCompile project(':extern:StickyListHeaders:library') - testCompile project(':extern:AndroidBootstrap:AndroidBootstrap') - testCompile project(':extern:zxing-qr-code') - testCompile project(':extern:zxing-android-integration') - testCompile project(':extern:spongycastle:core') - testCompile project(':extern:spongycastle:pg') - testCompile project(':extern:spongycastle:pkix') - testCompile project(':extern:spongycastle:prov') - testCompile project(':extern:AppMsg:library') - testCompile project(':extern:SuperToasts:supertoasts') + // compile dependencies are automatically also included in testCompile } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java new file mode 100644 index 000000000..68312dca3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java @@ -0,0 +1,19 @@ +package org.sufficientlysecure.keychain.pgp; + +/** + * No-op implementation of Progressable + */ +public class NullProgressable implements Progressable { + + @Override + public void setProgress(String message, int current, int total) { + } + + @Override + public void setProgress(int resourceId, int current, int total) { + } + + @Override + public void setProgress(int current, int total) { + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 955fb90ba..0db570be9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,6 +29,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.NullProgressable; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; @@ -655,19 +656,7 @@ public class ProviderHelper { @Deprecated public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { - return savePublicKeyRing(keyRing, new Progressable() { - @Override - public void setProgress(String message, int current, int total) { - } - - @Override - public void setProgress(int resourceId, int current, int total) { - } - - @Override - public void setProgress(int current, int total) { - } - }); + return savePublicKeyRing(keyRing, new NullProgressable()); } /** Save a public keyring into the database. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java new file mode 100644 index 000000000..d4bc6c541 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java @@ -0,0 +1,56 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; + +import org.sufficientlysecure.keychain.pgp.NullProgressable; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults; + +/** + * Helper for tests of the Keyring import in ProviderHelper. + */ +public class KeyringTestingHelper { + + private final Context context; + + public KeyringTestingHelper(Context robolectricContext) { + this.context = robolectricContext; + } + + public boolean addKeyring() throws Exception { + + ProviderHelper providerHelper = new ProviderHelper(context); + +// providerHelper.insertApiApp(new AppSettings("robo-test-package", new byte[]{5, 4, 3, 2, 1})); + + byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); + UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data); + long masterKeyId = ring.getMasterKeyId(); + + // Should throw an exception; key is not yet saved + retrieveKeyAndExpectNotFound(providerHelper, masterKeyId); + + OperationResults.SaveKeyringResult saveKeyringResult = providerHelper.savePublicKeyRing(ring, new NullProgressable()); + + boolean saveSuccess = saveKeyringResult.success(); + + // Now re-retrieve the saved key. Should not throw an exception. + providerHelper.getWrappedPublicKeyRing(masterKeyId); + + // A different ID should still fail + retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1); + + return saveSuccess; + } + + private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) { + try { + providerHelper.getWrappedPublicKeyRing(masterKeyId); + throw new AssertionError("Was expecting the previous call to fail!"); + } catch (ProviderHelper.NotFoundException expectedException) { + // good + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java index e27ca578d..1ab5878cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java @@ -1,19 +1,14 @@ package org.sufficientlysecure.keychain.testsupport; import android.content.Context; -import android.net.Uri; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; -import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.InputData; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -25,7 +20,7 @@ public class PgpVerifyTestingHelper { private final Context context; public PgpVerifyTestingHelper(Context robolectricContext) { - this.context=robolectricContext; + this.context = robolectricContext; } public int doTestFile(String testFileName) throws Exception { @@ -37,7 +32,7 @@ public class PgpVerifyTestingHelper { } }; - byte[] sampleInputBytes = readFully(getClass().getResourceAsStream(testFileName)); + byte[] sampleInputBytes = TestDataUtil.readFully(getClass().getResourceAsStream(testFileName)); InputStream sampleInput = new ByteArrayInputStream(sampleInputBytes); @@ -51,35 +46,4 @@ public class PgpVerifyTestingHelper { } - static class ProviderHelperStub extends ProviderHelper { - public ProviderHelperStub(Context context) { - super(context); - } - - @Override - public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException { - try { - byte[] data = readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); - return new WrappedPublicKeyRing(data, false, 0); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - public static byte[] readFully(InputStream input) throws IOException - { - byte[] buffer = new byte[8192]; - int bytesRead; - ByteArrayOutputStream output = new ByteArrayOutputStream(); - while ((bytesRead = input.read(buffer)) != -1) - { - output.write(buffer, 0, bytesRead); - } - return output.toByteArray(); - } - - - - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java new file mode 100644 index 000000000..c6d834bf9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java @@ -0,0 +1,22 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; +import android.net.Uri; + +import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; + +/** + * Created by art on 21/06/14. + */ +class ProviderHelperStub extends ProviderHelper { + public ProviderHelperStub(Context context) { + super(context); + } + + @Override + public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException { + byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); + return new WrappedPublicKeyRing(data, false, 0); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java new file mode 100644 index 000000000..06dc08eab --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java @@ -0,0 +1,25 @@ +package org.sufficientlysecure.keychain.testsupport; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Misc support functions. Would just use Guava / Apache Commons but + * avoiding extra dependencies. + */ +public class TestDataUtil { + public static byte[] readFully(InputStream input) { + byte[] buffer = new byte[8192]; + int bytesRead; + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try { + while ((bytesRead = input.read(buffer)) != -1) { + output.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return output.toByteArray(); + } +} diff --git a/OpenKeychain/src/test/java/tests/RoboTest.java b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java similarity index 96% rename from OpenKeychain/src/test/java/tests/RoboTest.java rename to OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java index 1a4f6b4aa..346a1f9df 100644 --- a/OpenKeychain/src/test/java/tests/RoboTest.java +++ b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java @@ -9,7 +9,7 @@ import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; @RunWith(RobolectricTestRunner.class) @org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 -public class RoboTest { +public class PgpDecryptVerifyTest { @Test public void testVerifySuccess() throws Exception { diff --git a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java new file mode 100644 index 000000000..265e01170 --- /dev/null +++ b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java @@ -0,0 +1,20 @@ +package tests; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.*; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.testsupport.KeyringTestingHelper; +import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class ProviderHelperKeyringTest { + + @Test + public void testSavePublicKeyring() throws Exception { + Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring()); + } + +} From 1e72f2cba5407cda3e563fc2122312cceb7a9bf9 Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Sat, 21 Jun 2014 10:32:11 -0700 Subject: [PATCH 13/22] Committed submodule --- extern/KeybaseLib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/KeybaseLib b/extern/KeybaseLib index e928c7559..838800d2c 160000 --- a/extern/KeybaseLib +++ b/extern/KeybaseLib @@ -1 +1 @@ -Subproject commit e928c7559672f301354bbf74f47e19aa24e4d2d9 +Subproject commit 838800d2cb57fdd4caca1bdbd919e9d96dae13e9 From 18430bef17234be2446c66405526a158c42c5141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 21 Jun 2014 20:25:51 +0200 Subject: [PATCH 14/22] Start new designed activity --- OpenKeychain/src/main/AndroidManifest.xml | 5 + .../keychain/ui/EditKeyActivityNew.java | 93 ++++++++ .../keychain/ui/EditKeyFragment.java | 200 ++++++++++++++++++ .../keychain/ui/ViewKeyKeysFragment.java | 10 +- .../keychain/ui/ViewKeyMainFragment.java | 15 ++ .../ui/adapter/ViewKeyKeysAdapter.java | 16 ++ .../main/res/layout/edit_key_activity_new.xml | 13 ++ .../src/main/res/layout/edit_key_fragment.xml | 44 ++++ .../res/layout/view_key_main_fragment.xml | 16 ++ 9 files changed, 404 insertions(+), 8 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java create mode 100644 OpenKeychain/src/main/res/layout/edit_key_activity_new.xml create mode 100644 OpenKeychain/src/main/res/layout/edit_key_fragment.xml diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 48677431c..c6e528f4d 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -84,6 +84,11 @@ android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_edit_key" android:windowSoftInputMode="stateHidden" /> + + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.View; +import android.view.View.OnClickListener; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.remote.ui.AccountsListFragment; +import org.sufficientlysecure.keychain.util.Log; + +public class EditKeyActivityNew extends ActionBarActivity { + + private Uri mDataUri; + + private EditKeyFragment mEditKeyFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.edit_key_activity_new); + +// // Inflate a "Done"/"Cancel" custom action bar view +// ActionBarHelper.setTwoButtonView(getSupportActionBar(), +// R.string.btn_save, R.drawable.ic_action_save, +// new OnClickListener() { +// @Override +// public void onClick(View v) { +// // Save +// +// } +// }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, +// new OnClickListener() { +// @Override +// public void onClick(View v) { +// // Cancel +// +// } +// } +// ); + + Uri dataUri = getIntent().getData(); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + finish(); + return; + } + + loadFragment(savedInstanceState, dataUri); + } + + private void loadFragment(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 + mEditKeyFragment = EditKeyFragment.newInstance(dataUri); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.edit_key_fragment_container, mEditKeyFragment) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java new file mode 100644 index 000000000..9231ce142 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter; +import org.sufficientlysecure.keychain.util.Log; + +public class EditKeyFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks { + + public static final String ARG_DATA_URI = "uri"; + + private ListView mUserIds; + private ListView mKeys; + + private static final int LOADER_ID_USER_IDS = 0; + private static final int LOADER_ID_KEYS = 1; + + private ViewKeyUserIdsAdapter mUserIdsAdapter; + private ViewKeyKeysAdapter mKeysAdapter; + + private Uri mDataUri; + + /** + * Creates new instance of this fragment + */ + public static EditKeyFragment newInstance(Uri dataUri) { + EditKeyFragment frag = new EditKeyFragment(); + + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.edit_key_fragment, getContainer()); + + mUserIds = (ListView) view.findViewById(R.id.edit_key_user_ids); + mKeys = (ListView) view.findViewById(R.id.edit_key_keys); +// mActionEdit = view.findViewById(R.id.view_key_action_edit); + + return root; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + + // Inflate a "Done"/"Cancel" custom action bar view + ActionBarHelper.setTwoButtonView(((ActionBarActivity) getActivity()).getSupportActionBar(), + R.string.btn_save, R.drawable.ic_action_save, + new OnClickListener() { + @Override + public void onClick(View v) { + // Save + save(); + } + }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, + new OnClickListener() { + @Override + public void onClick(View v) { + // Cancel + getActivity().finish(); + } + } + ); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + + loadData(dataUri); + } + + private void loadData(Uri dataUri) { + mDataUri = dataUri; + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + +// mActionEncrypt.setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// encrypt(mDataUri); +// } +// }); + + + mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0); + mUserIds.setAdapter(mUserIdsAdapter); + mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0); + mKeys.setAdapter(mKeysAdapter); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + getLoaderManager().initLoader(LOADER_ID_KEYS, null, this); + } + + public Loader onCreateLoader(int id, Bundle args) { + setContentShown(false); + + switch (id) { + case LOADER_ID_USER_IDS: { + Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, null, null, null); + } + + case LOADER_ID_KEYS: { + Uri baseUri = KeychainContract.Keys.buildKeysUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + ViewKeyKeysAdapter.KEYS_PROJECTION, null, null, null); + } + + default: + return null; + } + } + + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + switch (loader.getId()) { + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(data); + break; + + case LOADER_ID_KEYS: + mKeysAdapter.swapCursor(data); + break; + + } + setContentShown(true); + } + + /** + * 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. + */ + public void onLoaderReset(Loader loader) { + switch (loader.getId()) { + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(null); + break; + case LOADER_ID_KEYS: + mKeysAdapter.swapCursor(null); + break; + } + } + + private void save() { + getActivity().finish(); + // TODO + } + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java index f4a44f526..e01a0140c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java @@ -83,17 +83,11 @@ public class ViewKeyKeysFragment extends LoaderFragment implements getLoaderManager().initLoader(0, null, this); } - static final String[] KEYS_PROJECTION = new String[] { - Keys._ID, - Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.HAS_SECRET, - Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED, - Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT - }; - public Loader onCreateLoader(int id, Bundle args) { setContentShown(false); Uri baseUri = Keys.buildKeysUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null); + return new CursorLoader(getActivity(), baseUri, + ViewKeyKeysAdapter.KEYS_PROJECTION, null, null, null); } public void onLoadFinished(Loader loader, Cursor data) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index ae7cef70c..bd29f3820 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -49,6 +49,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements public static final String ARG_DATA_URI = "uri"; private View mActionEdit; + private View mActionEditNew; private View mActionEditDivider; private View mActionEncrypt; private View mActionCertify; @@ -73,6 +74,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); mActionEdit = view.findViewById(R.id.view_key_action_edit); + mActionEditNew = view.findViewById(R.id.view_key_action_edit_new); mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider); mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt); mActionCertify = view.findViewById(R.id.view_key_action_certify); @@ -116,6 +118,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements editKey(mDataUri); } }); + mActionEditNew.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + editKeyNew(mDataUri); + } + }); mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0); mUserIds.setAdapter(mUserIdsAdapter); @@ -256,4 +263,12 @@ public class ViewKeyMainFragment extends LoaderFragment implements startActivityForResult(editIntent, 0); } + private void editKeyNew(Uri dataUri) { + Intent editIntent = new Intent(getActivity(), EditKeyActivityNew.class); +// editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); + editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); + editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); + startActivityForResult(editIntent, 0); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index f4942a2a0..dae287bbc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -53,6 +53,22 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private ColorStateList mDefaultTextColor; + public static final String[] KEYS_PROJECTION = new String[] { + Keys._ID, + Keys.KEY_ID, + Keys.RANK, + Keys.ALGORITHM, + Keys.KEY_SIZE, + Keys.HAS_SECRET, + Keys.CAN_CERTIFY, + Keys.CAN_ENCRYPT, + Keys.CAN_SIGN, + Keys.IS_REVOKED, + Keys.CREATION, + Keys.EXPIRY, + Keys.FINGERPRINT + }; + public ViewKeyKeysAdapter(Context context, Cursor c, int flags) { super(context, c, flags); diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml b/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml new file mode 100644 index 000000000..f96b993c5 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/edit_key_fragment.xml b/OpenKeychain/src/main/res/layout/edit_key_fragment.xml new file mode 100644 index 000000000..dbc0c3941 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/edit_key_fragment.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml index d93420a99..96871aacf 100644 --- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml @@ -74,6 +74,22 @@ android:drawablePadding="8dp" android:gravity="center_vertical" /> + + Date: Sat, 21 Jun 2014 21:31:00 +0200 Subject: [PATCH 15/22] Cleanup pull keybase code --- .../keychain/keyimport/KeybaseKeyserver.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 98e5111f7..29ab06264 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -62,8 +62,7 @@ public class KeybaseKeyserver extends Keyserver { entry.setQuery(mQuery); entry.setOrigin(ORIGIN); - String username = null; - username = match.getUsername(); + String username = match.getUsername(); String fullName = match.getFullName(); String fingerprint = match.getFingerprint(); entry.setFingerprintHex(fingerprint); @@ -93,10 +92,6 @@ public class KeybaseKeyserver extends Keyserver { @Override public String get(String id) throws QueryFailedException { try { - /* - JSONObject user = getUser(id); - return JWalk.getString(user, "them", "public_keys", "primary", "bundle"); - */ return User.keyForUsername(id); } catch (KeybaseException e) { throw new QueryFailedException(e.getMessage()); From 9d20e68f115b8af4b76bf634eb919de9f9397486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 22 Jun 2014 10:59:50 +0200 Subject: [PATCH 16/22] Revert hint_keybase_search --- OpenKeychain/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 91676f09c..e24ac6925 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -332,7 +332,7 @@ Name/Email/Key ID… Search Secret Keys Share Key with… - Search Keybase.io for… + Name/Keybase.io username… 512 From 012b895e4f4fce0b3846bc688bb2738bde4f819a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 22 Jun 2014 12:03:40 +0200 Subject: [PATCH 17/22] Update lib --- extern/openpgp-api-lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib index a77887d32..289d48b63 160000 --- a/extern/openpgp-api-lib +++ b/extern/openpgp-api-lib @@ -1 +1 @@ -Subproject commit a77887d32fae68171fcd0d2989bf537c0c11f0b9 +Subproject commit 289d48b633b7493f2a760583c88670b9fc4ef96f From 857b15cbd5e84dc94350b4b2b1317c5ac411af38 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 22 Jun 2014 13:32:07 +0200 Subject: [PATCH 18/22] use hashcode instead of keyid in import list (keyids may not be unique!) --- .../keychain/keyimport/ImportKeysListEntry.java | 10 ++++++++++ .../keychain/ui/ImportKeysListFragment.java | 2 +- .../keychain/ui/adapter/ImportKeysListLoader.java | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 47265c3aa..0a49cb629 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -46,6 +46,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { private String mExtraData; private String mQuery; private String mOrigin; + private Integer mHashCode = null; private boolean mSelected; @@ -98,6 +99,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable { } }; + public int hashCode() { + if (mHashCode != null) { + return mHashCode; + } + return super.hashCode(); + } + public String getKeyIdHex() { return mKeyIdHex; } @@ -240,6 +248,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { mSecretKey = ring.isSecret(); UncachedPublicKey key = ring.getPublicKey(); + mHashCode = key.hashCode(); + mPrimaryUserId = key.getPrimaryUserId(); mUserIds = key.getUnorderedUserIds(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index d77e3a452..84fd513a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -79,7 +79,7 @@ public class ImportKeysListFragment extends ListFragment implements public ArrayList getSelectedData() { ArrayList result = new ArrayList(); for (ImportKeysListEntry entry : getSelectedEntries()) { - result.add(mCachedKeyData.get(entry.getKeyId())); + result.add(mCachedKeyData.get(entry.hashCode())); } return result; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index c2712e89e..99f959035 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -138,7 +138,7 @@ public class ImportKeysListLoader for(UncachedKeyRing key : rings) { ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key); mData.add(item); - mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded())); + mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(key.getEncoded())); isEmpty = false; } } From 22ea9c60475b20819a29b535b772b699526d0146 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 22 Jun 2014 13:41:54 +0200 Subject: [PATCH 19/22] fix parcel method in ParcelableKeyRing --- .../keychain/keyimport/ParcelableKeyRing.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index fdf561aaf..066c51a13 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -27,7 +27,9 @@ public class ParcelableKeyRing implements Parcelable { public static final Creator CREATOR = new Creator() { public ParcelableKeyRing createFromParcel(final Parcel source) { - return new ParcelableKeyRing(source.createByteArray()); + byte[] bytes = source.createByteArray(); + String expectedFingerprint = source.readString(); + return new ParcelableKeyRing(bytes, expectedFingerprint); } public ParcelableKeyRing[] newArray(final int size) { From 37cee1322ba4f8b5e22538d48a40c0f74941f5c3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 22 Jun 2014 14:17:08 +0200 Subject: [PATCH 20/22] fix indentation in various key import methods --- .../keychain/provider/ProviderHelper.java | 128 +++++++++--------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 0db570be9..f4e19d21a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -498,13 +498,12 @@ public class ProviderHelper { } } - mIndent -= 1; - } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); - mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; + } finally { + mIndent -= 1; } try { @@ -523,19 +522,16 @@ public class ProviderHelper { mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - mIndent -= 1; progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 90, 100); return result; } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); - mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EXC); Log.e(Constants.TAG, "OperationApplicationException during import", e); - mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; } @@ -581,76 +577,81 @@ public class ProviderHelper { log(LogLevel.START, LogType.MSG_IS, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; - - // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog, mIndent); - if (keyRing == null) { - return SaveKeyringResult.RESULT_ERROR; - } - - // IF this is successful, it's a secret key - int result = SaveKeyringResult.SAVED_SECRET; - - // save secret keyring try { - ContentValues values = new ContentValues(); - values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - // insert new version of this keyRing - Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - if (mContentResolver.insert(uri, values) == null) { - log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + + // Canonicalize this key, to assert a number of assumptions made about it. + keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { return SaveKeyringResult.RESULT_ERROR; } - } catch (IOException e) { - Log.e(Constants.TAG, "Failed to encode key!", e); - log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); - return SaveKeyringResult.RESULT_ERROR; - } - { - Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + // IF this is successful, it's a secret key + int result = SaveKeyringResult.SAVED_SECRET; - // first, mark all keys as not available - ContentValues values = new ContentValues(); - values.put(Keys.HAS_SECRET, 0); - mContentResolver.update(uri, values, null, null); + // save secret keyring + try { + ContentValues values = new ContentValues(); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + // insert new version of this keyRing + Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); + if (mContentResolver.insert(uri, values) == null) { + log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + return SaveKeyringResult.RESULT_ERROR; + } + } catch (IOException e) { + Log.e(Constants.TAG, "Failed to encode key!", e); + log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); + return SaveKeyringResult.RESULT_ERROR; + } - values.put(Keys.HAS_SECRET, 1); - // then, mark exactly the keys we have available - log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); - mIndent += 1; - Set available = keyRing.getAvailableSubkeys(); - for (UncachedPublicKey sub : - new IterableIterator(keyRing.getPublicKeys())) { - long id = sub.getKeyId(); - if(available.contains(id)) { - int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", - new String[] { Long.toString(id) }); - if (upd == 1) { - log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ - PgpKeyHelper.convertKeyIdToHex(id) - }); + { + Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + + // first, mark all keys as not available + ContentValues values = new ContentValues(); + values.put(Keys.HAS_SECRET, 0); + mContentResolver.update(uri, values, null, null); + + values.put(Keys.HAS_SECRET, 1); + // then, mark exactly the keys we have available + log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); + mIndent += 1; + Set available = keyRing.getAvailableSubkeys(); + for (UncachedPublicKey sub : + new IterableIterator(keyRing.getPublicKeys())) { + long id = sub.getKeyId(); + if (available.contains(id)) { + int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", + new String[]{Long.toString(id)}); + if (upd == 1) { + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) + }); + } else { + log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) + }); + } } else { - log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ + log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{ PgpKeyHelper.convertKeyIdToHex(id) }); } - } else { - log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{ - PgpKeyHelper.convertKeyIdToHex(id) - }); } + mIndent -= 1; + + // this implicitly leaves all keys which were not in the secret key ring + // with has_secret = 0 } + + log(LogLevel.OK, LogType.MSG_IS_SUCCESS); + return result; + + } finally { mIndent -= 1; - - // this implicitly leaves all keys which were not in the secret key ring - // with has_secret = 0 } - log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return result; - } @@ -738,12 +739,13 @@ public class ProviderHelper { } } - mIndent -= 1; return new SaveKeyringResult(result, mLog); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; } } @@ -833,6 +835,8 @@ public class ProviderHelper { } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC, null); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; } } From 4d091d17847724a0942c9b21d55c466fb7143a3d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 22 Jun 2014 14:45:40 +0200 Subject: [PATCH 21/22] use arraylists instead of arrays in savekeyringparcel --- .../keychain/service/SaveKeyringParcel.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index c68b7c189..47a6cab1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -4,7 +4,7 @@ import android.os.Parcel; import android.os.Parcelable; import java.io.Serializable; -import java.util.HashMap; +import java.util.ArrayList; /** This class is a a transferable representation for a collection of changes * to be done on a keyring. @@ -29,14 +29,14 @@ public class SaveKeyringParcel implements Parcelable { public String newPassphrase; - public String[] addUserIds; - public SubkeyAdd[] addSubKeys; + public ArrayList addUserIds; + public ArrayList addSubKeys; - public SubkeyChange[] changeSubKeys; + public ArrayList changeSubKeys; public String changePrimaryUserId; - public String[] revokeUserIds; - public long[] revokeSubKeys; + public ArrayList revokeUserIds; + public ArrayList revokeSubKeys; public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) { mMasterKeyId = masterKeyId; @@ -73,14 +73,14 @@ public class SaveKeyringParcel implements Parcelable { mMasterKeyId = source.readLong(); mFingerprint = source.createByteArray(); - addUserIds = source.createStringArray(); - addSubKeys = (SubkeyAdd[]) source.readSerializable(); + addUserIds = source.createStringArrayList(); + addSubKeys = (ArrayList) source.readSerializable(); - changeSubKeys = (SubkeyChange[]) source.readSerializable(); + changeSubKeys = (ArrayList) source.readSerializable(); changePrimaryUserId = source.readString(); - revokeUserIds = source.createStringArray(); - revokeSubKeys = source.createLongArray(); + revokeUserIds = source.createStringArrayList(); + revokeSubKeys = (ArrayList) source.readSerializable(); } @Override @@ -88,14 +88,14 @@ public class SaveKeyringParcel implements Parcelable { destination.writeLong(mMasterKeyId); destination.writeByteArray(mFingerprint); - destination.writeStringArray(addUserIds); + destination.writeStringList(addUserIds); destination.writeSerializable(addSubKeys); destination.writeSerializable(changeSubKeys); destination.writeString(changePrimaryUserId); - destination.writeStringArray(revokeUserIds); - destination.writeLongArray(revokeSubKeys); + destination.writeStringList(revokeUserIds); + destination.writeSerializable(revokeSubKeys); } public static final Creator CREATOR = new Creator() { From 9f947aefe63055045b4dfa7087b858f8f3d2987e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 22 Jun 2014 14:46:09 +0200 Subject: [PATCH 22/22] ditch outdated certifyKey operation --- .../keychain/pgp/PgpKeyOperation.java | 54 ------------------- 1 file changed, 54 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index c590200ee..9b3e5bc54 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -481,58 +481,4 @@ public class PgpKeyOperation { } - - /** - * Certify the given pubkeyid with the given masterkeyid. - * - * @param certificationKey Certifying key - * @param publicKey public key to certify - * @param userIds User IDs to certify, must not be null or empty - * @param passphrase Passphrase of the secret key - * @return A keyring with added certifications - */ - public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey, - List userIds, String passphrase) - throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, - PGPException, SignatureException { - - // create a signatureGenerator from the supplied masterKeyId and passphrase - PGPSignatureGenerator signatureGenerator; - { - - if (certificationKey == null) { - throw new PgpGeneralMsgIdException(R.string.error_no_signature_key); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new PgpGeneralMsgIdException(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 - for (String userId : new IterableIterator(userIds.iterator())) { - PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); - publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); - } - - return publicKey; - } - }