From d530a7b0ee0489c81ed841de5a5d94ebceb9d2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 21:52:17 +0200 Subject: [PATCH 01/60] Update buildTools to 19.1 --- OpenKeychain/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index 20e5ca594..a9c7aa282 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -51,7 +51,7 @@ dependencies { android { compileSdkVersion 19 - buildToolsVersion "19.0.3" + buildToolsVersion "19.1" defaultConfig { minSdkVersion 9 From fe1b8468a150679d572cf1cca1e6c99e2ded659c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 21:55:32 +0200 Subject: [PATCH 02/60] Update buildTools to 19.1 --- extern/AndroidBootstrap | 2 +- extern/AppMsg | 2 +- extern/StickyListHeaders | 2 +- extern/html-textview | 2 +- extern/openkeychain-api-lib | 2 +- extern/openpgp-api-lib | 2 +- extern/zxing-android-integration | 2 +- extern/zxing-qr-code | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extern/AndroidBootstrap b/extern/AndroidBootstrap index bfa160c4e..02f02391e 160000 --- a/extern/AndroidBootstrap +++ b/extern/AndroidBootstrap @@ -1 +1 @@ -Subproject commit bfa160c4ef3a1a53aebd68aca9c05b8e546219e0 +Subproject commit 02f02391e3eee9331e07d7690d3b533a8b0f69e2 diff --git a/extern/AppMsg b/extern/AppMsg index ca714df97..61e747419 160000 --- a/extern/AppMsg +++ b/extern/AppMsg @@ -1 +1 @@ -Subproject commit ca714df97bfce67a7a9a1efefd2c49645b6f22e4 +Subproject commit 61e74741909a712db2e0d31ddffa5b7cf37c21f2 diff --git a/extern/StickyListHeaders b/extern/StickyListHeaders index efe46c211..706c0e447 160000 --- a/extern/StickyListHeaders +++ b/extern/StickyListHeaders @@ -1 +1 @@ -Subproject commit efe46c21143cc54a2394303a67822f14580d1d20 +Subproject commit 706c0e447229226b6edc82ab10630d39fd0f6c38 diff --git a/extern/html-textview b/extern/html-textview index c31ef2aff..eedaa334e 160000 --- a/extern/html-textview +++ b/extern/html-textview @@ -1 +1 @@ -Subproject commit c31ef2aff4282ad00af98e879e3e0a6000885b55 +Subproject commit eedaa334e761273efbfc49ded2124df58c8a4d88 diff --git a/extern/openkeychain-api-lib b/extern/openkeychain-api-lib index 26497acb2..175a3cb77 160000 --- a/extern/openkeychain-api-lib +++ b/extern/openkeychain-api-lib @@ -1 +1 @@ -Subproject commit 26497acb27e9f6349c0557b15cd24a5b0b735e74 +Subproject commit 175a3cb772c88c9b50985abc98f81c9ea69c3659 diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib index 650e1ebda..a77887d32 160000 --- a/extern/openpgp-api-lib +++ b/extern/openpgp-api-lib @@ -1 +1 @@ -Subproject commit 650e1ebda82596cd4fbfaae406e6eccf189f4f63 +Subproject commit a77887d32fae68171fcd0d2989bf537c0c11f0b9 diff --git a/extern/zxing-android-integration b/extern/zxing-android-integration index 34029d4dc..1d7878456 160000 --- a/extern/zxing-android-integration +++ b/extern/zxing-android-integration @@ -1 +1 @@ -Subproject commit 34029d4dcac81ec06137a046a189dac608e76efe +Subproject commit 1d787845663fd232f98f5e8e0923733c1a188f2a diff --git a/extern/zxing-qr-code b/extern/zxing-qr-code index 50193905f..9ef2f3b66 160000 --- a/extern/zxing-qr-code +++ b/extern/zxing-qr-code @@ -1 +1 @@ -Subproject commit 50193905fd8cef92140ea242f77b04bb31391c9e +Subproject commit 9ef2f3b66ea7cc283e865ec39434d023a18d17f3 From de6fe46ecab01a55e20ba7e7479c31bf20f51af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 22:02:04 +0200 Subject: [PATCH 03/60] Fix travis for buildTools 19.1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index df700368c..19a36272d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ before_install: - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools # Install required Android components. - - echo "y" | android update sdk -a --filter build-tools-19.0.3,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force + - echo "y" | android update sdk -a --filter build-tools-19.1,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force install: echo "Installation done" script: gradle assemble -S -q From cb3c2b20086bbce4f338e458a00ab18e4dccc99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 22:09:30 +0200 Subject: [PATCH 04/60] Disable robolectric, update to android gradle 0.11.1, update gradle to 1.12 --- OpenKeychain/build.gradle | 8 ++++---- build.gradle | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index a9c7aa282..dca9faba6 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'android' -apply plugin: 'android-test' +//apply plugin: 'android-test' sourceSets { - androidTest { - java.srcDir file('src/test/java') + //androidTest { + //java.srcDir file('src/test/java') // configure the set of classes for JUnit tests // include '**/*Test.class' - } + //} } dependencies { diff --git a/build.gradle b/build.gradle index 815a8ff9a..e3cafe3ce 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ 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.10.0' - classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.10.0' + classpath 'com.android.tools.build:gradle:0.11.1' + //classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.10.0' } } @@ -17,5 +17,5 @@ allprojects { } task wrapper(type: Wrapper) { - gradleVersion = '1.10' + gradleVersion = '1.12' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d8e0b5b29..cc3d5d9a3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Mar 06 22:23:44 CET 2014 +#Mon Jun 09 22:04:23 CEST 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip From 73a825a152ef0d8bad0bbe2568cb2c38e6fa9581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 22:14:30 +0200 Subject: [PATCH 05/60] Really fix travis --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19a36272d..36e8f8fcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,13 +4,13 @@ before_install: # Install base Android SDK - sudo apt-get update -qq - if [ `uname -m` = x86_64 ]; then sudo apt-get install -qq --force-yes libgd2-xpm lib32z1 lib32stdc++6; fi - - wget http://dl.google.com/android/android-sdk_r22.3-linux.tgz - - tar xzf android-sdk_r22.3-linux.tgz + - wget http://dl.google.com/android/android-sdk_r22.6.2-linux.tgz + - tar xzf android-sdk_r22.6.2-linux.tgz - export ANDROID_HOME=$PWD/android-sdk-linux - export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools # Install required Android components. - - echo "y" | android update sdk -a --filter build-tools-19.1,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force + - echo "y" | android update sdk -a --filter build-tools-19.1.0,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force install: echo "Installation done" script: gradle assemble -S -q From 2b825c8df73d8f662b4fc9e896add1c0217ee6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 22:30:12 +0200 Subject: [PATCH 06/60] Update README for build tools --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8327c66b..ee755d2b7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Development mailinglist at http://groups.google.com/d/forum/openpgp-keychain-dev 1. Get all external submodules with ``git submodule update --init --recursive`` 2. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html) 3. Open the Android SDK Manager (shell command: ``android``). -Expand the Tools directory and select "Android SDK Build-tools (Version 19.0.3)". +Expand the Tools directory and select "Android SDK Build-tools (Version 19.1)". 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 From 70d454785fc87dfaf8731a6eb7cebe3fc0056e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 9 Jun 2014 22:51:13 +0200 Subject: [PATCH 07/60] Update example code instructions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee755d2b7..231dc7f16 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ Select everything for the newest SDK Platform (API-Level 19) ### Build API Demo with Gradle -1. Follow 1-3 from above -2. Change to API Demo directory ``cd OpenKeychain-API`` +1. Follow 1-4 from above +2. The example code is available at https://github.com/open-keychain/api-example 3. Execute ``./gradlew build`` ### Development with Android Studio From 97c679ada3461e676f65bd4c3258302ee5a44a51 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:23:24 +0200 Subject: [PATCH 08/60] import-log: add SuperToast external library --- .gitmodules | 3 +++ OpenKeychain/build.gradle | 3 +++ extern/SuperToasts | 1 + settings.gradle | 1 + 4 files changed, 8 insertions(+) create mode 160000 extern/SuperToasts diff --git a/.gitmodules b/.gitmodules index 572293f94..ca014e4dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "extern/openkeychain-api-lib"] path = extern/openkeychain-api-lib url = https://github.com/open-keychain/openkeychain-api-lib.git +[submodule "extern/SuperToasts"] + path = extern/SuperToasts + url = https://github.com/open-keychain/SuperToasts.git diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index c8ef7b418..4050ac139 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -26,6 +26,7 @@ dependencies { compile project(':extern:spongycastle:pkix') compile project(':extern:spongycastle:prov') compile project(':extern:AppMsg:library') + compile project(':extern:SuperToasts:supertoasts') // Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well androidTestCompile 'junit:junit:4.10' @@ -46,6 +47,8 @@ dependencies { androidTestCompile project(':extern:spongycastle:pkix') androidTestCompile project(':extern:spongycastle:prov') androidTestCompile project(':extern:AppMsg:library') + androidTestCompile project(':extern:SuperToasts:supertoasts') + } android { diff --git a/extern/SuperToasts b/extern/SuperToasts new file mode 160000 index 000000000..e93d50cbf --- /dev/null +++ b/extern/SuperToasts @@ -0,0 +1 @@ +Subproject commit e93d50cbf06e4dd7d61d33b6435eb3e7a75a152d diff --git a/settings.gradle b/settings.gradle index b0162875a..5afd28cd3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,3 +11,4 @@ include ':extern:spongycastle:pg' include ':extern:spongycastle:pkix' include ':extern:spongycastle:prov' include ':extern:AppMsg:library' +include ':extern:SuperToasts:supertoasts' From 067ffa876d4ec4e1f2ee63c851cb3c48f7eb1aa2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:30:20 +0200 Subject: [PATCH 09/60] import-log: add OperationResults, use it in ImportKeys operation --- .../keychain/pgp/PgpImportExport.java | 45 +++-- .../keychain/provider/ProviderHelper.java | 167 +++++++++++------- .../service/KeychainIntentService.java | 12 +- .../OperationResultParcel.java | 64 +++++-- .../keychain/service/OperationResults.java | 63 +++++++ OpenKeychain/src/main/res/values/strings.xml | 12 +- 6 files changed, 261 insertions(+), 102 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/{pgp => service}/OperationResultParcel.java (74%) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java 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 5ce0b11dd..ebc5a7868 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,6 +33,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -123,12 +126,9 @@ public class PgpImportExport { } } - /** - * Imports keys from given data. If keyIds is given only those are imported - */ - public Bundle importKeyRings(List entries) + /** Imports keys from given data. If keyIds is given only those are imported */ + public ImportResult importKeyRings(List entries) throws PgpGeneralException, PGPException, IOException { - Bundle returnData = new Bundle(); updateProgress(R.string.progress_importing, 0, 100); @@ -152,14 +152,8 @@ public class PgpImportExport { } } - mProviderHelper.resetLog(); OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); - for(OperationResultParcel.LogEntryParcel loge : result.mLog) { - Log.d(Constants.TAG, - loge.mIndent - + new String(new char[loge.mIndent]).replace("\0", " ") - + mContext.getString(loge.mType.getMsgId(), (Object[]) loge.mParameters)); - } + newKeys += 1; } catch (PgpGeneralException e) { @@ -171,11 +165,30 @@ public class PgpImportExport { updateProgress(position / entries.size() * 100, 100); } - returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); - returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); + OperationLog log = mProviderHelper.getLog(); + int resultType; + // Any key imported - overall success + if (newKeys > 0 || oldKeys > 0) { + if (badKeys > 0) { + resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; + } else if (log.containsWarnings()) { + resultType = ImportResult.RESULT_OK_WITH_WARNINGS; + } else if (newKeys > 0 && oldKeys > 0) { + resultType = ImportResult.RESULT_OK_BOTHKEYS; + } else if (newKeys > 0) { + resultType = ImportResult.RESULT_OK_NEWKEYS; + } else { + resultType = ImportResult.RESULT_OK_UPDATED; + } + // No keys imported, overall failure + } else if (badKeys > 0) { + resultType = ImportResult.RESULT_FAIL_ERROR; + } else { + resultType = ImportResult.RESULT_FAIL_NOTHING; + } + + return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); - return returnData; } public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, 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 5c8bf6752..ab4672a98 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,9 +29,10 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogType; -import org.sufficientlysecure.keychain.pgp.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -48,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -61,18 +63,27 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +/** This class contains high level methods for database access. Despite its + * name, it is not only a helper but actually the main interface for all + * synchronous database operations. + * + * Operations in this class write logs (TODO). These can be obtained from the + * OperationResultParcel return values directly, but are also accumulated over + * the lifetime of the executing ProviderHelper object unless the resetLog() + * method is called to start a new one specifically. + * + */ public class ProviderHelper { private final Context mContext; private final ContentResolver mContentResolver; - private final ArrayList mLog; + private OperationLog mLog; private int mIndent; public ProviderHelper(Context context) { - this(context, new ArrayList(), 0); + this(context, new OperationLog(), 0); } - public ProviderHelper(Context context, ArrayList log, - int indent) { + public ProviderHelper(Context context, OperationLog log, int indent) { mContext = context; mContentResolver = context.getContentResolver(); mLog = log; @@ -81,11 +92,16 @@ public class ProviderHelper { public void resetLog() { if(mLog != null) { - mLog.clear(); + // Start a new log (leaving the old one intact) + mLog = new OperationLog(); mIndent = 0; } } + public OperationLog getLog() { + return mLog; + } + public static class NotFoundException extends Exception { public NotFoundException() { } @@ -97,12 +113,12 @@ public class ProviderHelper { public void log(LogLevel level, LogType type) { if(mLog != null) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, null, mIndent)); + mLog.add(level, type, null, mIndent); } } public void log(LogLevel level, LogType type, String[] parameters) { if(mLog != null) { - mLog.add(new OperationResultParcel.LogEntryParcel(level, type, parameters, mIndent)); + mLog.add(level, type, parameters, mIndent); } } @@ -258,6 +274,9 @@ public class ProviderHelper { return new OperationResultParcel(1, mLog); } + // Canonicalize this key, to assert a number of assumptions made about the key. + keyRing = keyRing.canonicalize(mLog); + UncachedPublicKey masterKey = keyRing.getPublicKey(); long masterKeyId = masterKey.getKeyId(); log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, @@ -282,34 +301,80 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } - // insert new version of this keyRing - ContentValues values = new ContentValues(); - values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); try { - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - } catch (IOException e) { - log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new OperationResultParcel(1, mLog); - } - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - - try { + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); - Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); - operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + { // insert keyring + // insert new version of this keyRing + ContentValues values = new ContentValues(); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + try { + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + } catch (IOException e) { + log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); + return new OperationResultParcel(1, mLog); + } + + Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + } log(LogLevel.INFO, LogType.MSG_IP_INSERT_SUBKEYS); mIndent += 1; - int rank = 0; - for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - log(LogLevel.DEBUG, LogType.MSG_IP_INSERT_SUBKEY, new String[] { - PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) - }); - operations.add(buildPublicKeyOperations(masterKeyId, key, rank)); - ++rank; + { // insert subkeys + Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + int rank = 0; + for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY, new String[]{ + PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) + }); + mIndent += 1; + + ContentValues values = new ContentValues(); + values.put(Keys.MASTER_KEY_ID, masterKeyId); + values.put(Keys.RANK, rank); + + values.put(Keys.KEY_ID, key.getKeyId()); + values.put(Keys.KEY_SIZE, key.getBitStrength()); + values.put(Keys.ALGORITHM, key.getAlgorithm()); + values.put(Keys.FINGERPRINT, key.getFingerprint()); + + boolean c = key.canCertify(), s = key.canSign(), e = key.canEncrypt(); + values.put(Keys.CAN_CERTIFY, c); + values.put(Keys.CAN_SIGN, s); + values.put(Keys.CAN_ENCRYPT, e); + values.put(Keys.IS_REVOKED, key.isRevoked()); + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_FLAGS, new String[] { "X" }); + + Date creation = key.getCreationTime(); + values.put(Keys.CREATION, creation.getTime() / 1000); + if (creation.after(new Date())) { + log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { + creation.toString() + }); + return new OperationResultParcel(1, mLog); + } + Date expiryDate = key.getExpiryTime(); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + if (key.isExpired()) { + log(LogLevel.INFO, LogType.MSG_IP_SUBKEY_EXPIRED, new String[] { + expiryDate.toString() + }); + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[] { + expiryDate.toString() + }); + } + } + + operations.add(ContentProviderOperation.newInsert(uri).withValues(values).build()); + ++rank; + mIndent -= 1; + } } mIndent -= 1; @@ -374,7 +439,12 @@ public class ProviderHelper { // save certificate as primary self-cert item.selfCert = cert; item.isPrimary = cert.isPrimaryUserId(); - item.isRevoked = cert.isRevocation(); + if (cert.isRevocation()) { + item.isRevoked = true; + log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); + } else { + item.isRevoked = false; + } } @@ -500,7 +570,7 @@ public class ProviderHelper { long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, - new String[]{ Long.toString(masterKeyId) }); + new String[]{Long.toString(masterKeyId)}); // save secret keyring try { @@ -575,37 +645,6 @@ public class ProviderHelper { saveSecretKeyRing(secRing); } - /** - * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing - */ - private ContentProviderOperation - buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException { - - ContentValues values = new ContentValues(); - values.put(Keys.MASTER_KEY_ID, masterKeyId); - values.put(Keys.RANK, rank); - - values.put(Keys.KEY_ID, key.getKeyId()); - values.put(Keys.KEY_SIZE, key.getBitStrength()); - values.put(Keys.ALGORITHM, key.getAlgorithm()); - values.put(Keys.FINGERPRINT, key.getFingerprint()); - - values.put(Keys.CAN_CERTIFY, key.canCertify()); - values.put(Keys.CAN_SIGN, key.canSign()); - values.put(Keys.CAN_ENCRYPT, key.canEncrypt()); - values.put(Keys.IS_REVOKED, key.maybeRevoked()); - - values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000); - Date expiryDate = key.getExpiryTime(); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - - Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 498b963f2..f2231fbb5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -174,14 +174,11 @@ public class KeychainIntentService extends IntentService public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature"; - // import - public static final String RESULT_IMPORT_ADDED = "added"; - public static final String RESULT_IMPORT_UPDATED = "updated"; - public static final String RESULT_IMPORT_BAD = "bad"; - // export public static final String RESULT_EXPORT = "exported"; + public static final String RESULT = "result"; + Messenger mMessenger; private boolean mIsCanceled; @@ -648,7 +645,10 @@ public class KeychainIntentService extends IntentService List entries = data.getParcelableArrayList(IMPORT_KEY_LIST); PgpImportExport pgpImportExport = new PgpImportExport(this, this); - Bundle resultData = pgpImportExport.importKeyRings(entries); + OperationResults.ImportResult result = pgpImportExport.importKeyRings(entries); + + Bundle resultData = new Bundle(); + resultData.putParcelable(RESULT, result); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); } catch (Exception e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java similarity index 74% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 216e4b497..a1a1d0067 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -1,9 +1,10 @@ -package org.sufficientlysecure.keychain.pgp; +package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.IterableIterator; import java.util.ArrayList; @@ -17,33 +18,45 @@ import java.util.ArrayList; * */ public class OperationResultParcel implements Parcelable { - /** Holds the overall result. A value of 0 is considered a success, all - * other values may represent failure or varying degrees of success. */ + /** Holds the overall result, the number specifying varying degrees of success. + * Values smaller than 100 are considered an overall success. */ final int mResult; - /// A list of log entries tied to the operation result. - final ArrayList mLog; + public static final int RESULT_OK = 0; + public static final int RESULT_ERROR = 100; - public OperationResultParcel(int result, ArrayList log) { + /// A list of log entries tied to the operation result. + final OperationLog mLog; + + public OperationResultParcel(int result, OperationLog log) { mResult = result; mLog = log; } public OperationResultParcel(Parcel source) { mResult = source.readInt(); - mLog = source.createTypedArrayList(LogEntryParcel.CREATOR); + mLog = new OperationLog(); + mLog.addAll(source.createTypedArrayList(LogEntryParcel.CREATOR)); + } + + public int getResult() { + return mResult; } public boolean isSuccessful() { - return mResult == 0; + return mResult < 100; + } + + public OperationLog getLog() { + return mLog; } /** One entry in the log. */ public static class LogEntryParcel implements Parcelable { - final LogLevel mLevel; - final LogType mType; - final String[] mParameters; - final int mIndent; + public final LogLevel mLevel; + public final LogType mType; + public final String[] mParameters; + public final int mIndent; public LogEntryParcel(LogLevel level, LogType type, String[] parameters, int indent) { mLevel = level; @@ -98,10 +111,14 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_IMPORTING (R.string.msg_ip_importing), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), - MSG_IP_INSERT_SUBKEY (R.string.msg_ip_insert_subkey), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), + MSG_IP_SUBKEY (R.string.msg_ip_subkey), + MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), + MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), + MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), + MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), @@ -113,6 +130,7 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), + MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), @@ -138,6 +156,7 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { + OK, DEBUG, INFO, WARN, @@ -166,4 +185,23 @@ public class OperationResultParcel implements Parcelable { } }; + public static class OperationLog extends ArrayList { + + /// Simple convenience method + public void add(LogLevel level, LogType type, String[] parameters, int indent) { + add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); + } + + public boolean containsWarnings() { + int warn = LogLevel.WARN.ordinal(); + for(LogEntryParcel entry : new IterableIterator(iterator())) { + if (entry.mLevel.ordinal() >= warn) { + return true; + } + } + return false; + } + + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java new file mode 100644 index 000000000..e08b50500 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -0,0 +1,63 @@ +package org.sufficientlysecure.keychain.service; + +import android.os.Parcel; + +public abstract class OperationResults { + + public static class ImportResult extends OperationResultParcel { + + public final int mNewKeys, mUpdatedKeys, mBadKeys; + + // Operation ok, at least one new key (no warnings) + public static final int RESULT_OK_NEWKEYS = 1; + // Operation ok, at least one new and one updated key (no warnings) + public static final int RESULT_OK_BOTHKEYS = 2; + // Operation ok, no new keys but upated ones (no warnings) + public static final int RESULT_OK_UPDATED = 3; + // Operation ok, but with warnings + public static final int RESULT_OK_WITH_WARNINGS = 4; + + // Operation partially ok, but at least one key failed! + public static final int RESULT_PARTIAL_WITH_ERRORS = 50; + + // Operation failed, errors thrown and no new keys imported + public static final int RESULT_FAIL_ERROR = 100; + // Operation failed, no keys to import... + public static final int RESULT_FAIL_NOTHING = 101; + + public ImportResult(Parcel source) { + super(source); + mNewKeys = source.readInt(); + mUpdatedKeys = source.readInt(); + mBadKeys = source.readInt(); + } + + public ImportResult(int result, OperationLog log, + int newKeys, int updatedKeys, int badKeys) { + super(result, log); + mNewKeys = newKeys; + mUpdatedKeys = updatedKeys; + mBadKeys = badKeys; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mNewKeys); + dest.writeInt(mUpdatedKeys); + dest.writeInt(mBadKeys); + } + + public static Creator CREATOR = new Creator() { + public ImportResult createFromParcel(final Parcel source) { + return new ImportResult(source); + } + + public ImportResult[] newArray(final int size) { + return new ImportResult[size]; + } + }; + + } + +} diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 6f21900fe..86219da70 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -529,13 +529,17 @@ Operation failed due to i/o error Operation failed due to database error Operation failed due to internal error - Importing public keyring + Importing public keyring %s Inserting keyring data - Inserting subkey %s Inserting subkeys Preserving available secret key - Re-inserting secret key + Processing subkey %s + Subkey expired on %s + Subkey expires on %s + Subkey flags: %s + Subkey creation date lies in the future! (%s) Successfully inserted public keyring + Re-inserting secret key Retrieving trusted keys Using %s trusted keys Secret key available, self certificates are trusted @@ -546,6 +550,7 @@ Classifying user ids Inserting user ids Processing user id %s + Found uid revocation certificate Bad self certificate encountered! Found good self certificate Ignoring older self certificate @@ -558,5 +563,6 @@ Marked %s as available Marked %s as stripped Successfully inserted secret keyring + Log From cdc61c43927f11835fffa090ccb045d206728692 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:51:16 +0200 Subject: [PATCH 10/60] canonicalize: first step(s) --- .../keyimport/ImportKeysListEntry.java | 2 +- .../keychain/pgp/UncachedKeyRing.java | 55 +++++++++++++++++++ .../keychain/pgp/UncachedPublicKey.java | 12 +++- 3 files changed, 66 insertions(+), 3 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 c43f72235..7a7475603 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -229,7 +229,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.keyId = key.getKeyId(); this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); - this.revoked = key.maybeRevoked(); + this.revoked = key.isRevoked(); this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); this.bitStrength = key.getBitStrength(); final int algorithm = key.getAlgorithm(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 1264c8c36..624b7d068 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -7,9 +7,14 @@ import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -169,4 +174,54 @@ public class UncachedKeyRing { return result; } + /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be + * applied to public keyrings only. + * + * More specifically: + * - Remove all non-verifying self-certificates + * - Remove all expired self-certificates + * - Remove all certificates flagged as "local" + * - Remove all certificates which are superseded by a newer one on the same target + * + * After this cleaning, a number of checks are done: + * - See if each subkey retains a valid self certificate + * - See if each user id retains a valid self certificate + * + * This operation writes an OperationLog which can be used as part of a OperationResultParcel. + * + * If any of these checks fail, the operation as a whole fails and the keyring is declared + * unusable. (TODO: allow forcing of import?) + * + * TODO implement + * + * @return A canonicalized key + * + */ + public UncachedKeyRing canonicalize(OperationLog log) { + if(isSecret()) { + throw new RuntimeException("Tried to canonicalize non-secret keyring. " + + "This is a programming error and should never happen!"); + } + + // dummy + log.add(LogLevel.INFO, LogType.MSG_IP_BAD_TYPE_SECRET, null, 0); + + /* + // Remove all non-verifying self certificates + for (PGPPublicKey key : new IterableIterator(mRing.getPublicKeys())) { + + for (PGPSignature sig : new IterableIterator( + key.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION + : PGPSignature.SUBKEY_REVOCATION))) { + return true; + } + + }*/ + + return this; + + + } + + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index 108c8c8c3..33db7771b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; @@ -9,6 +10,7 @@ import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProv import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.IterableIterator; +import java.security.SignatureException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -28,8 +30,13 @@ public class UncachedPublicKey { } /** The revocation signature is NOT checked here, so this may be false! */ - public boolean maybeRevoked() { - return mPublicKey.isRevoked(); + public boolean isRevoked() { + for (PGPSignature sig : new IterableIterator( + mPublicKey.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION + : PGPSignature.SUBKEY_REVOCATION))) { + return true; + } + return false; } public Date getCreationTime() { @@ -193,4 +200,5 @@ public class UncachedPublicKey { } }; } + } From d73a3e2fa89663e133ce4750e4aba86354c8b252 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:52:38 +0200 Subject: [PATCH 11/60] import-log: use supertoast in ImportKeyActivity --- .../keychain/ui/ImportKeysActivity.java | 113 ++++++++++++++---- 1 file changed, 87 insertions(+), 26 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 48602aaa1..d1869454d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -38,7 +38,10 @@ import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; -import com.devspark.appmsg.AppMsg; +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; +import com.github.johnpersano.supertoasts.util.OnClickWrapper; +import com.github.johnpersano.supertoasts.util.Style; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -47,7 +50,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment; +import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -135,6 +138,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O } handleActions(savedInstanceState, getIntent()); + } protected void handleActions(Bundle savedInstanceState, Intent intent) { @@ -331,8 +335,11 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O public void loadFromFingerprint(Bundle savedInstanceState, String fingerprint) { if (fingerprint == null || fingerprint.length() < 40) { - AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint, - AppMsg.STYLE_ALERT).show(); + SuperCardToast toast = SuperCardToast.create(this, + getString(R.string.import_qr_code_too_short_fingerprint), + SuperToast.Duration.LONG); + toast.setBackground(SuperToast.Background.RED); + toast.show(); return; } @@ -368,34 +375,84 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { // get returned data bundle Bundle returnData = message.getData(); + final ImportResult result = + returnData.getParcelable(KeychainIntentService.RESULT); + + // , make pessimistic assumptions + String str = Integer.toString(result.getResult()); + int duration = 0, color = Style.RED; + + switch(result.getResult()) { + case ImportResult.RESULT_OK_NEWKEYS: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_added, result.mNewKeys, result.mNewKeys); + break; + + case ImportResult.RESULT_OK_UPDATED: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_updated, result.mNewKeys, result.mNewKeys); + break; + + case ImportResult.RESULT_OK_BOTHKEYS: + color = Style.GREEN; + duration = SuperToast.Duration.LONG; + str = getResources().getQuantityString( + R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); + str += getResources().getQuantityString( + R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys); + break; + + case ImportResult.RESULT_OK_WITH_WARNINGS: + str = "ok with warnings"; + color = Style.ORANGE; + break; + + case ImportResult.RESULT_PARTIAL_WITH_ERRORS: + str = "partial with errors"; + color = Style.ORANGE; + break; + + case ImportResult.RESULT_FAIL_ERROR: + str = "fail error"; + color = Style.RED; + break; + + case ImportResult.RESULT_FAIL_NOTHING: + str = getString(R.string.no_keys_added_or_updated); + color = Style.RED; + break; - int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED); - int updated = returnData - .getInt(KeychainIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - String addedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, added, added); - String updatedStr = getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, updated, updated); - toastMessage = addedStr + updatedStr; - } else if (added > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_added, - added, added); - } else if (updated > 0) { - toastMessage = getResources().getQuantityString(R.plurals.keys_updated, - updated, updated); - } else { - toastMessage = getString(R.string.no_keys_added_or_updated); } - AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) - .show(); + SuperCardToast toast = new SuperCardToast(ImportKeysActivity.this, + SuperToast.Type.BUTTON, Style.getStyle(color, SuperToast.Animations.POPUP)); + toast.setText(str); + toast.setDuration(duration); + toast.setIndeterminate(duration == 0); + toast.setButtonText("View log"); + toast.setSwipeToDismiss(true); + toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", + new SuperToast.OnClickListener() { + @Override + public void onClick(View view, Parcelable token) { + // Intent intent = new Intent( + // ImportKeysActivity.this, LogDisplayActivity.class); + // intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + // startActivity(intent); + } + })); + toast.show(); + + /* if (bad > 0) { BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad); badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog"); } + */ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData); @@ -483,7 +540,11 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O startService(intent); } else { - AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show(); + SuperCardToast toast = SuperCardToast.create(this, + getString(R.string.error_nothing_import), + SuperToast.Duration.LONG); + toast.setBackground(SuperToast.Background.RED); + toast.show(); } } From 7324bfcb536b4975365ab9affa0a71ef29e29b93 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 01:53:25 +0200 Subject: [PATCH 12/60] import-log: add LogDisplay activity --- OpenKeychain/src/main/AndroidManifest.xml | 6 ++ .../keychain/ui/ImportKeysActivity.java | 8 +- .../keychain/ui/LogDisplayActivity.java | 17 ++++ .../keychain/ui/LogDisplayFragment.java | 86 +++++++++++++++++++ .../main/res/layout/import_keys_activity.xml | 37 ++++---- .../main/res/layout/log_display_activity.xml | 46 ++++++++++ .../main/res/layout/log_display_fragment.xml | 11 +++ .../src/main/res/layout/log_display_item.xml | 14 +++ 8 files changed, 204 insertions(+), 21 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java create mode 100644 OpenKeychain/src/main/res/layout/log_display_activity.xml create mode 100644 OpenKeychain/src/main/res/layout/log_display_fragment.xml create mode 100644 OpenKeychain/src/main/res/layout/log_display_item.xml diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index f4007c098..f33886d44 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -434,6 +434,12 @@ + + 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 d1869454d..90d772bd1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -438,10 +438,10 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O new SuperToast.OnClickListener() { @Override public void onClick(View view, Parcelable token) { - // Intent intent = new Intent( - // ImportKeysActivity.this, LogDisplayActivity.class); - // intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - // startActivity(intent); + Intent intent = new Intent( + ImportKeysActivity.this, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + startActivity(intent); } })); toast.show(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java new file mode 100644 index 000000000..28c120d01 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -0,0 +1,17 @@ +package org.sufficientlysecure.keychain.ui; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +import org.sufficientlysecure.keychain.R; + +public class LogDisplayActivity extends ActionBarActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.log_display_activity); + } + +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java new file mode 100644 index 000000000..36d3e932e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -0,0 +1,86 @@ +package org.sufficientlysecure.keychain.ui; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryParcel; + +import java.util.ArrayList; + +public class LogDisplayFragment extends ListFragment { + + LogAdapter mAdapter; + + public static final String EXTRA_RESULT = "log"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Intent intent = getActivity().getIntent(); + if (intent.getExtras() == null || !intent.getExtras().containsKey(EXTRA_RESULT)) { + getActivity().finish(); + return; + } + + OperationResultParcel result = intent.getParcelableExtra(EXTRA_RESULT); + if (result == null) { + getActivity().finish(); + return; + } + + mAdapter = new LogAdapter(getActivity(), result.getLog()); + setListAdapter(mAdapter); + + } + + private class LogAdapter extends ArrayAdapter { + + private LayoutInflater mInflater; + private int dipFactor; + + public LogAdapter(Context context, ArrayList log) { + super(context, R.layout.log_display_item, log); + mInflater = LayoutInflater.from(getContext()); + dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + (float) 6, getResources().getDisplayMetrics()); + + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LogEntryParcel entry = getItem(position); + TextView text; + if (convertView == null) { + convertView = mInflater.inflate(R.layout.log_display_item, parent, false); + text = (TextView) convertView.findViewById(R.id.log_text); + convertView.setTag(text); + } else { + text = (TextView) convertView.getTag(); + } + + text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); + text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + switch (entry.mLevel) { + case DEBUG: text.setTextColor(Color.GRAY); break; + case INFO: text.setTextColor(Color.BLACK); break; + case WARN: text.setTextColor(Color.YELLOW); break; + case ERROR: text.setTextColor(Color.RED); break; + } + + return convertView; + } + + } +} diff --git a/OpenKeychain/src/main/res/layout/import_keys_activity.xml b/OpenKeychain/src/main/res/layout/import_keys_activity.xml index 2a332823e..0486b6bd6 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_activity.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_activity.xml @@ -1,22 +1,37 @@ - + android:layout_height="fill_parent" + android:orientation="vertical"> + + + + @@ -43,16 +58,4 @@ style="@style/SelectableItem" /> - - - \ No newline at end of file + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/log_display_activity.xml b/OpenKeychain/src/main/res/layout/log_display_activity.xml new file mode 100644 index 000000000..591e2650c --- /dev/null +++ b/OpenKeychain/src/main/res/layout/log_display_activity.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/log_display_fragment.xml b/OpenKeychain/src/main/res/layout/log_display_fragment.xml new file mode 100644 index 000000000..442e72d09 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/log_display_fragment.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_item.xml new file mode 100644 index 000000000..f0e0e4ecb --- /dev/null +++ b/OpenKeychain/src/main/res/layout/log_display_item.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file From eac582a313c779e77b0fd67358417d512680facd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 13:52:05 +0200 Subject: [PATCH 13/60] import-log: some cosmetics --- .../keychain/pgp/UncachedKeyRing.java | 5 +- .../keychain/ui/ImportKeysActivity.java | 3 +- .../keychain/ui/LogDisplayFragment.java | 2 +- .../drawable-hdpi/ic_action_view_as_list.png | Bin 0 -> 308 bytes .../drawable-mdpi/ic_action_view_as_list.png | Bin 0 -> 246 bytes .../drawable-xhdpi/ic_action_view_as_list.png | Bin 0 -> 337 bytes .../ic_action_view_as_list.png | Bin 0 -> 431 bytes OpenKeychain/src/main/res/values/strings.xml | 49 ++++++++++-------- 8 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 624b7d068..7853d0b00 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -204,7 +204,8 @@ public class UncachedKeyRing { } // dummy - log.add(LogLevel.INFO, LogType.MSG_IP_BAD_TYPE_SECRET, null, 0); + log.add(LogLevel.START, LogType.MSG_KC, + new String[] { PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, 0); /* // Remove all non-verifying self certificates @@ -218,6 +219,8 @@ public class UncachedKeyRing { }*/ + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, 0); + return this; 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 90d772bd1..9932e3e18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -432,8 +432,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setText(str); toast.setDuration(duration); toast.setIndeterminate(duration == 0); - toast.setButtonText("View log"); toast.setSwipeToDismiss(true); + toast.setButtonIcon(R.drawable.ic_action_view_as_list, + getResources().getString(R.string.view_log)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override 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 36d3e932e..5e038d3f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -54,7 +54,7 @@ public class LogDisplayFragment extends ListFragment { super(context, R.layout.log_display_item, log); mInflater = LayoutInflater.from(getContext()); dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 6, getResources().getDisplayMetrics()); + (float) 10, getResources().getDisplayMetrics()); } diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_view_as_list.png new file mode 100644 index 0000000000000000000000000000000000000000..86da228e978a34f5fa521e94f8b4ecbf3eda9302 GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G*F0SuLn>~)nR$@6*?@;Z`bs`q0;3p5b_uT$!{rMzPcW@H z$gpx^MM!N(s9{^jW|5DMoBE$T>Im6>XfYE|JA;MEyZ!rS6o31a(erB7oZ`(|2crHY zun458$N(i{JMAB@j5+P>mdmDadyZB^&gFw54nlnv3UNwRfNDef~fLGeb^x z%Gx63=xHe%a#9?BCReGrJ!(?%Qz{IZRgmvv4FO#mY=aRdMW literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_view_as_list.png new file mode 100644 index 0000000000000000000000000000000000000000..ccb4c7d7b2a505b6dfb862afbb78ea0a12fb0dea GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GGdx`!Ln>}1B}lL?PH13MwmbR%|Nrw#x>S35Uha4j!STX$ zSt*ABZ;1%c4(5oCkm-Cq$2TvL;PJ6v@M5;7F8di4DT_^u7l?P{Pt4dRB_JMrnsJ}Q zpP4S~AA*couCQ5gZDaUoEOd4nQ_}pz$sDz8w-~pd@M+1qQgk3KD%IKP>PJbf(Bo>0 sr(VoRHA%UAAhIpcfUC`#fy017>at4Mx~uA6fo^2*boFyt=akR{0BOxw)&Kwi literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_action_view_as_list.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c93c8c2038d2bfc36c679ef8ca2c2008f3ff31 GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XK6|=2hE&{oGxIg?VFezxg{Op0O05yRmat;o#B~Ry5_ukZ zNiJA=qUfnL=hJYx7{hbdTY#Dv80_dJPOL3Ji>Ww;ovk4+pxE5#;O# c+Ioy^3>WS@v@Ctmy9y-Y>FVdQ&MBb@0B}!#EC2ui literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_view_as_list.png new file mode 100644 index 0000000000000000000000000000000000000000..46004164086edec3c8b6e95541e90817db41aa08 GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGok|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+7y~_B978H@y}5fa_mF`I>xCVUrZXlmh@Fty;5M_3an{TP zMz#hGrWNo89;R7}E^7*$(H*o@pw;b zP>!@KXAyq^zec7{@SltuXBik3fT-bJm}9Yc&59bIvcvC<->Nb1SnBs9q*C2R%h4;q z?DVDZ8}@6W`4T4g$zR_qq{6_Wz`%0huF;n#6PHLXU|A@9jdL*zC(w2WhLWv3bA!}v zuG%PM&a_#7$<5e-bJ0;D6FalrC5PT~El8;iZ4zi;5NHU!a9MrNGOPTV`q8`(gyVv* za_(loP@?|EG}PFE0cZ{oF&VHh2{16dm}Xz_W4BKI0|qc)y~8TO5M^(FDfCj$2at%T LtDnm{r-UW|l+l=o literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 86219da70..e1ff77c63 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -28,6 +28,7 @@ Certify Identities Key Details Help + Log Identities @@ -499,28 +500,7 @@ error! key unavailable - - Certifier - Certificate Details - Identity - <unknown> - No certificates for this key - Identities to certify - Revocation Reason - Verification Status - Type - Key not found! - Error processing key! - subkey unavailable - stripped - Secret keys can only be deleted individually! - View Certificate Details - unknown - cannot sign - Encoding error - No encryption subkey available! - - + Applying insert batch operation. Tried to import secret keyring as public. This is a bug, please file a report! No old key deleted (creating a new one?) @@ -556,6 +536,8 @@ Ignoring older self certificate Using more recent good self certificate Tried to import public keyring as secret. This is a bug, please file a report! + + Importing secret key %s Processing secret subkeys Error encoding keyring @@ -563,6 +545,27 @@ Marked %s as available Marked %s as stripped Successfully inserted secret keyring - Log + + + Certifier + Certificate Details + Identity + <unknown> + No certificates for this key + Identities to certify + Revocation Reason + Verification Status + Type + Key not found! + Error processing key! + subkey unavailable + stripped + Secret keys can only be deleted individually! + View Certificate Details + unknown + cannot sign + Encoding error + No encryption subkey available! + View Log From e41e6ea0deec06703c5b9c80e429aff8ab110534 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 15:27:26 +0200 Subject: [PATCH 14/60] import-log: more interface work --- .../keychain/pgp/PgpImportExport.java | 2 - .../keychain/provider/ProviderHelper.java | 44 +++++++++++++------ .../service/OperationResultParcel.java | 26 ++++++++--- .../keychain/service/OperationResults.java | 2 - .../keychain/ui/ImportKeysActivity.java | 36 ++++++++------- .../keychain/ui/LogDisplayFragment.java | 1 + OpenKeychain/src/main/res/values/strings.xml | 33 +++++++++----- 7 files changed, 94 insertions(+), 50 deletions(-) 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 ebc5a7868..4cb41708d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -171,8 +171,6 @@ public class PgpImportExport { if (newKeys > 0 || oldKeys > 0) { if (badKeys > 0) { resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; - } else if (log.containsWarnings()) { - resultType = ImportResult.RESULT_OK_WITH_WARNINGS; } else if (newKeys > 0 && oldKeys > 0) { resultType = ImportResult.RESULT_OK_BOTHKEYS; } else if (newKeys > 0) { 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 ab4672a98..cbaf72270 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -252,7 +251,7 @@ public class ProviderHelper { throw new NotFoundException("Secret key not available!"); } return secret - ? new WrappedSecretKeyRing(blob, hasAnySecret, verified) + ? new WrappedSecretKeyRing(blob, true, verified) : new WrappedPublicKeyRing(blob, hasAnySecret, verified); } else { throw new NotFoundException("Key not found!"); @@ -274,14 +273,15 @@ public class ProviderHelper { return new OperationResultParcel(1, mLog); } - // Canonicalize this key, to assert a number of assumptions made about the key. + long masterKeyId = keyRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IP, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + + // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog); UncachedPublicKey masterKey = keyRing.getPublicKey(); - long masterKeyId = masterKey.getKeyId(); - log(LogLevel.INFO, LogType.MSG_IP_IMPORTING, - new String[]{Long.toString(masterKeyId)}); - mIndent += 1; // IF there is a secret key, preserve it! UncachedKeyRing secretRing; @@ -342,12 +342,28 @@ public class ProviderHelper { values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.FINGERPRINT, key.getFingerprint()); - boolean c = key.canCertify(), s = key.canSign(), e = key.canEncrypt(); + boolean c = key.canCertify(), e = key.canEncrypt(), s = key.canSign(); values.put(Keys.CAN_CERTIFY, c); - values.put(Keys.CAN_SIGN, s); values.put(Keys.CAN_ENCRYPT, e); + values.put(Keys.CAN_SIGN, s); values.put(Keys.IS_REVOKED, key.isRevoked()); - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_FLAGS, new String[] { "X" }); + if (c) { + if (e) { + log(LogLevel.DEBUG,s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + } + } else { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + } + } Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); @@ -531,7 +547,7 @@ public class ProviderHelper { mIndent -= 1; } - log(LogLevel.INFO, LogType.MSG_IP_SUCCESS); + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; return new OperationResultParcel(0, mLog); @@ -569,8 +585,8 @@ public class ProviderHelper { } long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.INFO, LogType.MSG_IS_IMPORTING, - new String[]{Long.toString(masterKeyId)}); + log(LogLevel.START, LogType.MSG_IS, + new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); // save secret keyring try { @@ -626,7 +642,7 @@ public class ProviderHelper { // with has_secret = 0 } - log(LogLevel.INFO, LogType.MSG_IS_SUCCESS); + log(LogLevel.OK, LogType.MSG_IS_SUCCESS); return new OperationResultParcel(0, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index a1a1d0067..85b7c456f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -101,6 +101,9 @@ public class OperationResultParcel implements Parcelable { } public static enum LogType { + + // import public + MSG_IP(R.string.msg_ip), MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch), MSG_IP_BAD_TYPE_SECRET (R.string.msg_ip_bad_type_secret), MSG_IP_DELETE_OLD_FAIL (R.string.msg_ip_delete_old_fail), @@ -109,7 +112,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_IO_EXC (R.string.msg_ip_fail_io_exc), MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), - MSG_IP_IMPORTING (R.string.msg_ip_importing), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), @@ -118,6 +120,14 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), + MSG_IP_SUBKEY_FLAGS_CES (R.string.msg_ip_subkey_flags_ces), + MSG_IP_SUBKEY_FLAGS_CEX (R.string.msg_ip_subkey_flags_cex), + MSG_IP_SUBKEY_FLAGS_CXS (R.string.msg_ip_subkey_flags_cxs), + MSG_IP_SUBKEY_FLAGS_XES (R.string.msg_ip_subkey_flags_xes), + MSG_IP_SUBKEY_FLAGS_CXX (R.string.msg_ip_subkey_flags_cxx), + MSG_IP_SUBKEY_FLAGS_XEX (R.string.msg_ip_subkey_flags_xex), + MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), + MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), @@ -135,14 +145,20 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), + + // import secret + MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), - MSG_IS_IMPORTING (R.string.msg_is_importing), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), MSG_IS_SUBKEY_OK (R.string.msg_is_subkey_ok), MSG_IS_SUBKEY_STRIPPED (R.string.msg_is_subkey_stripped), MSG_IS_SUCCESS (R.string.msg_is_success), + + // keyring canonicalization + MSG_KC(R.string.msg_kc), + MSG_KC_SUCCESS(R.string.msg_kc_success), ; private final int mMsgId; @@ -156,12 +172,12 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { - OK, + START, // should occur once at the start of each independent operation + OK, // should occur once at the end of a successful operation + ERROR, // should occur once at the end of a failed operation DEBUG, INFO, WARN, - /** If any ERROR log entry is included in the result, the overall operation should have failed. */ - ERROR, } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index e08b50500..bee6917be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -14,8 +14,6 @@ public abstract class OperationResults { public static final int RESULT_OK_BOTHKEYS = 2; // Operation ok, no new keys but upated ones (no warnings) public static final int RESULT_OK_UPDATED = 3; - // Operation ok, but with warnings - public static final int RESULT_OK_WITH_WARNINGS = 4; // Operation partially ok, but at least one key failed! public static final int RESULT_PARTIAL_WITH_ERRORS = 50; 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 9932e3e18..61d7e7949 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -378,37 +378,37 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O final ImportResult result = returnData.getParcelable(KeychainIntentService.RESULT); - // , make pessimistic assumptions - String str = Integer.toString(result.getResult()); - int duration = 0, color = Style.RED; + String str = ""; + boolean hasWarnings = result.getLog().containsWarnings(); + int duration = 0, color = hasWarnings ? Style.ORANGE : Style.GREEN; + String withWarnings = hasWarnings + ? getResources().getString(R.string.with_warnings) : ""; switch(result.getResult()) { case ImportResult.RESULT_OK_NEWKEYS: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( - R.plurals.keys_added, result.mNewKeys, result.mNewKeys); + R.plurals.keys_added, result.mNewKeys, result.mNewKeys, withWarnings); break; case ImportResult.RESULT_OK_UPDATED: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( - R.plurals.keys_updated, result.mNewKeys, result.mNewKeys); + R.plurals.keys_updated, result.mNewKeys, result.mNewKeys, withWarnings); break; case ImportResult.RESULT_OK_BOTHKEYS: - color = Style.GREEN; - duration = SuperToast.Duration.LONG; + if (!hasWarnings) { + duration = SuperToast.Duration.LONG; + } str = getResources().getQuantityString( R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); str += getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys); - break; - - case ImportResult.RESULT_OK_WITH_WARNINGS: - str = "ok with warnings"; - color = Style.ORANGE; + R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); break; case ImportResult.RESULT_PARTIAL_WITH_ERRORS: @@ -435,6 +435,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setSwipeToDismiss(true); toast.setButtonIcon(R.drawable.ic_action_view_as_list, getResources().getString(R.string.view_log)); + toast.setButtonTextColor(R.color.emphasis_dark); + toast.setTextColor(R.color.emphasis_dark); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override 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 5e038d3f6..275c9ac18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -73,6 +73,7 @@ public class LogDisplayFragment extends ListFragment { text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); switch (entry.mLevel) { + case OK: text.setTextColor(Color.GREEN); break; case DEBUG: text.setTextColor(Color.GRAY); break; case INFO: text.setTextColor(Color.BLACK); break; case WARN: text.setTextColor(Color.YELLOW); break; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index e1ff77c63..b60e281ee 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -222,23 +222,24 @@ Also export secret keys? - Successfully added %d key - Successfully added %d keys + Successfully added %1$d key + Successfully added %1$d keys - and updated %d key. - and updated %d keys. + and updated %1$d key%2$s. + and updated %1$d keys%2$s. - Successfully added %d key. - Successfully added %d keys. + Successfully added %1$d key%2$s. + Successfully added %1$d keys%2$s. - Successfully updated %d key. - Successfully updated %d keys. + Successfully updated %1$d key%2$s. + Successfully updated %1$d keys%2$s. No keys added or updated. + , with warnings Successfully exported 1 key. Successfully exported %d keys. No keys exported. @@ -509,7 +510,7 @@ Operation failed due to i/o error Operation failed due to database error Operation failed due to internal error - Importing public keyring %s + Importing public keyring %s Inserting keyring data Inserting subkeys Preserving available secret key @@ -517,6 +518,14 @@ Subkey expired on %s Subkey expires on %s Subkey flags: %s + Subkey flags: certify, encrypt, sign + Subkey flags: certify, encrypt + Subkey flags: certify, sign + Subkey flags: encrypt, sign + Subkey flags: certify + Subkey flags: encrypt + Subkey flags: sign + Subkey flags: none Subkey creation date lies in the future! (%s) Successfully inserted public keyring Re-inserting secret key @@ -538,7 +547,7 @@ Tried to import public keyring as secret. This is a bug, please file a report! - Importing secret key %s + Importing secret key %s Processing secret subkeys Error encoding keyring Subkey %s unavailable in public key @@ -546,6 +555,10 @@ Marked %s as stripped Successfully inserted secret keyring + + Canonicalizing keyring %s + Successfully canonicalized keyring + Certifier Certificate Details From f38556cab1215f1e49f2e6c2e90627a4c00b02d5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 16:24:04 +0200 Subject: [PATCH 15/60] import-log: switch to flags instead of statuses for result int --- .../keychain/pgp/PgpImportExport.java | 46 +++++----- .../keychain/provider/ProviderHelper.java | 24 +++-- .../service/OperationResultParcel.java | 9 +- .../keychain/service/OperationResults.java | 55 +++++++++--- .../keychain/ui/ImportKeysActivity.java | 87 +++++++++---------- .../src/main/res/values-de/strings.xml | 10 +-- .../src/main/res/values-es/strings.xml | 10 +-- .../src/main/res/values-fr/strings.xml | 10 +-- .../src/main/res/values-it/strings.xml | 10 +-- .../src/main/res/values-ja/strings.xml | 10 +-- .../src/main/res/values-nl/strings.xml | 10 +-- .../src/main/res/values-pl/strings.xml | 10 +-- .../src/main/res/values-ru/strings.xml | 10 +-- .../src/main/res/values-sl/strings.xml | 10 +-- .../src/main/res/values-uk/strings.xml | 10 +-- OpenKeychain/src/main/res/values/strings.xml | 42 ++++----- 16 files changed, 204 insertions(+), 159 deletions(-) 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 4cb41708d..bafb086d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -33,9 +33,9 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -152,9 +152,12 @@ public class PgpImportExport { } } - OperationResultParcel result = mProviderHelper.savePublicKeyRing(key); - - newKeys += 1; + SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); + if (result.updated()) { + newKeys += 1; + } else { + oldKeys += 1; + } } catch (PgpGeneralException e) { Log.e(Constants.TAG, "Encountered bad key on import!", e); @@ -166,23 +169,26 @@ public class PgpImportExport { } OperationLog log = mProviderHelper.getLog(); - int resultType; - // Any key imported - overall success - if (newKeys > 0 || oldKeys > 0) { - if (badKeys > 0) { - resultType = ImportResult.RESULT_PARTIAL_WITH_ERRORS; - } else if (newKeys > 0 && oldKeys > 0) { - resultType = ImportResult.RESULT_OK_BOTHKEYS; - } else if (newKeys > 0) { - resultType = ImportResult.RESULT_OK_NEWKEYS; - } else { - resultType = ImportResult.RESULT_OK_UPDATED; - } - // No keys imported, overall failure - } else if (badKeys > 0) { - resultType = ImportResult.RESULT_FAIL_ERROR; - } else { + int resultType = 0; + // special return case: no new keys at all + if (badKeys == 0 && newKeys == 0 && oldKeys == 0) { resultType = ImportResult.RESULT_FAIL_NOTHING; + } else { + if (newKeys > 0) { + resultType |= ImportResult.RESULT_OK_NEWKEYS; + } + if (oldKeys > 0) { + resultType |= ImportResult.RESULT_OK_UPDATED; + } + if (badKeys > 0) { + resultType |= ImportResult.RESULT_WITH_ERRORS; + if (newKeys == 0 && oldKeys == 0) { + resultType |= ImportResult.RESULT_ERROR; + } + } + if (log.containsWarnings()) { + resultType |= ImportResult.RESULT_WITH_WARNINGS; + } } return new ImportResult(resultType, log, newKeys, oldKeys, badKeys); 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 cbaf72270..00db473b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -267,12 +268,15 @@ public class ProviderHelper { * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public OperationResultParcel savePublicKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + // start with ok result + int result = SaveKeyringResult.SAVED_PUBLIC; + long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IP, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); @@ -296,6 +300,7 @@ public class ProviderHelper { try { mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); + result |= SaveKeyringResult.UPDATED; } catch (UnsupportedOperationException e) { Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); @@ -315,7 +320,7 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_ENCODE_FAIL); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)); @@ -371,7 +376,7 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { creation.toString() }); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { @@ -436,7 +441,7 @@ public class ProviderHelper { if (!cert.verifySignature(masterKey, userId)) { // Bad self certification? That's kinda bad... log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // if we already have a cert.. @@ -526,17 +531,17 @@ public class ProviderHelper { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EX); Log.e(Constants.TAG, "OperationApplicationException during import", e); mIndent -= 1; - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } // Save the saved keyring (if any) @@ -544,12 +549,13 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); mIndent += 1; saveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; mIndent -= 1; } log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; - return new OperationResultParcel(0, mLog); + return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 85b7c456f..8a3b06f67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -18,12 +18,13 @@ import java.util.ArrayList; * */ public class OperationResultParcel implements Parcelable { - /** Holds the overall result, the number specifying varying degrees of success. - * Values smaller than 100 are considered an overall success. */ + /** Holds the overall result, the number specifying varying degrees of success. The first bit + * is 0 on overall success, 1 on overall failure. All other bits may be used for more specific + * conditions. */ final int mResult; public static final int RESULT_OK = 0; - public static final int RESULT_ERROR = 100; + public static final int RESULT_ERROR = 1; /// A list of log entries tied to the operation result. final OperationLog mLog; @@ -44,7 +45,7 @@ public class OperationResultParcel implements Parcelable { } public boolean isSuccessful() { - return mResult < 100; + return (mResult & 1) == 1; } public OperationLog getLog() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index bee6917be..342d07953 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -8,20 +8,31 @@ public abstract class OperationResults { public final int mNewKeys, mUpdatedKeys, mBadKeys; - // Operation ok, at least one new key (no warnings) - public static final int RESULT_OK_NEWKEYS = 1; - // Operation ok, at least one new and one updated key (no warnings) - public static final int RESULT_OK_BOTHKEYS = 2; - // Operation ok, no new keys but upated ones (no warnings) - public static final int RESULT_OK_UPDATED = 3; + // At least one new key + public static final int RESULT_OK_NEWKEYS = 2; + // At least one updated key + public static final int RESULT_OK_UPDATED = 4; + // At least one key failed (might still be an overall success) + public static final int RESULT_WITH_ERRORS = 8; + // There are warnings in the log + public static final int RESULT_WITH_WARNINGS = 16; - // Operation partially ok, but at least one key failed! - public static final int RESULT_PARTIAL_WITH_ERRORS = 50; + // No keys to import... + public static final int RESULT_FAIL_NOTHING = 32 +1; - // Operation failed, errors thrown and no new keys imported - public static final int RESULT_FAIL_ERROR = 100; - // Operation failed, no keys to import... - public static final int RESULT_FAIL_NOTHING = 101; + public boolean isOkBoth() { + return (mResult & (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED)) + == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); + } + public boolean isOkNew() { + return (mResult & RESULT_OK_NEWKEYS) > 0; + } + public boolean isOkUpdated() { + return (mResult & RESULT_OK_UPDATED) > 0; + } + public boolean isFailNothing() { + return (mResult & RESULT_FAIL_NOTHING) > 0; + } public ImportResult(Parcel source) { super(source); @@ -58,4 +69,24 @@ public abstract class OperationResults { } + public static class SaveKeyringResult extends OperationResultParcel { + + public SaveKeyringResult(int result, OperationLog log) { + super(result, log); + } + + // Some old key was updated + public static final int UPDATED = 2; + + // Public key was saved + public static final int SAVED_PUBLIC = 8; + // Secret key was saved (not exclusive with public!) + public static final int SAVED_SECRET = 16; + + public boolean updated() { + return (mResult & UPDATED) == UPDATED; + } + + } + } 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 61d7e7949..3ff4f9887 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -378,55 +378,54 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O final ImportResult result = returnData.getParcelable(KeychainIntentService.RESULT); - String str = ""; - boolean hasWarnings = result.getLog().containsWarnings(); - int duration = 0, color = hasWarnings ? Style.ORANGE : Style.GREEN; - String withWarnings = hasWarnings - ? getResources().getString(R.string.with_warnings) : ""; + int resultType = result.getResult(); - switch(result.getResult()) { - case ImportResult.RESULT_OK_NEWKEYS: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_added, result.mNewKeys, result.mNewKeys, withWarnings); - break; + String str; + int duration, color; - case ImportResult.RESULT_OK_UPDATED: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_updated, result.mNewKeys, result.mNewKeys, withWarnings); - break; + // Not an overall failure + if ((resultType & ImportResult.RESULT_ERROR) == 0) { + String withWarnings; - case ImportResult.RESULT_OK_BOTHKEYS: - if (!hasWarnings) { - duration = SuperToast.Duration.LONG; - } - str = getResources().getQuantityString( - R.plurals.keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); - str += getResources().getQuantityString( - R.plurals.keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); - break; - - case ImportResult.RESULT_PARTIAL_WITH_ERRORS: - str = "partial with errors"; + // Any warnings? + if ((resultType & ImportResult.RESULT_WITH_WARNINGS) > 0) { + duration = 0; color = Style.ORANGE; - break; + withWarnings = getResources().getString(R.string.import_with_warnings); + } else { + duration = SuperToast.Duration.LONG; + color = Style.GREEN; + withWarnings = ""; + } - case ImportResult.RESULT_FAIL_ERROR: - str = "fail error"; + // New and updated keys + if (result.isOkBoth()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); + str += getResources().getQuantityString( + R.plurals.import_keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else if (result.isOkNew()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); + } else if (result.isOkUpdated()) { + str = getResources().getQuantityString( + R.plurals.import_keys_updated, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else { + duration = 0; color = Style.RED; - break; - - case ImportResult.RESULT_FAIL_NOTHING: - str = getString(R.string.no_keys_added_or_updated); - color = Style.RED; - break; + str = "internal error"; + } + } else { + duration = 0; + color = Style.RED; + if (result.isFailNothing()) { + str = getString(R.string.import_error_nothing); + } else { + str = getString(R.string.import_error); + } } + SuperCardToast toast = new SuperCardToast(ImportKeysActivity.this, SuperToast.Type.BUTTON, Style.getStyle(color, SuperToast.Animations.POPUP)); toast.setText(str); @@ -434,9 +433,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setIndeterminate(duration == 0); toast.setSwipeToDismiss(true); toast.setButtonIcon(R.drawable.ic_action_view_as_list, - getResources().getString(R.string.view_log)); - toast.setButtonTextColor(R.color.emphasis_dark); - toast.setTextColor(R.color.emphasis_dark); + getResources().getString(R.string.import_view_log)); + toast.setButtonTextColor(getResources().getColor(R.color.black)); + toast.setTextColor(getResources().getColor(R.color.black)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", new SuperToast.OnClickListener() { @Override diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index e2dfa196b..3485846ad 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -206,23 +206,23 @@ Es wurde eine leere Identität hinzugefügt. Wirklich fortfahren? Soll der öffentliche Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! Private Schlüssel auch exportieren - + %d Schlüssel erfolgreich hinzugefügt %d Schlüssel erfolgreich hinzugefügt - + und %d Schlüssel erfolgreich aktualisiert. und %d Schlüssel erfolgreich aktualisiert. - + %d Schlüssel erfolgreich hinzugefügt. %d Schlüssel erfolgreich hinzugefügt. - + %d Schlüssel erfolgreich aktualisiert. %d Schlüssel erfolgreich aktualisiert. - Keine Schlüssel hinzugefügt oder aktualisiert. + Keine Schlüssel hinzugefügt oder aktualisiert. 1 Schlüssel erfolgreich exportiert. %d Schlüssel erfolgreich exportiert. Keine Schlüssel exportiert. diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index 45d3d565b..f1e4e347d 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -206,23 +206,23 @@ Ha añadido una identidad vacía, ¿está seguro de que quiere continuar? ¿De veras quiere borrar la clave pública \'%s\'?\n¡No puede deshacer esto! ¿Exportar también las claves secretas? - + %d clave añadida satisfactoriamente %d claves añadidas satisfactoriamente - + y actualizada %d clave. y actualizadas %d claves. - + %d clave añadida satisfactoriamente. %d claves añadidas satisfactoriamente. - + %d clave actualizada satisfactoriamente. %d claves actualizadas satisfactoriamente. - No se han añadido o actualizado claves. + No se han añadido o actualizado claves. Se ha exportado 1 clave satisfactoriamente. %d claves exportadas satisfactoriamente. No se han exportado claves. diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index f49127b6f..55a85fb9b 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -206,23 +206,23 @@ Vous avez ajouté une identité vide, êtes-vous certain de vouloir continuer ? Voulez-vous vraiment supprimer la clef publique %s ?\nCeci est irréversible ! Exporter aussi les clefs secrètes ? - + %d clef ajoutée avec succès %d clefs ajoutées avec succès - + et %d clef mise à jour. et %d clefs mises à jour. - + %d clef ajoutée avec succès. %d clefs ajoutées avec succès. - + %d clef mise à jour avec succès. %d clefs mises à jour avec succès. - Aucune clef ajoutée ou mise à jour. + Aucune clef ajoutée ou mise à jour. 1 clef exportée avec succès. %d clefs exportées avec succès. Aucune clef exportée. diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index eae4dd4af..300627fa7 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -206,23 +206,23 @@ Hai aggiunto una identità vuota, sei sicuro di voler continuare? Vuoi veramente eliminare la chiave pubblica \'%s\'?\nNon potrai annullare! Esportare anche le chiavi segrete? - + %d chiave aggiunta correttamente %d chiavi aggiunte correttamente - + e %d chiave aggiornata. e %d chiavi aggiornate. - + %d chiave aggiunta correttamente. %d chiavi aggiunte correttamente. - + %d chiave aggiornata correttamente. %d chiavi aggiornate correttamente. - Nessuna chiave aggiunta o aggiornata. + Nessuna chiave aggiunta o aggiornata. 1 chiave esportata correttamente. %d chiavi esportate correttamente. Nessuna chiave esportata. diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index c40e9dbdc..63fef2af2 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -203,19 +203,19 @@ あなたは空のユーザIDを追加しました、このまま続けますか? 公開鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! 秘密鍵もエクスポートしますか? - + %d の鍵を追加しました - + そして %d の鍵をアップロードしました。 - + %d の鍵を追加しました。 - + %d の鍵をアップロードしました。 - 鍵の追加もしくは更新はありませんでした。 + 鍵の追加もしくは更新はありませんでした。 1つの鍵をエクスポートしました。 %d の鍵をエクスポートしました。 鍵をエクスポートしていません。 diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml index d35d83517..f75d7a166 100644 --- a/OpenKeychain/src/main/res/values-nl/strings.xml +++ b/OpenKeychain/src/main/res/values-nl/strings.xml @@ -206,23 +206,23 @@ U heeft een lege identiteit toegevoegd, weet u zeker dat u wilt doorgaan? Wilt u echt de publieke sleutel \'%s\' verwijderen?\nDit kunt u niet ongedaan maken! Ook geheime sleutels exporteren? - + Succesvol %d sleutel toegevoegd Succesvol %d sleutels toegevoegd - + en %d sleutel bijgewerkt. en %d sleutels bijgewerkt. - + Succesvol %d sleutel toegevoegd. Succesvol %d sleutels toegevoegd. - + Succesvol %d sleutel bijgewerkt. Succesvol %d sleutels bijgewerkt. - Geen sleutels toegevoegd of bijgewerkt. + Geen sleutels toegevoegd of bijgewerkt. 1 sleutel succesvol geëxporteerd. Succesvol %d sleutels geëxporteerd. Geen sleutels geëxporteerd. diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml index d1b7de393..851e77c3a 100644 --- a/OpenKeychain/src/main/res/values-pl/strings.xml +++ b/OpenKeychain/src/main/res/values-pl/strings.xml @@ -191,27 +191,27 @@ Zostały dokonane zmiany w pęku kluczy, czy chcesz je zachować? Czy na pewno chcesz usunąć klucz publiczny \'%s\'?\nNie można cofnąć tej operacji! Czy wyeksportować również klucze prywatne? - + Pomyślnie dodano %d klucz Pomyślnie dodano %d kluczy Pomyślnie dodano %d kluczy - + i zaktualizowano %d klucz. i zaktualizowano %d kluczy. i zaktualizowano %d kluczy. - + Pomyślnie dodano %d klucz. Pomyślnie dodano %d kluczy. Pomyślnie dodano %d kluczy. - + Pomyślnie zaktualizowano %d klucz. Pomyślnie zaktualizowano %d kluczy. Pomyślnie zaktualizowano %d kluczy. - Nie dodano ani zaktualizowano żadnych kluczy. + Nie dodano ani zaktualizowano żadnych kluczy. Pomyślnie wyeksportowano 1 klucz. Pomyślnie wyeksportowano %d kluczy. Nie wyeksportowano żadnych kluczy. diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml index 0becea0bc..b108324d1 100644 --- a/OpenKeychain/src/main/res/values-ru/strings.xml +++ b/OpenKeychain/src/main/res/values-ru/strings.xml @@ -206,27 +206,27 @@ Вы добавили пустой идентификатор. Вы уверены, что хотите продолжить? Вы правда хотите удалить публичный ключ \'%s\'?\nЭто действие нельзя отменить! Экспортировать секретные ключи? - + Успешно добавлено %d ключ Успешно добавлено %d ключей Успешно добавлено %d ключей - + и обновлен %d ключ. и обновлено %d ключей. и обновлено %d ключей. - + Добавлен %d ключ Добавлено %d ключей Добавлено %d ключей - + Обновлен %d ключ. Обновлено %d ключей. Обновлено %d ключей. - Нет обновленных или добавленных ключей + Нет обновленных или добавленных ключей Успешный экспорт 1 ключа. Экспортировано %d ключей. Ключи не были экспортированы. diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml index 8b12cdebe..0fe44725b 100644 --- a/OpenKeychain/src/main/res/values-sl/strings.xml +++ b/OpenKeychain/src/main/res/values-sl/strings.xml @@ -212,31 +212,31 @@ Dodali ste prazno identiteto, ali res želite nadaljevati? Ali res želite izbrisati javni ključ \'%s\'?\nTega koraka ne boste mogli preklicati! Želite izvoziti tudi zasebne ključe? - + Uspešno dodan %d ključ Uspešno dodana %d ključa Uspešno dodani %d ključi Uspešno dodanih %d ključev - + in posodbljen %d. in posodobljena %d. in posodobljeni %d. in posodobljenih %d. - + Uspešno dodan %d ključ. Uspešno dodana %d ključa. Uspešno dodani %d ključi. Uspešno dodanih %d ključev. - + Uspešno posodobljen %d ključ. Uspešno posodobljena %d ključa. Uspešno posodobljeni %d ključi. Uspešno posodobljenih %d ključev. - Noben ključ ni bil dodan ali posodobljen. + Noben ključ ni bil dodan ali posodobljen. Uspešno izvožen 1 ključ. Uspešno izvoženih ključev: %d Noben ključ ni bil izvožen. diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml index b27b6ffd3..2951d13f8 100644 --- a/OpenKeychain/src/main/res/values-uk/strings.xml +++ b/OpenKeychain/src/main/res/values-uk/strings.xml @@ -209,27 +209,27 @@ Ви вже додали порожню сутність. Ви справді хочете продовжити? Ви справді хочете вилучити відкритий ключ \'%s\'?\nВи не зможете це відмінити! Також експортувати секретні ключі? - + Успішно додано %d ключ Успішно додано %d ключі Успішно додано %d ключів - + і оновлено %d ключ. і оновлено %d ключі. і оновлено %d ключів. - + Успішно додано %d ключ. Успішно додано %d ключі. Успішно додано %d ключів. - + Успішно оновлено %d ключ. Успішно оновлено %d ключі. Успішно оновлено %d ключів. - Жодного ключа не додано та не оновлено. + Жодного ключа не додано та не оновлено. Успішно експортовано 1 ключ. Успішно експортовано %d ключів. Жодного ключа не експортовано. diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index b60e281ee..8ef93551b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -221,25 +221,6 @@ Do you really want to delete the public key \'%s\'?\nYou can\'t undo this! Also export secret keys? - - Successfully added %1$d key - Successfully added %1$d keys - - - and updated %1$d key%2$s. - and updated %1$d keys%2$s. - - - Successfully added %1$d key%2$s. - Successfully added %1$d keys%2$s. - - - Successfully updated %1$d key%2$s. - Successfully updated %1$d keys%2$s. - - - No keys added or updated. - , with warnings Successfully exported 1 key. Successfully exported %d keys. No keys exported. @@ -408,6 +389,28 @@ Get key from clipboard Get key from Keybase.io + + + Successfully added %1$d key + Successfully added %1$d keys + + + and updated %1$d key%2$s. + and updated %1$d keys%2$s. + + + Successfully added %1$d key%2$s. + Successfully added %1$d keys%2$s. + + + Successfully updated %1$d key%2$s. + Successfully updated %1$d keys%2$s. + + View Log + Nothing to import. + Error importing keys! + , with warnings + Decrypt File with OpenKeychain Import Key with OpenKeychain @@ -579,6 +582,5 @@ cannot sign Encoding error No encryption subkey available! - View Log From 3895c10a5821a3ce11946c4cd88298c0b7410d5f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Jun 2014 20:06:28 +0200 Subject: [PATCH 16/60] import-log: work on log fragment ui --- .../keychain/provider/ProviderHelper.java | 2 +- .../service/OperationResultParcel.java | 6 +- .../keychain/ui/LogDisplayActivity.java | 4 + .../keychain/ui/LogDisplayFragment.java | 108 +++++++++++++++--- .../src/main/res/layout/log_display_item.xml | 10 +- OpenKeychain/src/main/res/values/strings.xml | 4 +- 6 files changed, 113 insertions(+), 21 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 00db473b3..f83ea24df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -553,8 +553,8 @@ public class ProviderHelper { mIndent -= 1; } - log(LogLevel.OK, LogType.MSG_IP_SUCCESS); mIndent -= 1; + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 8a3b06f67..4c288502c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -173,12 +173,12 @@ public class OperationResultParcel implements Parcelable { /** Enumeration of possible log levels. */ public static enum LogLevel { - START, // should occur once at the start of each independent operation - OK, // should occur once at the end of a successful operation - ERROR, // should occur once at the end of a failed operation DEBUG, INFO, WARN, + ERROR, // should occur once at the end of a failed operation + START, // should occur once at the start of each independent operation + OK, // should occur once at the end of a successful operation } @Override 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 28c120d01..a0d449195 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,7 +1,11 @@ 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 org.sufficientlysecure.keychain.R; 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 275c9ac18..980ee5e4a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -5,22 +5,40 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v4.view.MotionEventCompat; import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.Filterable; +import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogEntryParcel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; +import java.util.HashMap; -public class LogDisplayFragment extends ListFragment { +public class LogDisplayFragment extends ListFragment implements OnTouchListener { + HashMap mAdapters = new HashMap(); LogAdapter mAdapter; + LogLevel mLevel = LogLevel.DEBUG; + + OperationResultParcel mResult; + + GestureDetector mDetector; public static final String EXTRA_RESULT = "log"; @@ -34,15 +52,68 @@ public class LogDisplayFragment extends ListFragment { return; } - OperationResultParcel result = intent.getParcelableExtra(EXTRA_RESULT); - if (result == null) { + mResult = intent.getParcelableExtra(EXTRA_RESULT); + if (mResult == null) { getActivity().finish(); return; } - mAdapter = new LogAdapter(getActivity(), result.getLog()); + mAdapter = new LogAdapter(getActivity(), mResult.getLog(), LogLevel.DEBUG); + mAdapters.put(LogLevel.DEBUG, mAdapter); setListAdapter(mAdapter); + mDetector = new GestureDetector(getActivity(), new SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float vx, float vy) { + Log.d(Constants.TAG, "x: " + vx + ", y: " + vy); + if (vx < -2000) { + decreaseLogLevel(); + } else if (vx > 2000) { + increaseLogLevel(); + } + return true; + } + }); + + } + + public void decreaseLogLevel() { + switch (mLevel) { + case DEBUG: mLevel = LogLevel.INFO; break; + case INFO: mLevel = LogLevel.WARN; break; + } + refreshLevel(); + } + + public void increaseLogLevel() { + switch (mLevel) { + case INFO: mLevel = LogLevel.DEBUG; break; + case WARN: mLevel = LogLevel.INFO; break; + } + refreshLevel(); + } + + private void refreshLevel() { + /* TODO not sure if this is a good idea + if (!mAdapters.containsKey(mLevel)) { + mAdapters.put(mLevel, new LogAdapter(getActivity(), mResult.getLog(), mLevel)); + } + mAdapter = mAdapters.get(mLevel); + setListAdapter(mAdapter); + */ + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getListView().setDividerHeight(0); + getListView().setOnTouchListener(this); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + mDetector.onTouchEvent(event); + return false; } private class LogAdapter extends ArrayAdapter { @@ -50,12 +121,18 @@ public class LogDisplayFragment extends ListFragment { private LayoutInflater mInflater; private int dipFactor; - public LogAdapter(Context context, ArrayList log) { - super(context, R.layout.log_display_item, log); + public LogAdapter(Context context, ArrayList log, LogLevel level) { + super(context, R.layout.log_display_item); mInflater = LayoutInflater.from(getContext()); dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 10, getResources().getDisplayMetrics()); - + (float) 8, getResources().getDisplayMetrics()); + // we can't use addAll for a LogLevel.DEBUG shortcut here, unfortunately :( + for (LogEntryParcel e : log) { + if (e.mLevel.ordinal() >= level.ordinal()) { + add(e); + } + } + notifyDataSetChanged(); } @Override @@ -69,15 +146,18 @@ public class LogDisplayFragment extends ListFragment { } else { text = (TextView) convertView.getTag(); } + ImageView img = (ImageView) convertView.findViewById(R.id.log_img); - text.setPadding(entry.mIndent*dipFactor, 0, 0, 0); text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + text.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); + convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); switch (entry.mLevel) { - case OK: text.setTextColor(Color.GREEN); break; - case DEBUG: text.setTextColor(Color.GRAY); break; - case INFO: text.setTextColor(Color.BLACK); break; - case WARN: text.setTextColor(Color.YELLOW); break; - case ERROR: text.setTextColor(Color.RED); break; + case DEBUG: img.setBackgroundColor(Color.GRAY); break; + case INFO: img.setBackgroundColor(Color.BLACK); break; + case WARN: img.setBackgroundColor(Color.YELLOW); break; + case ERROR: img.setBackgroundColor(Color.RED); break; + case START: img.setBackgroundColor(Color.GREEN); break; + case OK: img.setBackgroundColor(Color.GREEN); break; } return convertView; diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_item.xml index f0e0e4ecb..35489afed 100644 --- a/OpenKeychain/src/main/res/layout/log_display_item.xml +++ b/OpenKeychain/src/main/res/layout/log_display_item.xml @@ -4,11 +4,19 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> + + + android:layout_marginBottom="4dp" + android:layout_marginLeft="8dp" /> \ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 8ef93551b..45aa88433 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -530,7 +530,7 @@ Subkey flags: sign Subkey flags: none Subkey creation date lies in the future! (%s) - Successfully inserted public keyring + Successfully imported public keyring Re-inserting secret key Retrieving trusted keys Using %s trusted keys @@ -556,7 +556,7 @@ Subkey %s unavailable in public key Marked %s as available Marked %s as stripped - Successfully inserted secret keyring + Successfully imported secret keyring Canonicalizing keyring %s From 8d75d3e00e02527c4bc717cbcd937b1457eb4bda Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 11 Jun 2014 00:37:23 +0200 Subject: [PATCH 17/60] import-log: use holder pattern in log fragment --- .../keychain/ui/LogDisplayFragment.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) 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 980ee5e4a..496e98c18 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -5,8 +5,6 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.MotionEventCompat; import android.util.TypedValue; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -16,7 +14,6 @@ import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; @@ -135,29 +132,40 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener notifyDataSetChanged(); } + private class ItemHolder { + final TextView mText; + final ImageView mImg; + public ItemHolder(TextView text, ImageView image) { + mText = text; + mImg = image; + } + } + @Override public View getView(int position, View convertView, ViewGroup parent) { LogEntryParcel entry = getItem(position); - TextView text; + ItemHolder ih; if (convertView == null) { convertView = mInflater.inflate(R.layout.log_display_item, parent, false); - text = (TextView) convertView.findViewById(R.id.log_text); - convertView.setTag(text); + ih = new ItemHolder( + (TextView) convertView.findViewById(R.id.log_text), + (ImageView) convertView.findViewById(R.id.log_img) + ); + convertView.setTag(ih); } else { - text = (TextView) convertView.getTag(); + ih = (ItemHolder) convertView.getTag(); } - ImageView img = (ImageView) convertView.findViewById(R.id.log_img); - text.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); - text.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); + ih.mText.setText(getResources().getString(entry.mType.getMsgId(), (Object[]) entry.mParameters)); + ih.mText.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK); convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); switch (entry.mLevel) { - case DEBUG: img.setBackgroundColor(Color.GRAY); break; - case INFO: img.setBackgroundColor(Color.BLACK); break; - case WARN: img.setBackgroundColor(Color.YELLOW); break; - case ERROR: img.setBackgroundColor(Color.RED); break; - case START: img.setBackgroundColor(Color.GREEN); break; - case OK: img.setBackgroundColor(Color.GREEN); break; + case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break; + case INFO: ih.mImg.setBackgroundColor(Color.BLACK); break; + case WARN: ih.mImg.setBackgroundColor(Color.YELLOW); break; + case ERROR: ih.mImg.setBackgroundColor(Color.RED); break; + case START: ih.mImg.setBackgroundColor(Color.GREEN); break; + case OK: ih.mImg.setBackgroundColor(Color.GREEN); break; } return convertView; From 47368f1d24cb3b4112133a6dddd2793e6787dfdd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 01:37:49 +0200 Subject: [PATCH 18/60] import-log: better way to check self trust, and fix log level filtering --- .../keychain/provider/ProviderHelper.java | 18 +++++------------- .../service/OperationResultParcel.java | 4 +--- OpenKeychain/src/main/res/values/strings.xml | 1 - 3 files changed, 6 insertions(+), 17 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 f83ea24df..8d95da8b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -399,21 +399,12 @@ public class ProviderHelper { } mIndent -= 1; - log(LogLevel.DEBUG, LogType.MSG_IP_TRUST_RETRIEVE); // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - // special case: available secret keys verify themselves! - if (secretRing != null) { - trustedKeys.put(secretRing.getMasterKeyId(), secretRing.getPublicKey()); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING_SEC, new String[]{ - Integer.toString(trustedKeys.size()) - }); - } else { - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { - Integer.toString(trustedKeys.size()) - }); - } + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { + Integer.toString(trustedKeys.size()) + }); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. @@ -513,7 +504,8 @@ public class ProviderHelper { // no self cert is bad, but allowed by the rfc... if (item.selfCert != null) { operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, Certs.VERIFIED_SELF)); + masterKeyId, userIdRank, item.selfCert, + secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways if (item.isRevoked) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 4c288502c..473cf8244 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -133,7 +133,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), - MSG_IP_TRUST_USING_SEC (R.string.msg_ip_trust_using_sec), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), @@ -210,9 +209,8 @@ public class OperationResultParcel implements Parcelable { } public boolean containsWarnings() { - int warn = LogLevel.WARN.ordinal(); for(LogEntryParcel entry : new IterableIterator(iterator())) { - if (entry.mLevel.ordinal() >= warn) { + if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) { return true; } } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 45aa88433..367fdb599 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -534,7 +534,6 @@ Re-inserting secret key Retrieving trusted keys Using %s trusted keys - Secret key available, self certificates are trusted Encountered bad certificate! Error processing certificate! Found good certificate from %s From dea98a4a7e3143acfc01ce1567a9d17c25025b4d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 01:52:41 +0200 Subject: [PATCH 19/60] import-log: properly distinguish return states --- .../keychain/pgp/PgpImportExport.java | 17 +++++++---------- .../keychain/provider/ProviderHelper.java | 18 ++++++++++-------- .../service/OperationResultParcel.java | 4 ++-- .../keychain/service/OperationResults.java | 6 +++--- .../keychain/ui/ImportKeysActivity.java | 6 +++--- 5 files changed, 25 insertions(+), 26 deletions(-) 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 bafb086d0..bb45cc7db 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -58,10 +58,6 @@ public class PgpImportExport { private ProviderHelper mProviderHelper; - public static final int RETURN_OK = 0; - public static final int RETURN_BAD = -2; - public static final int RETURN_UPDATED = 1; - public PgpImportExport(Context context, Progressable progressable) { super(); this.mContext = context; @@ -118,10 +114,9 @@ public class PgpImportExport { if (aos != null) { aos.close(); } - if (bos != null) { - bos.close(); - } + bos.close(); } catch (IOException e) { + // this is just a finally thing, no matter if it doesn't work out. } } } @@ -153,10 +148,12 @@ public class PgpImportExport { } SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); - if (result.updated()) { - newKeys += 1; - } else { + if (!result.success()) { + badKeys += 1; + } else if (result.updated()) { oldKeys += 1; + } else { + newKeys += 1; } } catch (PgpGeneralException e) { 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 8d95da8b0..40af285e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -297,12 +297,12 @@ public class ProviderHelper { } // delete old version of this keyRing, which also deletes all keys and userIds on cascade - try { - mContentResolver.delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + int deleted = mContentResolver.delete( + KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + if (deleted > 0) { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); result |= SaveKeyringResult.UPDATED; - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } else { log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); } @@ -577,14 +577,16 @@ public class ProviderHelper { * is already in the database! */ public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { - if (!keyRing.isSecret()) { - log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new OperationResultParcel(1, mLog); - } long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); + mIndent += 1; + + if (!keyRing.isSecret()) { + log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); + return new OperationResultParcel(1, mLog); + } // save secret keyring try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 473cf8244..67ff8318b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -44,8 +44,8 @@ public class OperationResultParcel implements Parcelable { return mResult; } - public boolean isSuccessful() { - return (mResult & 1) == 1; + public boolean success() { + return (mResult & 1) == 0; } public OperationLog getLog() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java index 342d07953..6c44b01f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -25,13 +25,13 @@ public abstract class OperationResults { == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); } public boolean isOkNew() { - return (mResult & RESULT_OK_NEWKEYS) > 0; + return (mResult & RESULT_OK_NEWKEYS) == RESULT_OK_NEWKEYS; } public boolean isOkUpdated() { - return (mResult & RESULT_OK_UPDATED) > 0; + return (mResult & RESULT_OK_UPDATED) == RESULT_OK_UPDATED; } public boolean isFailNothing() { - return (mResult & RESULT_FAIL_NOTHING) > 0; + return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING; } public ImportResult(Parcel source) { 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 3ff4f9887..7fa18406c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -404,12 +404,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O R.plurals.import_keys_added_and_updated_1, result.mNewKeys, result.mNewKeys); str += getResources().getQuantityString( R.plurals.import_keys_added_and_updated_2, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); - } else if (result.isOkNew()) { - str = getResources().getQuantityString( - R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); } else if (result.isOkUpdated()) { str = getResources().getQuantityString( R.plurals.import_keys_updated, result.mUpdatedKeys, result.mUpdatedKeys, withWarnings); + } else if (result.isOkNew()) { + str = getResources().getQuantityString( + R.plurals.import_keys_added, result.mNewKeys, result.mNewKeys, withWarnings); } else { duration = 0; color = Style.RED; From 466eddb0051d8b19576b60be7e3769f13b308a57 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 15:36:35 +0200 Subject: [PATCH 20/60] canonicalize: implementation, first draft --- .../keychain/pgp/UncachedKeyRing.java | 294 ++++++++++++++++-- .../keychain/pgp/WrappedSignature.java | 26 +- .../keychain/provider/ProviderHelper.java | 2 +- .../service/OperationResultParcel.java | 23 +- OpenKeychain/src/main/res/values/strings.xml | 21 +- 5 files changed, 337 insertions(+), 29 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 7853d0b00..215c17590 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -5,16 +5,17 @@ import org.spongycastle.bcpg.S2K; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -179,51 +180,300 @@ public class UncachedKeyRing { * * More specifically: * - Remove all non-verifying self-certificates - * - Remove all expired self-certificates * - Remove all certificates flagged as "local" * - Remove all certificates which are superseded by a newer one on the same target * - * After this cleaning, a number of checks are done: + * After this cleaning, a number of checks are done: TODO implement * - See if each subkey retains a valid self certificate * - See if each user id retains a valid self certificate * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * - * If any of these checks fail, the operation as a whole fails and the keyring is declared - * unusable. (TODO: allow forcing of import?) - * - * TODO implement - * * @return A canonicalized key * */ - public UncachedKeyRing canonicalize(OperationLog log) { - if(isSecret()) { + public UncachedKeyRing canonicalize(OperationLog log, int indent) { + if (isSecret()) { throw new RuntimeException("Tried to canonicalize non-secret keyring. " + "This is a programming error and should never happen!"); } - // dummy log.add(LogLevel.START, LogType.MSG_KC, - new String[] { PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()) }, 0); + new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); + indent += 1; - /* - // Remove all non-verifying self certificates - for (PGPPublicKey key : new IterableIterator(mRing.getPublicKeys())) { + int removedCerts = 0; - for (PGPSignature sig : new IterableIterator( - key.getSignaturesOfType(isMasterKey() ? PGPSignature.KEY_REVOCATION - : PGPSignature.SUBKEY_REVOCATION))) { - return true; + PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; + PGPPublicKey masterKey = mRing.getPublicKey(); + final long masterKeyId = masterKey.getKeyID(); + + { + log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER, + new String[]{PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())}, indent); + indent += 1; + + PGPPublicKey modified = masterKey; + PGPSignature revocation = null; + for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { + int type = zert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later + if (type == PGPSignature.NO_CERTIFICATION + || type == PGPSignature.DEFAULT_CERTIFICATION + || type == PGPSignature.CASUAL_CERTIFICATION + || type == PGPSignature.POSITIVE_CERTIFICATION + || type == PGPSignature.CERTIFICATION_REVOCATION) { + continue; + } + WrappedSignature cert = new WrappedSignature(zert); + + if (type != PGPSignature.KEY_REVOCATION) { + // Unknown type, just remove + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + "0x" + Integer.toString(type, 16) + }, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey)) { + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_ERR, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + // first revocation? fine then. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(zert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, revocation); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); + revocation = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); + } } - }*/ + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + PGPSignature selfCert = null; + revocation = null; - log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, 0); + // look through signatures for this specific key + for (PGPSignature zert : new IterableIterator( + masterKey.getSignaturesForID(userId))) { + WrappedSignature cert = new WrappedSignature(zert); + long certId = cert.getKeyId(); - return this; + // If this is a foreign signature, never mind + if (certId != masterKeyId) { + continue; + } + // Otherwise, first make sure it checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, userId)) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD, + new String[] { userId }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR, + new String[] { userId }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + continue; + } + switch (zert.getSignatureType()) { + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: + if (selfCert == null) { + selfCert = zert; + } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, selfCert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + new String[] { userId }, indent); + selfCert = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + new String[] { userId }, indent); + } + // If there is a revocation certificate, and it's older than this, drop it + if (revocation != null + && revocation.getCreationTime().before(selfCert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, revocation); + revocation = null; + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + new String[] { userId }, indent); + } + break; + + case PGPSignature.CERTIFICATION_REVOCATION: + // If this is older than the (latest) self cert, drop it + if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + new String[] { userId }, indent); + continue; + } + // first revocation? remember it. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(cert.getCreationTime())) { + modified = PGPPublicKey.removeCertification(modified, userId, revocation); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + revocation = zert; + } else { + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + } + break; + + default: + log.add(LogLevel.WARN, LogType.MSG_KC_UID_UNKNOWN_CERT, + new String[] { + "0x" + Integer.toString(zert.getSignatureType(), 16), + userId + }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + } + + } + } + + // Replace modified key in the keyring + ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + + log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER_SUCCESS, null, indent); + indent -= 1; + + } + + // Process all keys + // NOTE we rely here on the special property that this iterator is independent from the keyring! + for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { + // Only care about the master key here! + if (key.isMasterKey()) { + continue; + } + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY, + new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + indent += 1; + // A subkey needs exactly one subkey binding certificate, and optionally one revocation + // certificate. + PGPPublicKey modified = key; + PGPSignature cert = null, revocation = null; + for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + // remove from keyring (for now) + modified = PGPPublicKey.removeCertification(modified, zig); + WrappedSignature sig = new WrappedSignature(zig); + int type = sig.getSignatureType(); + + // filter out bad key types... + if (sig.getKeyId() != masterKey.getKeyID()) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_KEYID, null, indent); + continue; + } + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + "0x" + Integer.toString(type, 16) + }, indent); + continue; + } + + // make sure the certificate checks out + try { + sig.init(masterKey); + if (!sig.verifySignature(masterKey, key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD, null, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_ERR, null, indent); + continue; + } + + if (type == PGPSignature.SUBKEY_BINDING) { + // TODO verify primary key binding signature for signing keys! + // if we already have a cert, and this one is not newer: skip it + if (cert != null && cert.getCreationTime().before(sig.getCreationTime())) { + continue; + } + cert = zig; + // if this is newer than a possibly existing revocation, drop that one + if (revocation != null && cert.getCreationTime().after(revocation.getCreationTime())) { + revocation = null; + } + // it must be a revocation, then (we made sure above) + } else { + // if there is no binding (yet), or the revocation is newer than the binding: keep it + if (cert == null || cert.getCreationTime().before(sig.getCreationTime())) { + revocation = zig; + } + } + } + + // it is not properly bound? error! + if (cert == null) { + ring = PGPPublicKeyRing.removePublicKey(ring, modified); + + log.add(LogLevel.ERROR, LogType.MSG_KC_SUBKEY_NO_CERT, + new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + indent -= 1; + continue; + } + + // re-add certification + modified = PGPPublicKey.addCertification(modified, cert); + // add revocation, if any + if (revocation != null) { + modified = PGPPublicKey.addCertification(modified, revocation); + } + // replace pubkey in keyring + ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY_SUCCESS, null, indent); + indent -= 1; + } + + if (removedCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REMOVED, + new String[] { Integer.toString(removedCerts) }, indent); + } else { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); + } + + return new UncachedKeyRing(ring); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 1b7a5e8ba..093b6c96f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -35,7 +35,7 @@ public class WrappedSignature { final PGPSignature mSig; - protected WrappedSignature(PGPSignature sig) { + WrappedSignature(PGPSignature sig) { mSig = sig; } @@ -88,7 +88,7 @@ public class WrappedSignature { init(key.getPublicKey()); } - protected void init(PGPPublicKey key) throws PgpGeneralException { + void init(PGPPublicKey key) throws PgpGeneralException { try { JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() @@ -125,7 +125,27 @@ public class WrappedSignature { } } - protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { + boolean verifySignature(PGPPublicKey key) throws PgpGeneralException { + try { + return mSig.verifyCertification(key); + } catch (SignatureException e) { + throw new PgpGeneralException("Sign!", e); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + + boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException { + try { + return mSig.verifyCertification(masterKey, subKey); + } catch (SignatureException e) { + throw new PgpGeneralException("Sign!", e); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + + boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { try { return mSig.verifyCertification(uid, key); } catch (SignatureException e) { 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 40af285e1..6c004f19a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -283,7 +283,7 @@ public class ProviderHelper { mIndent += 1; // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog); + keyRing = keyRing.canonicalize(mLog, mIndent); UncachedPublicKey masterKey = keyRing.getPublicKey(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 67ff8318b..48f40026e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -157,8 +157,27 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization - MSG_KC(R.string.msg_kc), - MSG_KC_SUCCESS(R.string.msg_kc_success), + MSG_KC (R.string.msg_kc), + MSG_KC_CERT_BAD_ERR (R.string.msg_kc_cert_bad_err), + MSG_KC_CERT_BAD_KEYID (R.string.msg_kc_cert_bad_keyid), + MSG_KC_CERT_BAD (R.string.msg_kc_cert_bad), + MSG_KC_CERT_BAD_TYPE (R.string.msg_kc_cert_bad_type), + MSG_KC_MASTER (R.string.msg_kc_master), + MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), + MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), + MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), + MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), + MSG_KC_SUBKEY_NO_CERT (R.string.msg_kc_subkey_no_cert), + MSG_KC_SUBKEY (R.string.msg_kc_subkey), + MSG_KC_SUBKEY_SUCCESS (R.string.msg_kc_subkey_success), + MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), + MSG_KC_SUCCESS (R.string.msg_kc_success), + MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), + MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), + MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), + MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + MSG_KC_UID_UNKNOWN_CERT (R.string.msg_kc_uid_unknown_cert), ; private final int mMsgId; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 367fdb599..27568132b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -559,7 +559,26 @@ Canonicalizing keyring %s - Successfully canonicalized keyring + Certificate verification failed! + Certificate verification failed with an error! + Certificate issuer id mismatch + Unknown certificate type: %s + Processing master key + OK + Removing bad keyring revocation key + Removing bad keyring revocation key + Removing redundant keyring revocation key + No valid certificate found for %s, removing from ring! + Processing subkey %s + OK + Keyring canonicalization successful + Keyring canonicalization successful, removed %s certificates + Removing bad self certificate for user id %s + Removing bad self certificate for user id "%s" + Removing outdated self certificate for user id "%s" + Removing redundant revocation certificate for user id "%s" + Removing outdated revocation certificate for user id "%s" + Removing unknown certificate of type 0x%1$s from user id "%2$s" Certifier From dae503284f47eb7e5eed71140f9fceaa2ff420c2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 17:38:48 +0200 Subject: [PATCH 21/60] canonicalize: more stuff --- .../keychain/keyimport/ParcelableKeyRing.java | 2 +- .../keychain/pgp/UncachedKeyRing.java | 83 +++++++++++-------- .../service/OperationResultParcel.java | 17 ++-- .../keychain/ui/ImportKeysActivity.java | 2 + OpenKeychain/src/main/res/values/strings.xml | 17 ++-- 5 files changed, 72 insertions(+), 49 deletions(-) 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 5da6c4cd3..fdf561aaf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -3,7 +3,7 @@ package org.sufficientlysecure.keychain.keyimport; import android.os.Parcel; import android.os.Parcelable; -/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists +/** This is a trivial wrapper around keyring bytes which implements Parcelable. It exists * for the sole purpose of keeping spongycastle and android imports in separate packages. */ public class ParcelableKeyRing implements Parcelable { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 215c17590..a8e4820cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -15,7 +15,6 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; -import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -24,7 +23,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -229,7 +227,7 @@ public class UncachedKeyRing { if (type != PGPSignature.KEY_REVOCATION) { // Unknown type, just remove - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); @@ -380,81 +378,98 @@ public class UncachedKeyRing { } // Process all keys - // NOTE we rely here on the special property that this iterator is independent from the keyring! for (PGPPublicKey key : new IterableIterator(ring.getPublicKeys())) { - // Only care about the master key here! + // Don't care about the master key here, that one gets special treatment above if (key.isMasterKey()) { continue; } - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY, + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB, new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); indent += 1; // A subkey needs exactly one subkey binding certificate, and optionally one revocation // certificate. PGPPublicKey modified = key; - PGPSignature cert = null, revocation = null; + PGPSignature selfCert = null, revocation = null; for (PGPSignature zig : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zig); - WrappedSignature sig = new WrappedSignature(zig); - int type = sig.getSignatureType(); + WrappedSignature cert = new WrappedSignature(zig); + int type = cert.getSignatureType(); // filter out bad key types... - if (sig.getKeyId() != masterKey.getKeyID()) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_KEYID, null, indent); + if (cert.getKeyId() != masterKey.getKeyID()) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); continue; } if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); continue; } - // make sure the certificate checks out - try { - sig.init(masterKey); - if (!sig.verifySignature(masterKey, key)) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD, null, indent); - continue; - } - } catch (PgpGeneralException e) { - log.add(LogLevel.WARN, LogType.MSG_KC_CERT_BAD_ERR, null, indent); - continue; - } - if (type == PGPSignature.SUBKEY_BINDING) { // TODO verify primary key binding signature for signing keys! - // if we already have a cert, and this one is not newer: skip it - if (cert != null && cert.getCreationTime().before(sig.getCreationTime())) { + + // make sure the certificate checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); + log.add(LogLevel.WARN, LogType.MSG_KC_SUB, new String[] { + cert.getCreationTime().toString() + }, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_ERR, null, indent); continue; } - cert = zig; + + // if we already have a cert, and this one is not newer: skip it + if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { + continue; + } + selfCert = zig; // if this is newer than a possibly existing revocation, drop that one - if (revocation != null && cert.getCreationTime().after(revocation.getCreationTime())) { + if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { revocation = null; } - // it must be a revocation, then (we made sure above) + + // it must be a revocation, then (we made sure above) } else { + + // make sure the certificate checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(key)) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, null, indent); + continue; + } + } catch (PgpGeneralException e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD_ERR, null, indent); + continue; + } + // if there is no binding (yet), or the revocation is newer than the binding: keep it - if (cert == null || cert.getCreationTime().before(sig.getCreationTime())) { + if (selfCert == null || selfCert.getCreationTime().before(cert.getCreationTime())) { revocation = zig; } } } // it is not properly bound? error! - if (cert == null) { + if (selfCert == null) { ring = PGPPublicKeyRing.removePublicKey(ring, modified); - log.add(LogLevel.ERROR, LogType.MSG_KC_SUBKEY_NO_CERT, + log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); indent -= 1; continue; } // re-add certification - modified = PGPPublicKey.addCertification(modified, cert); + modified = PGPPublicKey.addCertification(modified, selfCert); // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); @@ -462,7 +477,7 @@ public class UncachedKeyRing { // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUBKEY_SUCCESS, null, indent); + log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_SUCCESS, null, indent); indent -= 1; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 48f40026e..5c223e870 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -158,18 +158,21 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), - MSG_KC_CERT_BAD_ERR (R.string.msg_kc_cert_bad_err), - MSG_KC_CERT_BAD_KEYID (R.string.msg_kc_cert_bad_keyid), - MSG_KC_CERT_BAD (R.string.msg_kc_cert_bad), - MSG_KC_CERT_BAD_TYPE (R.string.msg_kc_cert_bad_type), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), - MSG_KC_SUBKEY_NO_CERT (R.string.msg_kc_subkey_no_cert), - MSG_KC_SUBKEY (R.string.msg_kc_subkey), - MSG_KC_SUBKEY_SUCCESS (R.string.msg_kc_subkey_success), + MSG_KC_SUB (R.string.msg_kc_sub), + MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), + MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), + MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), + MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), + MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), + MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), + MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), + MSG_KC_SUB_REVOKE_DUP (R.string.msg_kc_sub_revoke_dup), + MSG_KC_SUB_SUCCESS (R.string.msg_kc_sub_success), MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), 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 7fa18406c..f389726ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -456,10 +456,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O } */ + /* if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData); finish(); } + */ } } }; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 27568132b..bcf66d58f 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -559,18 +559,21 @@ Canonicalizing keyring %s - Certificate verification failed! - Certificate verification failed with an error! - Certificate issuer id mismatch - Unknown certificate type: %s Processing master key OK Removing bad keyring revocation key Removing bad keyring revocation key Removing redundant keyring revocation key - No valid certificate found for %s, removing from ring! - Processing subkey %s - OK + Processing subkey %s + Subkey binding certificate verification failed! + Subkey binding certificate verification failed with an error! + Subkey binding issuer id mismatch + Unknown subkey certificate type: %s + No valid certificate found for %s, removing from ring! + Removing bad subkey revocation key + Removing bad subkey revocation key + Removing redundant keyring revocation key + OK Keyring canonicalization successful Keyring canonicalization successful, removed %s certificates Removing bad self certificate for user id %s From e4a7d4f6e5dc6eb0acac2aa4945852ae2f1d8bb8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:10:48 +0200 Subject: [PATCH 22/60] import-log: minor improvements --- .../keychain/pgp/PgpImportExport.java | 11 +- .../keychain/pgp/UncachedKeyRing.java | 3 - .../keychain/provider/ProviderHelper.java | 115 ++++++++++-------- .../service/OperationResultParcel.java | 2 + OpenKeychain/src/main/res/values/strings.xml | 4 +- 5 files changed, 78 insertions(+), 57 deletions(-) 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 bb45cc7db..e1967429a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -127,9 +127,7 @@ public class PgpImportExport { updateProgress(R.string.progress_importing, 0, 100); - int newKeys = 0; - int oldKeys = 0; - int badKeys = 0; + int newKeys = 0, oldKeys = 0, badKeys = 0; int position = 0; for (ParcelableKeyRing entry : entries) { @@ -147,7 +145,12 @@ public class PgpImportExport { } } - SaveKeyringResult result = mProviderHelper.savePublicKeyRing(key); + SaveKeyringResult result; + if (key.isSecret()) { + result = mProviderHelper.saveSecretKeyRing(key); + } else { + result = mProviderHelper.savePublicKeyRing(key); + } if (!result.success()) { badKeys += 1; } else if (result.updated()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index a8e4820cf..1edc529c6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -416,9 +416,6 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); - log.add(LogLevel.WARN, LogType.MSG_KC_SUB, new String[] { - cert.getCreationTime().toString() - }, indent); continue; } } catch (PgpGeneralException e) { 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 6c004f19a..519d5ee0f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -296,20 +296,14 @@ public class ProviderHelper { secretRing = null; } - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - int deleted = mContentResolver.delete( - KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); - if (deleted > 0) { - log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); - result |= SaveKeyringResult.UPDATED; - } else { - log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); - } - + ArrayList operations; try { + log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE); + mIndent += 1; + // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); + operations = new ArrayList(); log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); { // insert keyring @@ -354,26 +348,26 @@ public class ProviderHelper { values.put(Keys.IS_REVOKED, key.isRevoked()); if (c) { if (e) { - log(LogLevel.DEBUG,s ? LogType.MSG_IP_SUBKEY_FLAGS_CES - : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); } else { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS - : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); } } else { if (e) { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES - : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); } else { log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS - : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); } } Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); if (creation.after(new Date())) { - log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[] { + log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[]{ creation.toString() }); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); @@ -382,11 +376,11 @@ public class ProviderHelper { if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); if (key.isExpired()) { - log(LogLevel.INFO, LogType.MSG_IP_SUBKEY_EXPIRED, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRED, new String[]{ expiryDate.toString() }); } else { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[]{ expiryDate.toString() }); } @@ -402,8 +396,8 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[] { - Integer.toString(trustedKeys.size()) + log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ + Integer.toString(trustedKeys.size()) }); // classify and order user ids. primary are moved to the front, revoked to the back, @@ -419,7 +413,7 @@ public class ProviderHelper { int unknownCerts = 0; - log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[] { userId }); + log(LogLevel.INFO, LogType.MSG_IP_UID_PROCESSING, new String[]{ userId }); mIndent += 1; // look through signatures for this specific key for (WrappedSignature cert : new IterableIterator( @@ -453,7 +447,7 @@ public class ProviderHelper { item.isPrimary = cert.isPrimaryUserId(); if (cert.isRevocation()) { item.isRevoked = true; - log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); + log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); } else { item.isRevoked = false; } @@ -467,7 +461,8 @@ public class ProviderHelper { if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD, new String[] { - PgpKeyHelper.convertKeyIdToHex(trustedKey.getKeyId()) + PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId()), + trustedKey.getPrimaryUserId() }); } else { log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_BAD); @@ -485,7 +480,7 @@ public class ProviderHelper { mIndent -= 1; if (unknownCerts > 0) { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[] { + log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[]{ Integer.toString(unknownCerts) }); } @@ -517,13 +512,43 @@ public class ProviderHelper { } } - log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); - mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE_SUCCESS); + 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 new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + + try { + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + int deleted = mContentResolver.delete( + KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null); + if (deleted > 0) { + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_OK); + result |= SaveKeyringResult.UPDATED; + } else { + log(LogLevel.DEBUG, LogType.MSG_IP_DELETE_OLD_FAIL); + } + + log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); + mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); + + // Save the saved keyring (if any) + if (secretRing != null) { + log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); + mIndent += 1; + saveSecretKeyRing(secretRing); + result |= SaveKeyringResult.SAVED_SECRET; + mIndent -= 1; + } + + mIndent -= 1; + log(LogLevel.OK, LogType.MSG_IP_SUCCESS); + return new SaveKeyringResult(result, mLog); + } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); @@ -536,19 +561,6 @@ public class ProviderHelper { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } - // Save the saved keyring (if any) - if (secretRing != null) { - log(LogLevel.DEBUG, LogType.MSG_IP_REINSERT_SECRET); - mIndent += 1; - saveSecretKeyRing(secretRing); - result |= SaveKeyringResult.SAVED_SECRET; - mIndent -= 1; - } - - mIndent -= 1; - log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - return new SaveKeyringResult(result, mLog); - } private static class UserIdItem implements Comparable { @@ -575,19 +587,24 @@ public class ProviderHelper { /** * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * is already in the database! + * + * TODO allow adding secret keys where no public key exists (ie, consolidate keys) */ - public OperationResultParcel saveSecretKeyRing(UncachedKeyRing keyRing) { - - long masterKeyId = keyRing.getMasterKeyId(); - log(LogLevel.START, LogType.MSG_IS, - new String[]{PgpKeyHelper.convertKeyIdToHex(masterKeyId)}); - mIndent += 1; + public SaveKeyringResult saveSecretKeyRing(UncachedKeyRing keyRing) { if (!keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + long masterKeyId = keyRing.getMasterKeyId(); + log(LogLevel.START, LogType.MSG_IS, + new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); + mIndent += 1; + + // IF this is successful, it's a secret key + int result = SaveKeyringResult.SAVED_SECRET; + // save secret keyring try { ContentValues values = new ContentValues(); @@ -599,7 +616,7 @@ public class ProviderHelper { } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); - return new OperationResultParcel(1, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } { @@ -643,7 +660,7 @@ public class ProviderHelper { } log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return new OperationResultParcel(0, mLog); + return new SaveKeyringResult(result, mLog); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 5c223e870..9790d216d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -115,6 +115,8 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_PREPARE (R.string.msg_ip_prepare), + MSG_IP_PREPARE_SUCCESS(R.string.msg_ip_prepare_success), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), MSG_IP_SUBKEY (R.string.msg_ip_subkey), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index bcf66d58f..642106c1b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -516,6 +516,8 @@ Importing public keyring %s Inserting keyring data Inserting subkeys + Preparing database operations + OK Preserving available secret key Processing subkey %s Subkey expired on %s @@ -536,7 +538,7 @@ Using %s trusted keys Encountered bad certificate! Error processing certificate! - Found good certificate from %s + Found good certificate from %2$s (%2$s) Ignored %s certificates from unknown pubkeys Classifying user ids Inserting user ids From 59701250ba442b12d98f7b328a4cc61db1fe9158 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:11:31 +0200 Subject: [PATCH 23/60] import: use wrapped keyring instead of uncached keyring for trust --- .../keychain/provider/ProviderHelper.java | 44 ++++++++----------- OpenKeychain/src/main/res/values/strings.xml | 2 +- 2 files changed, 20 insertions(+), 26 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 519d5ee0f..1c68a7b0b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,7 +29,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; @@ -172,36 +172,31 @@ public class ProviderHelper { } } - public Object getUnifiedData(long masterKeyId, String column, int type) - throws NotFoundException { - return getUnifiedData(masterKeyId, new String[]{column}, new int[]{type}).get(column); - } - public HashMap getUnifiedData(long masterKeyId, String[] proj, int[] types) throws NotFoundException { return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - private LongSparseArray getUncachedMasterKeys(Uri queryUri) { - Cursor cursor = mContentResolver.query(queryUri, - new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, - null, null, null); + private LongSparseArray getAllWrappedMasterKeys() { + Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { + KeyRings.MASTER_KEY_ID, + // we pick from cache only information that is not easily available from keyrings + KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED, + // and of course, ring data + KeyRings.PUBKEY_DATA + }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); - LongSparseArray result = - new LongSparseArray(cursor.getCount()); + LongSparseArray result = + new LongSparseArray(cursor.getCount()); try { if (cursor != null && cursor.moveToFirst()) do { long masterKeyId = cursor.getLong(0); - byte[] data = cursor.getBlob(1); - if (data != null) { - try { - result.put(masterKeyId, - UncachedKeyRing.decodeFromData(data).getPublicKey()); - } catch(PgpGeneralException e) { - Log.e(Constants.TAG, "Error parsing keyring, skipping " + masterKeyId, e); - } catch(IOException e) { - Log.e(Constants.TAG, "IO error, skipping keyring" + masterKeyId, e); - } + boolean hasAnySecret = cursor.getInt(1) > 0; + int verified = cursor.getInt(2); + byte[] blob = cursor.getBlob(3); + if (blob != null) { + result.put(masterKeyId, + new WrappedPublicKeyRing(blob, hasAnySecret, verified).getSubkey()); } } while (cursor.moveToNext()); } finally { @@ -394,8 +389,7 @@ public class ProviderHelper { mIndent -= 1; // get a list of owned secret keys, for verification filtering - LongSparseArray trustedKeys = - getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri()); + LongSparseArray trustedKeys = getAllWrappedMasterKeys(); log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ Integer.toString(trustedKeys.size()) }); @@ -456,7 +450,7 @@ public class ProviderHelper { // verify signatures from known private keys if (trustedKeys.indexOfKey(certId) >= 0) { - UncachedPublicKey trustedKey = trustedKeys.get(certId); + WrappedPublicKey trustedKey = trustedKeys.get(certId); cert.init(trustedKey); if (cert.verifySignature(masterKey, userId)) { item.trustedCerts.add(cert); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 642106c1b..41cf0d246 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -538,7 +538,7 @@ Using %s trusted keys Encountered bad certificate! Error processing certificate! - Found good certificate from %2$s (%2$s) + Found good certificate from %1$s (%2$s) Ignored %s certificates from unknown pubkeys Classifying user ids Inserting user ids From 9dd40b7238826ec05dc3162c58cfa90909b1c5ad Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 18:17:35 +0200 Subject: [PATCH 24/60] import-log: more minor improvements --- .../keychain/provider/ProviderHelper.java | 11 +++++------ .../keychain/service/OperationResultParcel.java | 4 +--- OpenKeychain/src/main/res/values/strings.xml | 14 ++++++-------- 3 files changed, 12 insertions(+), 17 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 1c68a7b0b..96bf3f207 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -390,13 +390,12 @@ public class ProviderHelper { // get a list of owned secret keys, for verification filtering LongSparseArray trustedKeys = getAllWrappedMasterKeys(); - log(LogLevel.INFO, LogType.MSG_IP_TRUST_USING, new String[]{ - Integer.toString(trustedKeys.size()) - }); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. - log(LogLevel.DEBUG, LogType.MSG_IP_UID_CLASSIFYING); + log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ + Integer.toString(trustedKeys.size()) + }); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( @@ -471,18 +470,18 @@ public class ProviderHelper { }); } } - mIndent -= 1; if (unknownCerts > 0) { log(LogLevel.DEBUG, LogType.MSG_IP_UID_CERTS_UNKNOWN, new String[]{ Integer.toString(unknownCerts) }); } + mIndent -= 1; } mIndent -= 1; - log(LogLevel.INFO, LogType.MSG_IP_UID_INSERT); + log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. Collections.sort(uids); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 9790d216d..f83dc796f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -133,14 +133,12 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), - MSG_IP_TRUST_RETRIEVE (R.string.msg_ip_trust_retrieve), - MSG_IP_TRUST_USING (R.string.msg_ip_trust_using), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), MSG_IP_UID_CERT_GOOD (R.string.msg_ip_uid_cert_good), MSG_IP_UID_CERTS_UNKNOWN (R.string.msg_ip_uid_certs_unknown), MSG_IP_UID_CLASSIFYING (R.string.msg_ip_uid_classifying), - MSG_IP_UID_INSERT (R.string.msg_ip_uid_insert), + MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 41cf0d246..c57347055 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -534,14 +534,12 @@ Subkey creation date lies in the future! (%s) Successfully imported public keyring Re-inserting secret key - Retrieving trusted keys - Using %s trusted keys Encountered bad certificate! Error processing certificate! Found good certificate from %1$s (%2$s) - Ignored %s certificates from unknown pubkeys - Classifying user ids - Inserting user ids + Ignoring %s certificates from unknown pubkeys + Classifying user ids, using %s trusted signatures + Re-ordering user ids Processing user id %s Found uid revocation certificate Bad self certificate encountered! @@ -567,15 +565,15 @@ Removing bad keyring revocation key Removing redundant keyring revocation key Processing subkey %s - Subkey binding certificate verification failed! - Subkey binding certificate verification failed with an error! + Removing invalid subkey binding certificate + Removing bad subkey binding certificate! Subkey binding issuer id mismatch Unknown subkey certificate type: %s No valid certificate found for %s, removing from ring! Removing bad subkey revocation key Removing bad subkey revocation key Removing redundant keyring revocation key - OK + Subkey binding OK Keyring canonicalization successful Keyring canonicalization successful, removed %s certificates Removing bad self certificate for user id %s From 0594d9156ef148e3dd4ad7301cb4c036893dd383 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 21:57:03 +0200 Subject: [PATCH 25/60] canonicalize: filter out future and local certificates --- .../keychain/pgp/UncachedKeyRing.java | 80 ++++++++++++++++--- .../keychain/pgp/WrappedSignature.java | 7 ++ .../service/OperationResultParcel.java | 13 ++- OpenKeychain/src/main/res/values/strings.xml | 19 +++-- 4 files changed, 99 insertions(+), 20 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 1edc529c6..8019b2b52 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.S2K; +import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; @@ -23,6 +24,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -178,6 +180,7 @@ public class UncachedKeyRing { * * More specifically: * - Remove all non-verifying self-certificates + * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" * - Remove all certificates which are superseded by a newer one on the same target * @@ -200,6 +203,8 @@ public class UncachedKeyRing { new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); indent += 1; + final Date now = new Date(); + int removedCerts = 0; PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; @@ -215,6 +220,7 @@ public class UncachedKeyRing { PGPSignature revocation = null; for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { int type = zert.getSignatureType(); + // Disregard certifications on user ids, we will deal with those later if (type == PGPSignature.NO_CERTIFICATION || type == PGPSignature.DEFAULT_CERTIFICATION @@ -227,7 +233,7 @@ public class UncachedKeyRing { if (type != PGPSignature.KEY_REVOCATION) { // Unknown type, just remove - log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); @@ -235,6 +241,22 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + try { cert.init(masterKey); if (!cert.verifySignature(masterKey)) { @@ -276,7 +298,38 @@ public class UncachedKeyRing { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); - // If this is a foreign signature, never mind + + int type = zert.getSignatureType(); + if (type != PGPSignature.DEFAULT_CERTIFICATION + && type != PGPSignature.NO_CERTIFICATION + && type != PGPSignature.CASUAL_CERTIFICATION + && type != PGPSignature.POSITIVE_CERTIFICATION + && type != PGPSignature.CERTIFICATION_REVOCATION) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TYPE, + new String[] { + "0x" + Integer.toString(zert.getSignatureType(), 16) + }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + removedCerts += 1; + } + + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + removedCerts += 1; + continue; + } + + // If this is a foreign signature, never mind any further if (certId != masterKeyId) { continue; } @@ -299,7 +352,7 @@ public class UncachedKeyRing { continue; } - switch (zert.getSignatureType()) { + switch (type) { case PGPSignature.DEFAULT_CERTIFICATION: case PGPSignature.NO_CERTIFICATION: case PGPSignature.CASUAL_CERTIFICATION: @@ -356,14 +409,6 @@ public class UncachedKeyRing { } break; - default: - log.add(LogLevel.WARN, LogType.MSG_KC_UID_UNKNOWN_CERT, - new String[] { - "0x" + Integer.toString(zert.getSignatureType(), 16), - userId - }, indent); - modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; } } @@ -401,6 +446,7 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); continue; } + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) @@ -408,6 +454,18 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().after(now)) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TIME, null, indent); + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_LOCAL, null, indent); + continue; + } + if (type == PGPSignature.SUBKEY_BINDING) { // TODO verify primary key binding signature for signing keys! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 093b6c96f..be7f960a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -178,4 +178,11 @@ public class WrappedSignature { return new WrappedSignature(signatures.get(0)); } + public boolean isLocal() { + if (!mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { + return false; + } + SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE); + return p.getData()[0] == 0; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index f83dc796f..e7fb951cd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -161,12 +161,17 @@ public class OperationResultParcel implements Parcelable { MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), + MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), + MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time), + MSG_KC_REVOKE_BAD_TYPE (R.string.msg_kc_revoke_bad_type), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), MSG_KC_SUB (R.string.msg_kc_sub), - MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), - MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), MSG_KC_SUB_BAD(R.string.msg_kc_sub_bad), + MSG_KC_SUB_BAD_ERR(R.string.msg_kc_sub_bad_err), + MSG_KC_SUB_BAD_LOCAL(R.string.msg_kc_sub_bad_local), + MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), + MSG_KC_SUB_BAD_TIME(R.string.msg_kc_sub_bad_time), MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), @@ -176,11 +181,13 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), + MSG_KC_UID_BAD_LOCAL (R.string.msg_kc_uid_bad_local), + MSG_KC_UID_BAD_TIME (R.string.msg_kc_uid_bad_time), + MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), - MSG_KC_UID_UNKNOWN_CERT (R.string.msg_kc_uid_unknown_cert), ; private final int mMsgId; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index c57347055..1f0cf0c4e 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -561,15 +561,20 @@ Canonicalizing keyring %s Processing master key OK - Removing bad keyring revocation key - Removing bad keyring revocation key - Removing redundant keyring revocation key + Removing bad keyring revocation certificate + Removing keyring revocation certificate with "local" flag + Removing keyring revocation certificate with future timestamp + Removing master key certificate of unknown type (%s) + Removing bad keyring revocation certificate + Removing redundant keyring revocation certificate Processing subkey %s Removing invalid subkey binding certificate - Removing bad subkey binding certificate! + Removing bad subkey binding certificate + Removing subkey binding certificate with "local" flag Subkey binding issuer id mismatch + Removing subkey binding certificate with future timestamp Unknown subkey certificate type: %s - No valid certificate found for %s, removing from ring! + No valid certificate found for %s, removing from ring Removing bad subkey revocation key Removing bad subkey revocation key Removing redundant keyring revocation key @@ -577,11 +582,13 @@ Keyring canonicalization successful Keyring canonicalization successful, removed %s certificates Removing bad self certificate for user id %s + Removing user id certificate with "local" flag + Removing user id with future timestamp + Removing user id certificate of unknown type (%s) Removing bad self certificate for user id "%s" Removing outdated self certificate for user id "%s" Removing redundant revocation certificate for user id "%s" Removing outdated revocation certificate for user id "%s" - Removing unknown certificate of type 0x%1$s from user id "%2$s" Certifier From 79131be5f09c9ebaaa9c76ef717b2bd68c9b49db Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 22:27:32 +0200 Subject: [PATCH 26/60] canonicalize: simplify import with info from canonicalization --- .../keychain/provider/ProviderHelper.java | 44 ++++--------------- .../service/OperationResultParcel.java | 4 -- OpenKeychain/src/main/res/values/strings.xml | 4 -- 3 files changed, 9 insertions(+), 43 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 96bf3f207..102c8e6d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -177,7 +177,7 @@ public class ProviderHelper { return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); } - private LongSparseArray getAllWrappedMasterKeys() { + private LongSparseArray getTrustedMasterKeys() { Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { KeyRings.MASTER_KEY_ID, // we pick from cache only information that is not easily available from keyrings @@ -361,12 +361,6 @@ public class ProviderHelper { Date creation = key.getCreationTime(); values.put(Keys.CREATION, creation.getTime() / 1000); - if (creation.after(new Date())) { - log(LogLevel.ERROR, LogType.MSG_IP_SUBKEY_FUTURE, new String[]{ - creation.toString() - }); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } Date expiryDate = key.getExpiryTime(); if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); @@ -389,7 +383,7 @@ public class ProviderHelper { mIndent -= 1; // get a list of owned secret keys, for verification filtering - LongSparseArray trustedKeys = getAllWrappedMasterKeys(); + LongSparseArray trustedKeys = getTrustedMasterKeys(); // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. @@ -415,34 +409,16 @@ public class ProviderHelper { try { // self signature if (certId == masterKeyId) { - cert.init(masterKey); - if (!cert.verifySignature(masterKey, userId)) { - // Bad self certification? That's kinda bad... - log(LogLevel.ERROR, LogType.MSG_IP_UID_SELF_BAD); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); - } - // if we already have a cert.. - if (item.selfCert != null) { - // ..is this perchance a more recent one? - if (item.selfCert.getCreationTime().before(cert.getCreationTime())) { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_NEWER); - } else { - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_IGNORING_OLD); - continue; - } - } else { + // NOTE self-certificates are already verified during canonicalization, + // AND we know there is at most one cert plus at most one revocation + if (!cert.isRevocation()) { + item.selfCert = cert; + item.isPrimary = cert.isPrimaryUserId(); log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_GOOD); - } - - // save certificate as primary self-cert - item.selfCert = cert; - item.isPrimary = cert.isPrimaryUserId(); - if (cert.isRevocation()) { + } else { item.isRevoked = true; log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); - } else { - item.isRevoked = false; } } @@ -489,10 +465,8 @@ public class ProviderHelper { for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) { UserIdItem item = uids.get(userIdRank); operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); - // no self cert is bad, but allowed by the rfc... if (item.selfCert != null) { - operations.add(buildCertOperations( - masterKeyId, userIdRank, item.selfCert, + operations.add(buildCertOperations(masterKeyId, userIdRank, item.selfCert, secretRing != null ? Certs.VERIFIED_SECRET : Certs.VERIFIED_SELF)); } // don't bother with trusted certs if the uid is revoked, anyways diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index e7fb951cd..701285fa8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -131,7 +131,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_SUBKEY_FLAGS_XEX (R.string.msg_ip_subkey_flags_xex), MSG_IP_SUBKEY_FLAGS_XXS (R.string.msg_ip_subkey_flags_xxs), MSG_IP_SUBKEY_FLAGS_XXX (R.string.msg_ip_subkey_flags_xxx), - MSG_IP_SUBKEY_FUTURE (R.string.msg_ip_subkey_future), MSG_IP_SUCCESS (R.string.msg_ip_success), MSG_IP_UID_CERT_BAD (R.string.msg_ip_uid_cert_bad), MSG_IP_UID_CERT_ERROR (R.string.msg_ip_uid_cert_error), @@ -141,10 +140,7 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), - MSG_IP_UID_SELF_BAD (R.string.msg_ip_uid_self_bad), MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), - MSG_IP_UID_SELF_IGNORING_OLD (R.string.msg_ip_uid_self_ignoring_old), - MSG_IP_UID_SELF_NEWER (R.string.msg_ip_uid_self_newer), // import secret MSG_IS(R.string.msg_is), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 1f0cf0c4e..29bcdd679 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -531,7 +531,6 @@ Subkey flags: encrypt Subkey flags: sign Subkey flags: none - Subkey creation date lies in the future! (%s) Successfully imported public keyring Re-inserting secret key Encountered bad certificate! @@ -542,10 +541,7 @@ Re-ordering user ids Processing user id %s Found uid revocation certificate - Bad self certificate encountered! Found good self certificate - Ignoring older self certificate - Using more recent good self certificate Tried to import public keyring as secret. This is a bug, please file a report! From 073433fa747d698c6666081b2ee312062ecf2115 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Jun 2014 23:10:44 +0200 Subject: [PATCH 27/60] canonicalize: require primary key binding certificates for signing subkeys --- .../keychain/pgp/UncachedKeyRing.java | 44 +++++++++++++++++-- .../service/OperationResultParcel.java | 3 ++ OpenKeychain/src/main/res/values/strings.xml | 7 ++- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 8019b2b52..e309ed632 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -3,6 +3,8 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.S2K; import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPKeyFlags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; @@ -10,6 +12,7 @@ import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPUtil; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -298,7 +301,6 @@ public class UncachedKeyRing { WrappedSignature cert = new WrappedSignature(zert); long certId = cert.getKeyId(); - int type = zert.getSignatureType(); if (type != PGPSignature.DEFAULT_CERTIFICATION && type != PGPSignature.NO_CERTIFICATION @@ -435,9 +437,12 @@ public class UncachedKeyRing { // certificate. PGPPublicKey modified = key; PGPSignature selfCert = null, revocation = null; - for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + uids: for (PGPSignature zig : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) modified = PGPPublicKey.removeCertification(modified, zig); + // add this too, easier than adding it for every single "continue" case + removedCerts += 1; + WrappedSignature cert = new WrappedSignature(zig); int type = cert.getSignatureType(); @@ -467,7 +472,6 @@ public class UncachedKeyRing { } if (type == PGPSignature.SUBKEY_BINDING) { - // TODO verify primary key binding signature for signing keys! // make sure the certificate checks out try { @@ -481,10 +485,42 @@ public class UncachedKeyRing { continue; } + if (zig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { + int flags = ((KeyFlags) zig.getHashedSubPackets() + .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); + // If this subkey is allowed to sign data, + if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) { + try { + PGPSignatureList list = zig.getUnhashedSubPackets().getEmbeddedSignatures(); + boolean ok = false; + for (int i = 0; i < list.size(); i++) { + WrappedSignature subsig = new WrappedSignature(list.get(i)); + if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { + subsig.init(key); + if (subsig.verifySignature(masterKey, key)) { + ok = true; + } else { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD, null, indent); + continue uids; + } + } + } + if (!ok) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, null, indent); + continue; + } + } catch (Exception e) { + log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, null, indent); + continue; + } + } + } + // if we already have a cert, and this one is not newer: skip it if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { continue; } + selfCert = zig; // if this is newer than a possibly existing revocation, drop that one if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { @@ -525,9 +561,11 @@ public class UncachedKeyRing { // re-add certification modified = PGPPublicKey.addCertification(modified, selfCert); + removedCerts -= 1; // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); + removedCerts -= 1; } // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 701285fa8..b5f01ce4d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -169,6 +169,9 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUB_BAD_KEYID(R.string.msg_kc_sub_bad_keyid), MSG_KC_SUB_BAD_TIME(R.string.msg_kc_sub_bad_time), MSG_KC_SUB_BAD_TYPE(R.string.msg_kc_sub_bad_type), + MSG_KC_SUB_PRIMARY_BAD(R.string.msg_kc_sub_primary_bad), + MSG_KC_SUB_PRIMARY_BAD_ERR(R.string.msg_kc_sub_primary_bad_err), + MSG_KC_SUB_PRIMARY_NONE(R.string.msg_kc_sub_primary_none), MSG_KC_SUB_NO_CERT(R.string.msg_kc_sub_no_cert), MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 29bcdd679..b415c6d42 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -514,8 +514,8 @@ Operation failed due to database error Operation failed due to internal error Importing public keyring %s - Inserting keyring data - Inserting subkeys + Encoding keyring data + Evaluating subkeys Preparing database operations OK Preserving available secret key @@ -570,6 +570,9 @@ Subkey binding issuer id mismatch Removing subkey binding certificate with future timestamp Unknown subkey certificate type: %s + Removing subkey binding certificate due to invalid primary binding certificate + Removing subkey binding certificate due to bad primary binding certificate + Removing subkey binding certificate due to missing primary binding certificate No valid certificate found for %s, removing from ring Removing bad subkey revocation key Removing bad subkey revocation key From 63861fa60bc02c7f25ae417727a6327718373b88 Mon Sep 17 00:00:00 2001 From: Art O Cathain Date: Sat, 14 Jun 2014 13:16:39 +0100 Subject: [PATCH 28/60] Later version of Junit prevents 'Multiple dex files define Lorg/hamcrest/Description' error --- OpenKeychain/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index 32b60e915..ba527948a 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -30,7 +30,7 @@ dependencies { compile project(':extern:dnsjava') // Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well - androidTestCompile 'junit:junit:4.10' + 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' From 41545e5e52cd75b3408c127feeb43d4f2bd86e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 19:59:41 +0200 Subject: [PATCH 29/60] Added notification wrapper for toast like notifys --- .../keychain/ui/adapter/Notify.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java new file mode 100644 index 000000000..5b8979235 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.ui.adapter; + +import android.app.Activity; +import android.content.res.Resources; + +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; + +/** + * @author danielhass + * Notify wrapper which allows a more easy use of different notification libraries + */ +public class Notify { + + public static enum Style {OK, WARN, ERROR} + + /** + * Shows a simple in-layout notification with the CharSequence given as parameter + * @param activity + * @param text Text to show + * @param style Notification styling + */ + public static void showNotify(Activity activity, CharSequence text, Style style) { + + SuperCardToast st = new SuperCardToast(activity); + st.setText(text); + st.setDuration(SuperToast.Duration.MEDIUM); + switch (style){ + case OK: + st.setBackground(SuperToast.Background.GREEN); + break; + case WARN: + st.setBackground(SuperToast.Background.ORANGE); + break; + case ERROR: + st.setBackground(SuperToast.Background.RED); + break; + } + st.show(); + + } + + /** + * Shows a simple in-layout notification with the resource text from given id + * @param activity + * @param resId ResourceId of notification text + * @param style Notification styling + * @throws Resources.NotFoundException + */ + public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException { + showNotify(activity, activity.getResources().getText(resId), style); + } +} \ No newline at end of file From 998f6166077693781f79268d191b776516d04cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 20:02:09 +0200 Subject: [PATCH 30/60] Added notification area as reusable layout --- .../src/main/res/layout/notification_area.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 OpenKeychain/src/main/res/layout/notification_area.xml diff --git a/OpenKeychain/src/main/res/layout/notification_area.xml b/OpenKeychain/src/main/res/layout/notification_area.xml new file mode 100644 index 000000000..d1ba265a5 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/notification_area.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file From e48d5e4474a19cb81fa1842f35c85906c6ce5132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 20:03:40 +0200 Subject: [PATCH 31/60] Replaced AppMsg with notify wrapper call --- .../sufficientlysecure/keychain/ui/DecryptFileFragment.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index d953e2591..6805ae49a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; +import android.provider.SyncStateContract; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -39,6 +40,7 @@ import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.adapter.Notify; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -115,7 +117,8 @@ public class DecryptFileFragment extends DecryptFragment { } if (mInputFilename.equals("")) { - AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); + //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR); return; } From 76d733cbc7175c7c7635cc96364e889f839993bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Sun, 15 Jun 2014 20:04:31 +0200 Subject: [PATCH 32/60] Added notification area to decrypt content --- OpenKeychain/src/main/res/layout/decrypt_content.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenKeychain/src/main/res/layout/decrypt_content.xml b/OpenKeychain/src/main/res/layout/decrypt_content.xml index a496d8b9d..866857143 100644 --- a/OpenKeychain/src/main/res/layout/decrypt_content.xml +++ b/OpenKeychain/src/main/res/layout/decrypt_content.xml @@ -6,6 +6,8 @@ android:layout_height="match_parent" android:orientation="vertical"> + + Date: Sun, 15 Jun 2014 20:11:40 +0200 Subject: [PATCH 33/60] Removed unused import --- .../org/sufficientlysecure/keychain/ui/DecryptFileFragment.java | 1 - 1 file changed, 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index 6805ae49a..f0bdabe93 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -24,7 +24,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; -import android.provider.SyncStateContract; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; From 5bd0b5011e2af23dc8b1054f65c9459bdeddcd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Mon, 16 Jun 2014 13:13:43 +0200 Subject: [PATCH 34/60] Moved Notify class to utils --- .../org/sufficientlysecure/keychain/ui/DecryptFileFragment.java | 2 +- .../keychain/{ui/adapter => util}/Notify.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/{ui/adapter => util}/Notify.java (97%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index f0bdabe93..7fdab7bdd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -39,7 +39,7 @@ import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.adapter.Notify; +import org.sufficientlysecure.keychain.util.Notify; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java similarity index 97% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java index 5b8979235..245a99be7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.sufficientlysecure.keychain.ui.adapter; +package org.sufficientlysecure.keychain.util; import android.app.Activity; import android.content.res.Resources; From da28c000adf57baab1c282cc6efd3f1d8eb9e392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ha=C3=9F?= Date: Mon, 16 Jun 2014 13:19:34 +0200 Subject: [PATCH 35/60] Changed license header --- .../keychain/util/Notify.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java index 245a99be7..67f81fb24 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java @@ -1,18 +1,18 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2011 Senecaso + * Copyright (C) 2014 Dominik Schürmann * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package org.sufficientlysecure.keychain.util; From 109bea754207e960a22eaea7df819273df51112f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 22:00:19 +0200 Subject: [PATCH 36/60] import-log: distinguish master/subkeys, distinguish redundant/bad certs, more docs --- .../keychain/pgp/UncachedKeyRing.java | 99 +++++++++++-------- .../keychain/provider/ProviderHelper.java | 64 +++++++----- .../service/OperationResultParcel.java | 40 ++++++-- OpenKeychain/src/main/res/values/strings.xml | 26 +++-- 4 files changed, 150 insertions(+), 79 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index e309ed632..9b9818c2f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -185,7 +185,12 @@ public class UncachedKeyRing { * - Remove all non-verifying self-certificates * - Remove all "future" self-certificates * - Remove all certificates flagged as "local" - * - Remove all certificates which are superseded by a newer one on the same target + * - Remove all certificates which are superseded by a newer one on the same target, + * including revocations with later re-certifications. + * - Remove all certificates of unknown type: + * - key revocation signatures on the master key + * - subkey binding signatures for subkeys + * - certifications and certification revocations for user ids * * After this cleaning, a number of checks are done: TODO implement * - See if each subkey retains a valid self certificate @@ -208,7 +213,7 @@ public class UncachedKeyRing { final Date now = new Date(); - int removedCerts = 0; + int redundantCerts = 0, badCerts = 0; PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; PGPPublicKey masterKey = mRing.getPublicKey(); @@ -240,7 +245,7 @@ public class UncachedKeyRing { "0x" + Integer.toString(type, 16) }, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -248,7 +253,7 @@ public class UncachedKeyRing { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -256,7 +261,7 @@ public class UncachedKeyRing { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -265,13 +270,13 @@ public class UncachedKeyRing { if (!cert.verifySignature(masterKey)) { log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_ERR, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -281,12 +286,12 @@ public class UncachedKeyRing { // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(zert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, revocation); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); revocation = zert; } else { modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, null, indent); } } @@ -312,14 +317,14 @@ public class UncachedKeyRing { "0x" + Integer.toString(zert.getSignatureType(), 16) }, indent); modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + badCerts += 1; } if (cert.getCreationTime().after(now)) { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -327,7 +332,7 @@ public class UncachedKeyRing { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, null, indent); modified = PGPPublicKey.removeCertification(modified, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -343,14 +348,14 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD, new String[] { userId }, indent); modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_ERR, new String[] { userId }, indent); modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + badCerts += 1; continue; } @@ -363,13 +368,13 @@ public class UncachedKeyRing { selfCert = zert; } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, selfCert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); selfCert = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); } @@ -378,7 +383,7 @@ public class UncachedKeyRing { && revocation.getCreationTime().before(selfCert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, revocation); revocation = null; - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); } @@ -388,7 +393,7 @@ public class UncachedKeyRing { // If this is older than the (latest) self cert, drop it if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); continue; @@ -399,13 +404,13 @@ public class UncachedKeyRing { // more revocations? at least one is superfluous, then. } else if (revocation.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, revocation); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); revocation = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); - removedCerts += 1; + redundantCerts += 1; log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); } @@ -418,8 +423,6 @@ public class UncachedKeyRing { // Replace modified key in the keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - - log.add(LogLevel.DEBUG, LogType.MSG_KC_MASTER_SUCCESS, null, indent); indent -= 1; } @@ -437,18 +440,17 @@ public class UncachedKeyRing { // certificate. PGPPublicKey modified = key; PGPSignature selfCert = null, revocation = null; - uids: for (PGPSignature zig : new IterableIterator(key.getSignatures())) { + uids: for (PGPSignature zert : new IterableIterator(key.getSignatures())) { // remove from keyring (for now) - modified = PGPPublicKey.removeCertification(modified, zig); - // add this too, easier than adding it for every single "continue" case - removedCerts += 1; + modified = PGPPublicKey.removeCertification(modified, zert); - WrappedSignature cert = new WrappedSignature(zig); + WrappedSignature cert = new WrappedSignature(zert); int type = cert.getSignatureType(); // filter out bad key types... if (cert.getKeyId() != masterKey.getKeyID()) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_KEYID, null, indent); + badCerts += 1; continue; } @@ -456,18 +458,21 @@ public class UncachedKeyRing { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TYPE, new String[]{ "0x" + Integer.toString(type, 16) }, indent); + badCerts += 1; continue; } if (cert.getCreationTime().after(now)) { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_TIME, null, indent); + badCerts += 1; continue; } if (cert.isLocal()) { // Creation date in the future? No way! log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_LOCAL, null, indent); + badCerts += 1; continue; } @@ -478,20 +483,22 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(masterKey, key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD, null, indent); + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_BAD_ERR, null, indent); + badCerts += 1; continue; } - if (zig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { - int flags = ((KeyFlags) zig.getHashedSubPackets() + if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) { + int flags = ((KeyFlags) zert.getHashedSubPackets() .getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags(); // If this subkey is allowed to sign data, if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) { try { - PGPSignatureList list = zig.getUnhashedSubPackets().getEmbeddedSignatures(); + PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures(); boolean ok = false; for (int i = 0; i < list.size(); i++) { WrappedSignature subsig = new WrappedSignature(list.get(i)); @@ -501,16 +508,19 @@ public class UncachedKeyRing { ok = true; } else { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD, null, indent); + badCerts += 1; continue uids; } } } if (!ok) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, null, indent); + badCerts += 1; continue; } } catch (Exception e) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, null, indent); + badCerts += 1; continue; } } @@ -518,10 +528,11 @@ public class UncachedKeyRing { // if we already have a cert, and this one is not newer: skip it if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { + redundantCerts += 1; continue; } - selfCert = zig; + selfCert = zert; // if this is newer than a possibly existing revocation, drop that one if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) { revocation = null; @@ -535,17 +546,22 @@ public class UncachedKeyRing { cert.init(masterKey); if (!cert.verifySignature(key)) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, null, indent); + badCerts += 1; continue; } } catch (PgpGeneralException e) { log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD_ERR, null, indent); + badCerts += 1; continue; } // if there is no binding (yet), or the revocation is newer than the binding: keep it - if (selfCert == null || selfCert.getCreationTime().before(cert.getCreationTime())) { - revocation = zig; + if (selfCert != null && selfCert.getCreationTime().after(cert.getCreationTime())) { + redundantCerts += 1; + continue; } + + revocation = zert; } } @@ -561,22 +577,25 @@ public class UncachedKeyRing { // re-add certification modified = PGPPublicKey.addCertification(modified, selfCert); - removedCerts -= 1; // add revocation, if any if (revocation != null) { modified = PGPPublicKey.addCertification(modified, revocation); - removedCerts -= 1; } // replace pubkey in keyring ring = PGPPublicKeyRing.insertPublicKey(ring, modified); - - log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_SUCCESS, null, indent); indent -= 1; } - if (removedCerts > 0) { - log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REMOVED, - new String[] { Integer.toString(removedCerts) }, indent); + if (badCerts > 0 && redundantCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_BAD_AND_RED, + new String[] { Integer.toString(badCerts), + Integer.toString(redundantCerts) }, indent); + } else if (badCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_BAD, + new String[] { Integer.toString(badCerts) }, indent); + } else if (redundantCerts > 0) { + log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS_REDUNDANT, + new String[] { Integer.toString(redundantCerts) }, indent); } else { log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, null, indent); } 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 102c8e6d0..80de65e0e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -302,7 +302,6 @@ public class ProviderHelper { log(LogLevel.INFO, LogType.MSG_IP_INSERT_KEYRING); { // insert keyring - // insert new version of this keyRing ContentValues values = new ContentValues(); values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); try { @@ -322,8 +321,9 @@ public class ProviderHelper { Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); int rank = 0; for (UncachedPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY, new String[]{ - PgpKeyHelper.convertKeyIdToHex(key.getKeyId()) + long keyId = key.getKeyId(); + log(LogLevel.DEBUG, keyId == masterKeyId ? LogType.MSG_IP_MASTER : LogType.MSG_IP_SUBKEY, new String[]{ + PgpKeyHelper.convertKeyIdToHex(keyId) }); mIndent += 1; @@ -341,21 +341,41 @@ public class ProviderHelper { values.put(Keys.CAN_ENCRYPT, e); values.put(Keys.CAN_SIGN, s); values.put(Keys.IS_REVOKED, key.isRevoked()); - if (c) { - if (e) { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES - : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + if (masterKeyId == keyId) { + if (c) { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_CES + : LogType.MSG_IP_MASTER_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_CXS + : LogType.MSG_IP_MASTER_FLAGS_CXX, null); + } } else { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS - : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_XES + : LogType.MSG_IP_MASTER_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_MASTER_FLAGS_XXS + : LogType.MSG_IP_MASTER_FLAGS_XXX, null); + } } } else { - if (e) { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES - : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + if (c) { + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CES + : LogType.MSG_IP_SUBKEY_FLAGS_CEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_CXS + : LogType.MSG_IP_SUBKEY_FLAGS_CXX, null); + } } else { - log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS - : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + if (e) { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XES + : LogType.MSG_IP_SUBKEY_FLAGS_XEX, null); + } else { + log(LogLevel.DEBUG, s ? LogType.MSG_IP_SUBKEY_FLAGS_XXS + : LogType.MSG_IP_SUBKEY_FLAGS_XXX, null); + } } } @@ -365,13 +385,13 @@ public class ProviderHelper { if (expiryDate != null) { values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); if (key.isExpired()) { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRED, new String[]{ - expiryDate.toString() - }); + log(LogLevel.DEBUG, keyId == masterKeyId ? + LogType.MSG_IP_MASTER_EXPIRED : LogType.MSG_IP_SUBKEY_EXPIRED, + new String[]{ expiryDate.toString() }); } else { - log(LogLevel.DEBUG, LogType.MSG_IP_SUBKEY_EXPIRES, new String[]{ - expiryDate.toString() - }); + log(LogLevel.DEBUG, keyId == masterKeyId ? + LogType.MSG_IP_MASTER_EXPIRES : LogType.MSG_IP_SUBKEY_EXPIRES, + new String[] { expiryDate.toString() }); } } @@ -415,10 +435,9 @@ public class ProviderHelper { if (!cert.isRevocation()) { item.selfCert = cert; item.isPrimary = cert.isPrimaryUserId(); - log(LogLevel.DEBUG, LogType.MSG_IP_UID_SELF_GOOD); } else { item.isRevoked = true; - log(LogLevel.DEBUG, LogType.MSG_IP_UID_REVOKED); + log(LogLevel.INFO, LogType.MSG_IP_UID_REVOKED); } } @@ -479,7 +498,6 @@ public class ProviderHelper { } } - log(LogLevel.DEBUG, LogType.MSG_IP_PREPARE_SUCCESS); mIndent -= 1; } catch (IOException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index b5f01ce4d..b9531f83f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -16,6 +16,7 @@ import java.util.ArrayList; * list (ie, enum) of all possible log types, which should in all cases be tied * to string resource ids. * + * */ public class OperationResultParcel implements Parcelable { /** Holds the overall result, the number specifying varying degrees of success. The first bit @@ -101,6 +102,23 @@ public class OperationResultParcel implements Parcelable { } + /** This is an enum of all possible log events. + * + * Element names should generally be prefixed with MSG_XX_ where XX is an + * identifier based on the related activity. + * + * Log messages should occur for each distinguishable action group. For + * each such group, one message is displayed followed by warnings or + * errors, and optionally subactions. The granularity should generally be + * optimistic: No "success" messages are printed except for the outermost + * operations - the success of an action group is indicated by the + * beginning message of the next action group. + * + * Log messages should be in present tense, There should be no trailing + * punctuation, except for error messages which may end in an exclamation + * mark. + * + */ public static enum LogType { // import public @@ -114,15 +132,24 @@ public class OperationResultParcel implements Parcelable { MSG_IP_FAIL_OP_EX (R.string.msg_ip_fail_op_ex), MSG_IP_FAIL_REMOTE_EX (R.string.msg_ip_fail_remote_ex), MSG_IP_INSERT_KEYRING (R.string.msg_ip_insert_keyring), - MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_subkeys), + MSG_IP_INSERT_SUBKEYS (R.string.msg_ip_insert_keys), MSG_IP_PREPARE (R.string.msg_ip_prepare), - MSG_IP_PREPARE_SUCCESS(R.string.msg_ip_prepare_success), MSG_IP_PRESERVING_SECRET (R.string.msg_ip_preserving_secret), MSG_IP_REINSERT_SECRET (R.string.msg_ip_reinsert_secret), + MSG_IP_MASTER (R.string.msg_ip_master), + MSG_IP_MASTER_EXPIRED (R.string.msg_ip_master_expired), + MSG_IP_MASTER_EXPIRES (R.string.msg_ip_master_expires), + MSG_IP_MASTER_FLAGS_CES (R.string.msg_ip_master_flags_ces), + MSG_IP_MASTER_FLAGS_CEX (R.string.msg_ip_master_flags_cex), + MSG_IP_MASTER_FLAGS_CXS (R.string.msg_ip_master_flags_cxs), + MSG_IP_MASTER_FLAGS_XES (R.string.msg_ip_master_flags_xes), + MSG_IP_MASTER_FLAGS_CXX (R.string.msg_ip_master_flags_cxx), + MSG_IP_MASTER_FLAGS_XEX (R.string.msg_ip_master_flags_xex), + MSG_IP_MASTER_FLAGS_XXS (R.string.msg_ip_master_flags_xxs), + MSG_IP_MASTER_FLAGS_XXX (R.string.msg_ip_master_flags_xxx), MSG_IP_SUBKEY (R.string.msg_ip_subkey), MSG_IP_SUBKEY_EXPIRED (R.string.msg_ip_subkey_expired), MSG_IP_SUBKEY_EXPIRES (R.string.msg_ip_subkey_expires), - MSG_IP_SUBKEY_FLAGS (R.string.msg_ip_subkey_flags), MSG_IP_SUBKEY_FLAGS_CES (R.string.msg_ip_subkey_flags_ces), MSG_IP_SUBKEY_FLAGS_CEX (R.string.msg_ip_subkey_flags_cex), MSG_IP_SUBKEY_FLAGS_CXS (R.string.msg_ip_subkey_flags_cxs), @@ -140,7 +167,6 @@ public class OperationResultParcel implements Parcelable { MSG_IP_UID_REORDER(R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (R.string.msg_ip_uid_revoked), - MSG_IP_UID_SELF_GOOD (R.string.msg_ip_uid_self_good), // import secret MSG_IS(R.string.msg_is), @@ -155,7 +181,6 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), MSG_KC_MASTER (R.string.msg_kc_master), - MSG_KC_MASTER_SUCCESS (R.string.msg_kc_master_success), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time), @@ -176,8 +201,9 @@ public class OperationResultParcel implements Parcelable { MSG_KC_SUB_REVOKE_BAD_ERR (R.string.msg_kc_sub_revoke_bad_err), MSG_KC_SUB_REVOKE_BAD (R.string.msg_kc_sub_revoke_bad), MSG_KC_SUB_REVOKE_DUP (R.string.msg_kc_sub_revoke_dup), - MSG_KC_SUB_SUCCESS (R.string.msg_kc_sub_success), - MSG_KC_SUCCESS_REMOVED (R.string.msg_kc_success_removed), + MSG_KC_SUCCESS_BAD (R.string.msg_kc_success_bad), + MSG_KC_SUCCESS_BAD_AND_RED (R.string.msg_kc_success_bad_and_red), + MSG_KC_SUCCESS_REDUNDANT (R.string.msg_kc_success_redundant), MSG_KC_SUCCESS (R.string.msg_kc_success), MSG_KC_UID_BAD_ERR (R.string.msg_kc_uid_bad_err), MSG_KC_UID_BAD_LOCAL (R.string.msg_kc_uid_bad_local), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 5a2c38419..d365302d9 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -516,14 +516,23 @@ Operation failed due to internal error Importing public keyring %s Encoding keyring data - Evaluating subkeys + Parsing keys Preparing database operations - OK Preserving available secret key + Processing master key %s + Keyring expired on %s + Keyring expires on %s + Master key flags: certify, encrypt, sign + Master key flags: certify, encrypt + Master key flags: certify, sign + Master key flags: encrypt, sign + Master key flags: certify + Master key flags: encrypt + Master key flags: sign + Master key flags: none Processing subkey %s Subkey expired on %s Subkey expires on %s - Subkey flags: %s Subkey flags: certify, encrypt, sign Subkey flags: certify, encrypt Subkey flags: certify, sign @@ -536,13 +545,12 @@ Re-inserting secret key Encountered bad certificate! Error processing certificate! - Found good certificate from %1$s (%2$s) + User id is certified by %1$s (%2$s) Ignoring %s certificates from unknown pubkeys Classifying user ids, using %s trusted signatures Re-ordering user ids Processing user id %s - Found uid revocation certificate - Found good self certificate + User id is revoked Tried to import public keyring as secret. This is a bug, please file a report! @@ -557,7 +565,6 @@ Canonicalizing keyring %s Processing master key - OK Removing bad keyring revocation certificate Removing keyring revocation certificate with "local" flag Removing keyring revocation certificate with future timestamp @@ -578,9 +585,10 @@ Removing bad subkey revocation key Removing bad subkey revocation key Removing redundant keyring revocation key - Subkey binding OK Keyring canonicalization successful - Keyring canonicalization successful, removed %s certificates + Keyring canonicalization successful, removed %s erroneous certificates + Keyring canonicalization successful, removed %1$s erroneous and %2$s redundant certificates + Keyring canonicalization successful, removed %s redundant certificates Removing bad self certificate for user id %s Removing user id certificate with "local" flag Removing user id with future timestamp From 2f0e70587dfc21ce5c3d826f3d96b8231674f022 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 16:23:15 +0200 Subject: [PATCH 37/60] add more progress info from import routine --- .../keychain/pgp/PgpImportExport.java | 6 +++-- .../keychain/provider/ProviderHelper.java | 27 ++++++++++++++++++- .../keychain/ui/ViewKeyActivity.java | 13 +++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) 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 e1967429a..3681d62d8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLo import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -130,6 +131,7 @@ public class PgpImportExport { int newKeys = 0, oldKeys = 0, badKeys = 0; int position = 0; + int progSteps = 100 / entries.size(); for (ParcelableKeyRing entry : entries) { try { UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes()); @@ -149,7 +151,8 @@ public class PgpImportExport { if (key.isSecret()) { result = mProviderHelper.saveSecretKeyRing(key); } else { - result = mProviderHelper.savePublicKeyRing(key); + result = mProviderHelper.savePublicKeyRing(key, + new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); } if (!result.success()) { badKeys += 1; @@ -165,7 +168,6 @@ public class PgpImportExport { } // update progress position++; - updateProgress(position / entries.size() * 100, 100); } OperationLog log = mProviderHelper.getLog(); 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 80de65e0e..497957e5e 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.Progressable; import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; @@ -259,11 +260,29 @@ public class ProviderHelper { } } + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { + return savePublicKeyRing(keyRing, new Progressable() { + @Override + public void setProgress(String message, int current, int total) { + return; + } + + @Override + public void setProgress(int resourceId, int current, int total) { + return; + } + + @Override + public void setProgress(int current, int total) { + return; + } + }); + } /** * Saves PGPPublicKeyRing with its keys and userIds in DB */ @SuppressWarnings("unchecked") - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing, Progressable progress) { if (keyRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); @@ -287,6 +306,7 @@ public class ProviderHelper { try { secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached(); log(LogLevel.DEBUG, LogType.MSG_IP_PRESERVING_SECRET); + progress.setProgress(LogType.MSG_IP_PRESERVING_SECRET.getMsgId(), 30, 100); } catch (NotFoundException e) { secretRing = null; } @@ -316,6 +336,7 @@ public class ProviderHelper { } log(LogLevel.INFO, LogType.MSG_IP_INSERT_SUBKEYS); + progress.setProgress(LogType.MSG_IP_INSERT_SUBKEYS.getMsgId(), 40, 100); mIndent += 1; { // insert subkeys Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); @@ -410,6 +431,7 @@ public class ProviderHelper { log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ Integer.toString(trustedKeys.size()) }); + progress.setProgress(LogType.MSG_IP_UID_CLASSIFYING.getMsgId(), 60, 100); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( @@ -476,6 +498,7 @@ public class ProviderHelper { } mIndent -= 1; + progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 80, 100); log(LogLevel.DEBUG, LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) // this is a stable sort, so the order of keys is otherwise preserved. @@ -519,6 +542,7 @@ public class ProviderHelper { } log(LogLevel.DEBUG, LogType.MSG_IP_APPLY_BATCH); + progress.setProgress(LogType.MSG_IP_APPLY_BATCH.getMsgId(), 90, 100); mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); // Save the saved keyring (if any) @@ -532,6 +556,7 @@ public class ProviderHelper { mIndent -= 1; log(LogLevel.OK, LogType.MSG_IP_SUCCESS); + progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 100, 100); return new SaveKeyringResult(result, mLog); } catch (RemoteException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 463c800d2..13e9d22ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -56,6 +56,7 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; @@ -121,6 +122,18 @@ public class ViewKeyActivity extends ActionBarActivity implements mViewPager = (ViewPager) findViewById(R.id.view_key_pager); mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout); + mSlidingTabLayout.setCustomTabColorizer(new TabColorizer() { + @Override + public int getIndicatorColor(int position) { + return position == TAB_CERTS || position == TAB_KEYS ? 0xFFFF4444 : 0xFFAA66CC; + } + + @Override + public int getDividerColor(int position) { + return 0; + } + }); + int switchToTab = TAB_MAIN; Intent intent = getIntent(); if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { From ffea551afc95ec68892ca9003f99c383c7cc5661 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jun 2014 22:32:17 +0200 Subject: [PATCH 38/60] drop invalid user ids and add fatal case(s) --- .../keychain/pgp/UncachedKeyRing.java | 21 ++++++++++++++----- .../keychain/provider/ProviderHelper.java | 3 +++ .../service/OperationResultParcel.java | 2 ++ OpenKeychain/src/main/res/values/strings.xml | 2 ++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 9b9818c2f..26c6c6451 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -191,10 +191,8 @@ public class UncachedKeyRing { * - key revocation signatures on the master key * - subkey binding signatures for subkeys * - certifications and certification revocations for user ids - * - * After this cleaning, a number of checks are done: TODO implement - * - See if each subkey retains a valid self certificate - * - See if each user id retains a valid self certificate + * - If a subkey retains no valid subkey binding certificate, remove it + * - If a user id retains no valid self certificate, remove it * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * @@ -419,6 +417,19 @@ public class UncachedKeyRing { } } + + // If no valid certificate (if only a revocation) remains, drop it + if (selfCert == null && revocation == null) { + modified = PGPPublicKey.removeCertification(modified, userId); + log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REVOKE_DUP, + new String[] { userId }, indent); + } + } + + // If NO user ids remain, error out! + if (!modified.getUserIDs().hasNext()) { + log.add(LogLevel.ERROR, LogType.MSG_KC_FATAL_NO_UID, null, indent); + return null; } // Replace modified key in the keyring @@ -570,7 +581,7 @@ public class UncachedKeyRing { ring = PGPPublicKeyRing.removePublicKey(ring, modified); log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, - new String[]{PgpKeyHelper.convertKeyIdToHex(key.getKeyID())}, indent); + new String[]{ PgpKeyHelper.convertKeyIdToHex(key.getKeyID()) }, indent); indent -= 1; continue; } 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 497957e5e..80fabdf48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -298,6 +298,9 @@ public class ProviderHelper { // Canonicalize this key, to assert a number of assumptions made about it. keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } UncachedPublicKey masterKey = keyRing.getPublicKey(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index b9531f83f..0760aadf8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -180,6 +180,7 @@ public class OperationResultParcel implements Parcelable { // keyring canonicalization MSG_KC (R.string.msg_kc), + MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), @@ -211,6 +212,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), ; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index d365302d9..ed06f983b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -564,6 +564,7 @@ Canonicalizing keyring %s + Keyring canonicalization failed: Keyring has no valid user ids Processing master key Removing bad keyring revocation certificate Removing keyring revocation certificate with "local" flag @@ -597,6 +598,7 @@ Removing outdated self certificate for user id "%s" Removing redundant revocation certificate for user id "%s" Removing outdated revocation certificate for user id "%s" + No valid self-certificate found for user id %s, removing from ring Certifier From 9058291c0504c0684ef9d84edbc2c4e7c446d6d2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 11:57:14 +0200 Subject: [PATCH 39/60] canonicalize: fix NullPointerException --- .../org/sufficientlysecure/keychain/pgp/WrappedSignature.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index be7f960a9..196ac1dee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -179,7 +179,8 @@ public class WrappedSignature { } public boolean isLocal() { - if (!mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { + if (!mSig.hasSubpackets() + || !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) { return false; } SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE); From 02fbaaf8580c4988d9e4e5031586ad4328ee21c2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 12:18:41 +0200 Subject: [PATCH 40/60] import-log: clean up progress messages --- .../keychain/pgp/UncachedKeyRing.java | 12 ++++++------ .../keychain/provider/ProviderHelper.java | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 26c6c6451..f22ea7697 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -367,13 +367,13 @@ public class UncachedKeyRing { } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, selfCert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); selfCert = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP, new String[] { userId }, indent); } // If there is a revocation certificate, and it's older than this, drop it @@ -382,7 +382,7 @@ public class UncachedKeyRing { modified = PGPPublicKey.removeCertification(modified, userId, revocation); revocation = null; redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); } break; @@ -392,7 +392,7 @@ public class UncachedKeyRing { if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_OLD, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD, new String[] { userId }, indent); continue; } @@ -403,13 +403,13 @@ public class UncachedKeyRing { } else if (revocation.getCreationTime().before(cert.getCreationTime())) { modified = PGPPublicKey.removeCertification(modified, userId, revocation); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); revocation = zert; } else { modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; - log.add(LogLevel.INFO, LogType.MSG_KC_UID_REVOKE_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP, new String[] { userId }, indent); } break; 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 80fabdf48..8688ecb6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -434,7 +434,6 @@ public class ProviderHelper { log(LogLevel.INFO, LogType.MSG_IP_UID_CLASSIFYING, new String[]{ Integer.toString(trustedKeys.size()) }); - progress.setProgress(LogType.MSG_IP_UID_CLASSIFYING.getMsgId(), 60, 100); mIndent += 1; List uids = new ArrayList(); for (String userId : new IterableIterator( From a0f546739d518a924770540795bca71bfacd4b1d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 20:03:58 +0200 Subject: [PATCH 41/60] new-edit: remove old save keyring stuff --- .../keychain/pgp/PgpKeyOperation.java | 635 ++---------------- .../service/KeychainIntentService.java | 156 +---- .../service/OldSaveKeyringParcel.java | 128 ---- .../keychain/service/SaveKeyringParcel.java | 2 +- .../keychain/ui/EditKeyActivity.java | 9 +- .../keychain/ui/widget/SectionView.java | 8 +- 6 files changed, 101 insertions(+), 837 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java 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 44fc4c8c9..e9d8fd295 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -26,10 +26,8 @@ import org.spongycastle.jce.spec.ElGamalParameterSpec; import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPKeyPair; -import org.spongycastle.openpgp.PGPKeyRingGenerator; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; @@ -49,7 +47,6 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; -import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Primes; @@ -62,11 +59,9 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.SignatureException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; -import java.util.Iterator; import java.util.List; import java.util.TimeZone; @@ -109,571 +104,83 @@ public class PgpKeyOperation { } } - /** - * Creates new secret key. - * - * @param algorithmChoice - * @param keySize - * @param passphrase - * @param isMasterKey - * @return A newly created PGPSecretKey - * @throws NoSuchAlgorithmException - * @throws PGPException - * @throws NoSuchProviderException - * @throws PgpGeneralMsgIdException - * @throws InvalidAlgorithmParameterException - */ - - // TODO: key flags? - public byte[] createKey(int algorithmChoice, int keySize, String passphrase, - boolean isMasterKey) - throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, - PgpGeneralMsgIdException, InvalidAlgorithmParameterException { - - if (keySize < 512) { - throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit); - } - - if (passphrase == null) { - passphrase = ""; - } - - int algorithm; - KeyPairGenerator keyGen; - - switch (algorithmChoice) { - case Constants.choice.algorithm.dsa: { - keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - algorithm = PGPPublicKey.DSA; - break; - } - - case Constants.choice.algorithm.elgamal: { - if (isMasterKey) { - throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal); - } - keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - BigInteger p = Primes.getBestPrime(keySize); - BigInteger g = new BigInteger("2"); - - ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); - - keyGen.initialize(elParams); - algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; - break; - } - - case Constants.choice.algorithm.rsa: { - keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - - algorithm = PGPPublicKey.RSA_GENERAL; - break; - } - - default: { - throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice); - } - } - - // build new key pair - PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - - // Build key encrypter and decrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + /** Creates new secret key. */ + private PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase, + boolean isMasterKey) throws PgpGeneralMsgIdException { try { + if (keySize < 512) { + throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit); + } + + if (passphrase == null) { + passphrase = ""; + } + + int algorithm; + KeyPairGenerator keyGen; + + switch (algorithmChoice) { + case Constants.choice.algorithm.dsa: { + keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + algorithm = PGPPublicKey.DSA; + break; + } + + case Constants.choice.algorithm.elgamal: { + if (isMasterKey) { + throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal); + } + keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + BigInteger p = Primes.getBestPrime(keySize); + BigInteger g = new BigInteger("2"); + + ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); + + keyGen.initialize(elParams); + algorithm = PGPPublicKey.ELGAMAL_ENCRYPT; + break; + } + + case Constants.choice.algorithm.rsa: { + keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + + algorithm = PGPPublicKey.RSA_GENERAL; + break; + } + + default: { + throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice); + } + } + + // build new key pair + PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + + // Build key encrypter and decrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), - sha1Calc, isMasterKey, keyEncryptor).getEncoded(); - } catch(IOException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding); + sha1Calc, isMasterKey, keyEncryptor); + } catch(NoSuchProviderException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + } catch(NoSuchAlgorithmException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + } catch(InvalidAlgorithmParameterException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + } catch(PGPException e) { + throw new PgpGeneralMsgIdException(R.string.error_encoding, e); } } - public Pair buildNewSecretKey( - OldSaveKeyringParcel saveParcel) - throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { - - int usageId = saveParcel.keysUsages.get(0); - boolean canSign; - String mainUserId = saveParcel.userIds.get(0); - - PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal(); - - // this removes all userIds and certifications previously attached to the masterPublicKey - PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassphrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(R.string.progress_certifying_master_key, 20, 100); - - for (String userId : saveParcel.userIds) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - hashedPacketsGen.setKeyFlags(true, usageId); - - hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - - if (saveParcel.keysExpiryDates.get(0) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(masterPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(0); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway - } - - updateProgress(R.string.progress_building_master_key, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassphrase.toCharArray()); - - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - updateProgress(R.string.progress_adding_sub_keys, 40, 100); - - for (int i = 1; i < saveParcel.keys.size(); ++i) { - updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100); - - PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal(); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassphrase.toCharArray()); - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - usageId = saveParcel.keysUsages.get(i); - canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this - if (canSign) { - Date todayDate = new Date(); //both sig times the same - // cross-certify signing keys - hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time - PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - subPublicKey.getAlgorithm(), PGPUtil.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); - PGPSignature certification = sGen.generateCertification(masterPublicKey, - subPublicKey); - unhashedPacketsGen.setEmbeddedSignature(false, certification); - } - hashedPacketsGen.setKeyFlags(false, usageId); - - if (saveParcel.keysExpiryDates.get(i) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(subPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(i); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway - } - - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - } - - PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); - PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); - - return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing)); - - } - - public Pair buildSecretKey(WrappedSecretKeyRing wmKR, - WrappedPublicKeyRing wpKR, - OldSaveKeyringParcel saveParcel) - throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { - - PGPSecretKeyRing mKR = wmKR.getRing(); - PGPPublicKeyRing pKR = wpKR.getRing(); - - updateProgress(R.string.progress_building_key, 0, 100); - - if (saveParcel.oldPassphrase == null) { - saveParcel.oldPassphrase = ""; - } - if (saveParcel.newPassphrase == null) { - saveParcel.newPassphrase = ""; - } - - /* - IDs - NB This might not need to happen later, if we change the way the primary ID is chosen - remove deleted ids - if the primary ID changed we need to: - remove all of the IDs from the keyring, saving their certifications - add them all in again, updating certs of IDs which have changed - else - remove changed IDs and add in with new certs - - if the master key changed, we need to remove the primary ID certification, so we can add - the new one when it is generated, and they don't conflict - - Keys - remove deleted keys - if a key is modified, re-sign it - do we need to remove and add in? - - Todo - identify more things which need to be preserved - e.g. trust levels? - user attributes - */ - - if (saveParcel.deletedKeys != null) { - for (UncachedSecretKey dKey : saveParcel.deletedKeys) { - mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal()); - } - } - - PGPSecretKey masterKey = mKR.getSecretKey(); - PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - - int usageId = saveParcel.keysUsages.get(0); - boolean canSign; - String mainUserId = saveParcel.userIds.get(0); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassphrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(R.string.progress_certifying_master_key, 20, 100); - - boolean anyIDChanged = false; - for (String delID : saveParcel.deletedIDs) { - anyIDChanged = true; - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID); - } - - int userIDIndex = 0; - - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - hashedPacketsGen.setKeyFlags(true, usageId); - - hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - - if (saveParcel.keysExpiryDates.get(0) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(masterPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(0); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway - } - - if (saveParcel.primaryIDChanged || - !saveParcel.originalIDs.get(0).equals(saveParcel.userIds.get(0))) { - anyIDChanged = true; - ArrayList> sigList = new ArrayList>(); - for (String userId : saveParcel.userIds) { - String origID = saveParcel.originalIDs.get(userIDIndex); - if (origID.equals(userId) && !saveParcel.newIDs[userIDIndex] && - !userId.equals(saveParcel.originalPrimaryID) && userIDIndex != 0) { - Iterator origSigs = masterPublicKey.getSignaturesForID(origID); - // TODO: make sure this iterator only has signatures we are interested in - while (origSigs.hasNext()) { - PGPSignature origSig = origSigs.next(); - sigList.add(new Pair(origID, origSig)); - } - } else { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - if (userIDIndex == 0) { - sGen.setHashedSubpackets(hashedPacketsGen.generate()); - sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); - } - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - sigList.add(new Pair(userId, certification)); - } - if (!saveParcel.newIDs[userIDIndex]) { - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID); - } - userIDIndex++; - } - for (Pair toAdd : sigList) { - masterPublicKey = - PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second); - } - } else { - for (String userId : saveParcel.userIds) { - String origID = saveParcel.originalIDs.get(userIDIndex); - if (!origID.equals(userId) || saveParcel.newIDs[userIDIndex]) { - anyIDChanged = true; - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - if (userIDIndex == 0) { - sGen.setHashedSubpackets(hashedPacketsGen.generate()); - sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); - } - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - if (!saveParcel.newIDs[userIDIndex]) { - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID); - } - masterPublicKey = - PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - userIDIndex++; - } - } - - ArrayList> sigList = new ArrayList>(); - if (saveParcel.moddedKeys[0]) { - userIDIndex = 0; - for (String userId : saveParcel.userIds) { - String origID = saveParcel.originalIDs.get(userIDIndex); - if (!(origID.equals(saveParcel.originalPrimaryID) && !saveParcel.primaryIDChanged)) { - Iterator sigs = masterPublicKey.getSignaturesForID(userId); - // TODO: make sure this iterator only has signatures we are interested in - while (sigs.hasNext()) { - PGPSignature sig = sigs.next(); - sigList.add(new Pair(userId, sig)); - } - } - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, userId); - userIDIndex++; - } - anyIDChanged = true; - } - - //update the keyring with the new ID information - if (anyIDChanged) { - pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); - mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); - } - - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - updateProgress(R.string.progress_building_master_key, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encryptor based on old passphrase, as some keys may be unchanged - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassphrase.toCharArray()); - - //this generates one more signature than necessary... - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - for (int i = 1; i < saveParcel.keys.size(); ++i) { - updateProgress(40 + 50 * i / saveParcel.keys.size(), 100); - if (saveParcel.moddedKeys[i]) { - PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal(); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2; - if (saveParcel.newKeys[i]) { - keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - "".toCharArray()); - } else { - keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassphrase.toCharArray()); - } - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - usageId = saveParcel.keysUsages.get(i); - canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this - if (canSign) { - Date todayDate = new Date(); //both sig times the same - // cross-certify signing keys - hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time - PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - subPublicKey.getAlgorithm(), PGPUtil.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); - PGPSignature certification = sGen.generateCertification(masterPublicKey, - subPublicKey); - unhashedPacketsGen.setEmbeddedSignature(false, certification); - } - hashedPacketsGen.setKeyFlags(false, usageId); - - if (saveParcel.keysExpiryDates.get(i) != null) { - Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationDate.setTime(subPublicKey.getCreationTime()); - Calendar expiryDate = saveParcel.keysExpiryDates.get(i); - // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - // here we purposefully ignore partial days in each date - long type has - // no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); - } - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); - // do this explicitly, although since we're rebuilding, - // this happens anyway - } - - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - // certifications will be discarded if the key is changed, because I think, for a start, - // they will be invalid. Binding certs are regenerated anyway, and other certs which - // need to be kept are on IDs and attributes - // TODO: don't let revoked keys be edited, other than removed - changing one would - // result in the revocation being wrong? - } - } - - PGPSecretKeyRing updatedSecretKeyRing = keyGen.generateSecretKeyRing(); - //finally, update the keyrings - Iterator itr = updatedSecretKeyRing.getSecretKeys(); - while (itr.hasNext()) { - PGPSecretKey theNextKey = itr.next(); - if ((theNextKey.isMasterKey() && saveParcel.moddedKeys[0]) || !theNextKey.isMasterKey()) { - mKR = PGPSecretKeyRing.insertSecretKey(mKR, theNextKey); - pKR = PGPPublicKeyRing.insertPublicKey(pKR, theNextKey.getPublicKey()); - } - } - - //replace lost IDs - if (saveParcel.moddedKeys[0]) { - masterPublicKey = mKR.getPublicKey(); - for (Pair toAdd : sigList) { - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second); - } - pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); - mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); - } - - // Build key encryptor based on new passphrase - PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassphrase.toCharArray()); - - //update the passphrase - mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew); - - /* additional handy debug info - - Log.d(Constants.TAG, " ------- in private key -------"); - - for(String uid : new IterableIterator(secretKeyRing.getPublicKey().getUserIDs())) { - for(PGPSignature sig : new IterableIterator( - secretKeyRing.getPublicKey().getSignaturesForId(uid))) { - Log.d(Constants.TAG, "sig: " + - PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); - } - - } - - Log.d(Constants.TAG, " ------- in public key -------"); - - for(String uid : new IterableIterator(publicKeyRing.getPublicKey().getUserIDs())) { - for(PGPSignature sig : new IterableIterator( - publicKeyRing.getPublicKey().getSignaturesForId(uid))) { - Log.d(Constants.TAG, "sig: " + - PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); - } - } - - */ - - return new Pair(new UncachedKeyRing(pKR), - new UncachedKeyRing(mKR)); - - } - public Pair buildSecretKey(PGPSecretKeyRing sKR, PGPPublicKeyRing pKR, SaveKeyringParcel saveParcel, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 3ddcdfcf4..d3f46a7a4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -44,7 +44,6 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.pgp.UncachedSecretKey; import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; @@ -87,9 +86,6 @@ public class KeychainIntentService extends IntentService public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY"; public static final String ACTION_SAVE_KEYRING = Constants.INTENT_PREFIX + "SAVE_KEYRING"; - public static final String ACTION_GENERATE_KEY = Constants.INTENT_PREFIX + "GENERATE_KEY"; - public static final String ACTION_GENERATE_DEFAULT_RSA_KEYS = Constants.INTENT_PREFIX - + "GENERATE_DEFAULT_RSA_KEYS"; public static final String ACTION_DELETE_FILE_SECURELY = Constants.INTENT_PREFIX + "DELETE_FILE_SECURELY"; @@ -127,14 +123,7 @@ public class KeychainIntentService extends IntentService // save keyring public static final String SAVE_KEYRING_PARCEL = "save_parcel"; - public static final String SAVE_KEYRING_CAN_SIGN = "can_sign"; - - - // generate key - public static final String GENERATE_KEY_ALGORITHM = "algorithm"; - public static final String GENERATE_KEY_KEY_SIZE = "key_size"; - public static final String GENERATE_KEY_SYMMETRIC_PASSPHRASE = "passphrase"; - public static final String GENERATE_KEY_MASTER_KEY = "master_key"; + public static final String SAVE_KEYRING_PASSPHRASE = "passphrase"; // delete file securely public static final String DELETE_FILE = "deleteFile"; @@ -164,9 +153,6 @@ public class KeychainIntentService extends IntentService /* * possible data keys as result send over messenger */ - // keys - public static final String RESULT_NEW_KEY = "new_key"; - public static final String RESULT_KEY_USAGES = "new_key_usages"; // encrypt public static final String RESULT_BYTES = "encrypted_data"; @@ -490,136 +476,38 @@ public class KeychainIntentService extends IntentService } else if (ACTION_SAVE_KEYRING.equals(action)) { try { /* Input */ - OldSaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); - String oldPassphrase = saveParcel.oldPassphrase; - String newPassphrase = saveParcel.newPassphrase; - boolean canSign = true; - - if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) { - canSign = data.getBoolean(SAVE_KEYRING_CAN_SIGN); - } - - if (newPassphrase == null) { - newPassphrase = oldPassphrase; - } - - long masterKeyId = saveParcel.keys.get(0).getKeyId(); + SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL); + long masterKeyId = saveParcel.mMasterKeyId; /* Operation */ ProviderHelper providerHelper = new ProviderHelper(this); - if (!canSign) { - setProgress(R.string.progress_building_key, 0, 100); - WrappedSecretKeyRing keyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); - UncachedKeyRing newKeyRing = - keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase); - setProgress(R.string.progress_saving_key_ring, 50, 100); - providerHelper.saveSecretKeyRing(newKeyRing); - setProgress(R.string.progress_done, 100, 100); - } else { - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); - try { - WrappedSecretKeyRing seckey = providerHelper.getWrappedSecretKeyRing(masterKeyId); - WrappedPublicKeyRing pubkey = providerHelper.getWrappedPublicKeyRing(masterKeyId); + PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100)); + try { + String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); + WrappedSecretKeyRing secRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); - PgpKeyOperation.Pair pair = - keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing - setProgress(R.string.progress_saving_key_ring, 90, 100); - providerHelper.saveKeyRing(pair.first, pair.second); - } catch (ProviderHelper.NotFoundException e) { - PgpKeyOperation.Pair pair = - keyOperations.buildNewSecretKey(saveParcel); //new Keyring - // save the pair - setProgress(R.string.progress_saving_key_ring, 90, 100); - providerHelper.saveKeyRing(pair.first, pair.second); - } - - setProgress(R.string.progress_done, 100, 100); + UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + setProgress(R.string.progress_saving_key_ring, 90, 100); + providerHelper.saveSecretKeyRing(ring); + } catch (ProviderHelper.NotFoundException e) { + // UncachedKeyRing ring = keyOperations.(saveParcel); //new Keyring + // save the pair + setProgress(R.string.progress_saving_key_ring, 90, 100); + // providerHelper.saveSecretKeyRing(ring); + sendErrorToHandler(e); + } + + setProgress(R.string.progress_done, 100, 100); + + if (saveParcel.newPassphrase != null) { + PassphraseCacheService.addCachedPassphrase(this, masterKeyId, saveParcel.newPassphrase); } - PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase); /* Output */ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); } catch (Exception e) { sendErrorToHandler(e); } - } else if (ACTION_GENERATE_KEY.equals(action)) { - try { - /* Input */ - int algorithm = data.getInt(GENERATE_KEY_ALGORITHM); - String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - int keysize = data.getInt(GENERATE_KEY_KEY_SIZE); - boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY); - - /* Operation */ - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); - byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey); - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, newKey); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - } else if (ACTION_GENERATE_DEFAULT_RSA_KEYS.equals(action)) { - // generate one RSA 4096 key for signing and one subkey for encrypting! - try { - /* Input */ - String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - ArrayList keyUsageList = new ArrayList(); - - /* Operation */ - int keysTotal = 3; - int keysCreated = 0; - setProgress( - getApplicationContext().getResources(). - getQuantityString(R.plurals.progress_generating, keysTotal), - keysCreated, - keysTotal); - PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - byte[] buf; - - buf = keyOperations.createKey(Constants.choice.algorithm.rsa, - 4096, passphrase, true); - os.write(buf); - keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER); - keysCreated++; - setProgress(keysCreated, keysTotal); - - buf = keyOperations.createKey(Constants.choice.algorithm.rsa, - 4096, passphrase, false); - os.write(buf); - keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE); - keysCreated++; - setProgress(keysCreated, keysTotal); - - buf = keyOperations.createKey(Constants.choice.algorithm.rsa, - 4096, passphrase, false); - os.write(buf); - keyUsageList.add(UncachedSecretKey.SIGN_DATA); - keysCreated++; - setProgress(keysCreated, keysTotal); - - // TODO: default to one master for cert, one sub for encrypt and one sub - // for sign - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray()); - resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) { try { /* Input */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java deleted file mode 100644 index b722393ad..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OldSaveKeyringParcel.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2014 Ash Hughes - * - * 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.service; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; -import org.sufficientlysecure.keychain.pgp.UncachedSecretKey; -import org.sufficientlysecure.keychain.util.IterableIterator; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Calendar; - -/** Class for parcelling data between ui and services. - * This class is outdated and scheduled for removal, pending a rewrite of the - * EditKeyActivity and save keyring routines. - */ -@Deprecated -public class OldSaveKeyringParcel implements Parcelable { - - public ArrayList userIds; - public ArrayList originalIDs; - public ArrayList deletedIDs; - public boolean[] newIDs; - public boolean primaryIDChanged; - public boolean[] moddedKeys; - public ArrayList deletedKeys; - public ArrayList keysExpiryDates; - public ArrayList keysUsages; - public String newPassphrase; - public String oldPassphrase; - public boolean[] newKeys; - public ArrayList keys; - public String originalPrimaryID; - - public OldSaveKeyringParcel() {} - - private OldSaveKeyringParcel(Parcel source) { - userIds = (ArrayList) source.readSerializable(); - originalIDs = (ArrayList) source.readSerializable(); - deletedIDs = (ArrayList) source.readSerializable(); - newIDs = source.createBooleanArray(); - primaryIDChanged = source.readByte() != 0; - moddedKeys = source.createBooleanArray(); - byte[] tmp = source.createByteArray(); - if (tmp == null) { - deletedKeys = null; - } else { - deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp); - } - keysExpiryDates = (ArrayList) source.readSerializable(); - keysUsages = source.readArrayList(Integer.class.getClassLoader()); - newPassphrase = source.readString(); - oldPassphrase = source.readString(); - newKeys = source.createBooleanArray(); - keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); - originalPrimaryID = source.readString(); - } - - @Override - public void writeToParcel(Parcel destination, int flags) { - destination.writeSerializable(userIds); //might not be the best method to store. - destination.writeSerializable(originalIDs); - destination.writeSerializable(deletedIDs); - destination.writeBooleanArray(newIDs); - destination.writeByte((byte) (primaryIDChanged ? 1 : 0)); - destination.writeBooleanArray(moddedKeys); - destination.writeByteArray(encodeArrayList(deletedKeys)); - destination.writeSerializable(keysExpiryDates); - destination.writeList(keysUsages); - destination.writeString(newPassphrase); - destination.writeString(oldPassphrase); - destination.writeBooleanArray(newKeys); - destination.writeByteArray(encodeArrayList(keys)); - destination.writeString(originalPrimaryID); - } - - public static final Creator CREATOR = new Creator() { - public OldSaveKeyringParcel createFromParcel(final Parcel source) { - return new OldSaveKeyringParcel(source); - } - - public OldSaveKeyringParcel[] newArray(final int size) { - return new OldSaveKeyringParcel[size]; - } - }; - - private static byte[] encodeArrayList(ArrayList list) { - if(list.isEmpty()) { - return null; - } - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - for(UncachedSecretKey key : new IterableIterator(list.iterator())) { - try { - key.encodeSecretKey(os); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); - } - } - return os.toByteArray(); - } - - @Override - public int describeContents() { - return 0; - } -} 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 3514ab2e5..ca7da5ae3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -23,7 +23,7 @@ import java.util.HashMap; public class SaveKeyringParcel implements Parcelable { // the master key id to be edited - private final long mMasterKeyId; + public final long mMasterKeyId; // the key fingerprint, for safety private final byte[] mFingerprint; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index bcb2286ab..4309e3505 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -57,7 +57,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; @@ -199,13 +198,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // generate key if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { + /* boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); if (generateDefaultKeys) { - // Send all information needed to service generate keys in other thread - final Intent serviceIntent = new Intent(this, KeychainIntentService.class); - serviceIntent.setAction(KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); - // fill values for this action Bundle data = new Bundle(); data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, @@ -265,6 +261,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // start service with intent startService(serviceIntent); } + */ } } else { buildLayout(false); @@ -547,6 +544,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } private void finallySaveClicked() { + /* try { // Send all information needed to service to edit key in other thread Intent intent = new Intent(this, KeychainIntentService.class); @@ -609,6 +607,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show(); } + */ } private void cancelClicked() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 3e8e18ce5..b7336318f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -346,13 +346,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } private void createKey() { - // Send all information needed to service to edit key in other thread - final Intent intent = new Intent(mActivity, KeychainIntentService.class); - - intent.setAction(KeychainIntentService.ACTION_GENERATE_KEY); // fill values for this action - Bundle data = new Bundle(); Boolean isMasterKey; String passphrase; @@ -365,6 +360,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor passphrase = ""; isMasterKey = true; } + /* data.putBoolean(KeychainIntentService.GENERATE_KEY_MASTER_KEY, isMasterKey); data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase); data.putInt(KeychainIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId()); @@ -410,6 +406,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor // start service with intent mActivity.startService(intent); + */ + } private void addGeneratedKeyToView(UncachedSecretKey newKey) { From 5c47143d64e8ab2e1ef00f5134d9362c08e6f529 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 20:04:25 +0200 Subject: [PATCH 42/60] new-edit: add new save keyring stuff --- .../keychain/pgp/PgpKeyOperation.java | 282 ++++++++---------- .../exception/PgpGeneralMsgIdException.java | 5 + .../keychain/service/SaveKeyringParcel.java | 6 +- 3 files changed, 133 insertions(+), 160 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 e9d8fd295..e08c96dad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -171,173 +171,139 @@ public class PgpKeyOperation { return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), sha1Calc, isMasterKey, keyEncryptor); } catch(NoSuchProviderException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new RuntimeException(e); } catch(NoSuchAlgorithmException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new RuntimeException(e); } catch(InvalidAlgorithmParameterException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new RuntimeException(e); } catch(PGPException e) { - throw new PgpGeneralMsgIdException(R.string.error_encoding, e); + throw new PgpGeneralMsgIdException(R.string.msg_mr_error_pgp, e); } } - public Pair buildSecretKey(PGPSecretKeyRing sKR, - PGPPublicKeyRing pKR, - SaveKeyringParcel saveParcel, - String passphrase) + /** This method introduces a list of modifications specified by a SaveKeyringParcel to a + * PGPSecretKeyRing. + * + * Note that PGPPublicKeyRings can not be directly modified. Instead, the corresponding + * PGPSecretKeyRing must be modified and consequently consolidated with its public counterpart. + * This is a natural workflow since pgp keyrings are immutable data structures: Old semantics + * are changed by adding new certificates, which implicitly override older certificates. + * + */ + public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, + String passphrase) throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { - updateProgress(R.string.progress_building_key, 0, 100); - - // sort these, so we can use binarySearch later on - Arrays.sort(saveParcel.revokeSubKeys); - Arrays.sort(saveParcel.revokeUserIds); - /* - * What's gonna happen here: - * * 1. Unlock private key - * - * 2. Create new secret key ring - * - * 3. Copy subkeys - * - Generate revocation if requested - * - Copy old cert, or generate new if change requested - * - * 4. Generate and add new subkeys - * - * 5. Copy user ids - * - Generate revocation if requested - * - Copy old cert, or generate new if primary user id status changed - * - * 6. Add new user ids - * - * 7. Generate PublicKeyRing from SecretKeyRing - * - * 8. Return pair (PublicKeyRing,SecretKeyRing) - * + * 2a. Add certificates for new user ids + * 2b. Add revocations for revoked user ids + * 3. If primary user id changed, generate new certificates for both old and new + * 4a. For each subkey change, generate new subkey binding certificate + * 4b. For each subkey revocation, generate new subkey revocation certificate + * 5. Generate and add new subkeys + * 6. If requested, change passphrase */ - // 1. Unlock private key updateProgress(R.string.progress_building_key, 0, 100); + // We work on bouncycastle object level here + PGPSecretKeyRing sKR = wsKR.getRing(); PGPPublicKey masterPublicKey = sKR.getPublicKey(); + PGPSecretKey masterSecretKey = sKR.getSecretKey(); + + // 1. Unlock private key PGPPrivateKey masterPrivateKey; { - PGPSecretKey masterKey = sKR.getSecretKey(); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); + masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + } + if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) { + return null; } - // 2. Create new secret key ring updateProgress(R.string.progress_certifying_master_key, 20, 100); - // Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff - // we want to do manually. Instead, we simply use a list of secret keys. - ArrayList secretKeys = new ArrayList(); - ArrayList publicKeys = new ArrayList(); + { // work on master secret key - // 3. Copy subkeys - // - Generate revocation if requested - // - Copy old cert, or generate new if change requested - for (PGPSecretKey sKey : new IterableIterator(sKR.getSecretKeys())) { - PGPPublicKey pKey = sKey.getPublicKey(); - if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) { - // add revocation signature to key, if there is none yet - if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) { - // generate revocation signature - } + PGPPublicKey modifiedPublicKey = masterPublicKey; + + // 2a. Add certificates for new user ids + for (String userId : saveParcel.addUserIds) { + PGPSignature cert = generateUserIdSignature(masterPrivateKey, + masterPublicKey, userId, false); + modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); } - if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) { - // change subkey flags? - SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID()); - // remove old subkey binding signature(s?) - for (PGPSignature sig : new IterableIterator( - pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) { - pKey = PGPPublicKey.removeCertification(pKey, sig); - } - // generate and add new signature - PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, change.mFlags, change.mExpiry, passphrase); - pKey = PGPPublicKey.addCertification(pKey, sig); - } - secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey)); - publicKeys.add(pKey); - } - - // 4. Generate and add new subkeys - // TODO - - // 5. Copy user ids - for (String userId : new IterableIterator(masterPublicKey.getUserIDs())) { - // - Copy old cert, or generate new if primary user id status changed - boolean certified = false, revoked = false; - for (PGPSignature sig : new IterableIterator( - masterPublicKey.getSignaturesForID(userId))) { - // We know there are only revocation and certification types in here. - switch(sig.getSignatureType()) { - case PGPSignature.CERTIFICATION_REVOCATION: - revoked = true; - continue; - - case PGPSignature.DEFAULT_CERTIFICATION: - case PGPSignature.NO_CERTIFICATION: - case PGPSignature.CASUAL_CERTIFICATION: - case PGPSignature.POSITIVE_CERTIFICATION: - // Already got one? Remove this one, then. - if (certified) { - masterPublicKey = PGPPublicKey.removeCertification( - masterPublicKey, userId, sig); - continue; - } - boolean primary = userId.equals(saveParcel.changePrimaryUserId); - // Generate a new one under certain circumstances - if (saveParcel.changePrimaryUserId != null && - sig.getHashedSubPackets().isPrimaryUserID() != primary) { - PGPSignature cert = generateUserIdSignature( - masterPrivateKey, masterPublicKey, userId, primary); - PGPPublicKey.addCertification(masterPublicKey, userId, cert); - } - certified = true; - } - } - // - Generate revocation if requested - if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) { + // 2b. Add revocations for revoked user ids + for (String userId : saveParcel.revokeUserIds) { PGPSignature cert = generateRevocationSignature(masterPrivateKey, masterPublicKey, userId); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); + modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); + } + + // 3. If primary user id changed, generate new certificates for both old and new + if (saveParcel.changePrimaryUserId != null) { + // todo + } + + // Update the secret key ring + if (modifiedPublicKey != masterPublicKey) { + masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey); + masterPublicKey = modifiedPublicKey; + sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); + } + + } + + + // 4a. For each subkey change, generate new subkey binding certificate + for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { + PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); + if (sKey == null) { + return null; + } + PGPPublicKey pKey = sKey.getPublicKey(); + + // generate and add new signature + PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, change.mFlags, change.mExpiry, passphrase); + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } + + // 4b. For each subkey change, generate new subkey binding certificate + for (long revocation : saveParcel.revokeSubKeys) { + PGPSecretKey sKey = sKR.getSecretKey(revocation); + if (sKey == null) { + return null; + } + PGPPublicKey pKey = sKey.getPublicKey(); + + // generate and add new signature + PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); + + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } + + // 5. Generate and add new subkeys + for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { + try { + PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); + PGPPublicKey pKey = sKey.getPublicKey(); + PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, add.mFlags, add.mExpiry, passphrase); + + pKey = PGPPublicKey.addCertification(pKey, cert); + sKey = PGPSecretKey.replacePublicKey(sKey, pKey); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } catch (PgpGeneralMsgIdException e) { + return null; } } - // 6. Add new user ids - for(String userId : saveParcel.addUserIds) { - PGPSignature cert = generateUserIdSignature(masterPrivateKey, - masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId)); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); - } - - // 7. Generate PublicKeyRing from SecretKeyRing - updateProgress(R.string.progress_building_master_key, 30, 100); - PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys); - - // Copy all non-self uid certificates - for (String userId : new IterableIterator(masterPublicKey.getUserIDs())) { - // - Copy old cert, or generate new if primary user id status changed - boolean certified = false, revoked = false; - for (PGPSignature sig : new IterableIterator( - masterPublicKey.getSignaturesForID(userId))) { - } - } - - for (PGPPublicKey newKey : publicKeys) { - PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID()); - for (PGPSignature sig : new IterableIterator( - oldKey.getSignatures())) { - } - } - - // If requested, set new passphrase + // 6. If requested, change passphrase if (saveParcel.newPassphrase != null) { PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() .get(HashAlgorithmTags.SHA1); @@ -352,9 +318,7 @@ public class PgpKeyOperation { sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); } - // 8. Return pair (PublicKeyRing,SecretKeyRing) - - return new Pair(sKR, pKR); + return new UncachedKeyRing(sKR); } @@ -390,10 +354,29 @@ public class PgpKeyOperation { return sGen.generateCertification(userId, pKey); } + private static PGPSignature generateRevocationSignature( + PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey) + throws IOException, PGPException, SignatureException { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + pKey.getAlgorithm(), PGPUtil.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); + subHashedPacketsGen.setSignatureCreationTime(false, new Date()); + sGen.setHashedSubpackets(subHashedPacketsGen.generate()); + // Generate key revocation or subkey revocation, depending on master/subkey-ness + if (masterPublicKey.getKeyID() == pKey.getKeyID()) { + sGen.init(PGPSignature.KEY_REVOCATION, masterPrivateKey); + return sGen.generateCertification(masterPublicKey); + } else { + sGen.init(PGPSignature.SUBKEY_REVOCATION, masterPrivateKey); + return sGen.generateCertification(masterPublicKey, pKey); + } + } + private static PGPSignature generateSubkeyBindingSignature( PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - PGPSecretKey sKey, PGPPublicKey pKey, - int flags, Long expiry, String passphrase) + PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase) throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException { // date for signing @@ -510,19 +493,4 @@ public class PgpKeyOperation { return publicKey; } - /** - * Simple static subclass that stores two values. - *

- * This is only used to return a pair of values in one function above. We specifically don't use - * com.android.Pair to keep this class free from android dependencies. - */ - public static class Pair { - public final K first; - public final V second; - - public Pair(K first, V second) { - this.first = first; - this.second = second; - } - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java index 3b88897ed..3700b4c34 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java @@ -30,6 +30,11 @@ public class PgpGeneralMsgIdException extends Exception { mMessageId = messageId; } + public PgpGeneralMsgIdException(int messageId, Throwable cause) { + super("msg[" + messageId + "]", cause); + mMessageId = messageId; + } + public PgpGeneralException getContextualized(Context context) { return new PgpGeneralException(context.getString(mMessageId), this); } 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 ca7da5ae3..c68b7c189 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -25,14 +25,14 @@ public class SaveKeyringParcel implements Parcelable { // the master key id to be edited public final long mMasterKeyId; // the key fingerprint, for safety - private final byte[] mFingerprint; + public final byte[] mFingerprint; public String newPassphrase; public String[] addUserIds; public SubkeyAdd[] addSubKeys; - public HashMap changeSubKeys; + public SubkeyChange[] changeSubKeys; public String changePrimaryUserId; public String[] revokeUserIds; @@ -76,7 +76,7 @@ public class SaveKeyringParcel implements Parcelable { addUserIds = source.createStringArray(); addSubKeys = (SubkeyAdd[]) source.readSerializable(); - changeSubKeys = (HashMap) source.readSerializable(); + changeSubKeys = (SubkeyChange[]) source.readSerializable(); changePrimaryUserId = source.readString(); revokeUserIds = source.createStringArray(); From 4bff50bffc43c23e08f87ff7c5b19fe790874d01 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 20:40:26 +0200 Subject: [PATCH 43/60] new-edit: add logging to modifySecretKeyRing operation --- .../keychain/pgp/PgpKeyOperation.java | 196 +++++++++++------- .../service/KeychainIntentService.java | 5 +- .../service/OperationResultParcel.java | 22 ++ OpenKeychain/src/main/res/values/strings.xml | 19 ++ 4 files changed, 164 insertions(+), 78 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 e08c96dad..bbdbfffd2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -47,6 +47,9 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel; +import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Primes; @@ -98,12 +101,6 @@ public class PgpKeyOperation { } } - void updateProgress(int current, int total) { - if (mProgress != null) { - mProgress.setProgress(current, total); - } - } - /** Creates new secret key. */ private PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase, boolean isMasterKey) throws PgpGeneralMsgIdException { @@ -182,7 +179,9 @@ public class PgpKeyOperation { } /** This method introduces a list of modifications specified by a SaveKeyringParcel to a - * PGPSecretKeyRing. + * WrappedSecretKeyRing. + * + * This method relies on WrappedSecretKeyRing's canonicalization property! * * Note that PGPPublicKeyRings can not be directly modified. Instead, the corresponding * PGPSecretKeyRing must be modified and consequently consolidated with its public counterpart. @@ -191,8 +190,7 @@ public class PgpKeyOperation { * */ public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, - String passphrase) - throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException { + String passphrase, OperationLog log, int indent) { /* * 1. Unlock private key @@ -205,6 +203,8 @@ public class PgpKeyOperation { * 6. If requested, change passphrase */ + log.add(LogLevel.START, LogType.MSG_MR, indent); + indent += 1; updateProgress(R.string.progress_building_key, 0, 100); // We work on bouncycastle object level here @@ -213,10 +213,16 @@ public class PgpKeyOperation { PGPSecretKey masterSecretKey = sKR.getSecretKey(); // 1. Unlock private key + log.add(LogLevel.DEBUG, LogType.MSG_MR_UNLOCK, indent); PGPPrivateKey masterPrivateKey; { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_UNLOCK_ERROR, indent+1); + return null; + } } if (!Arrays.equals(saveParcel.mFingerprint, sKR.getPublicKey().getFingerprint())) { return null; @@ -224,12 +230,14 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); - { // work on master secret key + // work on master secret key + try { PGPPublicKey modifiedPublicKey = masterPublicKey; // 2a. Add certificates for new user ids for (String userId : saveParcel.addUserIds) { + log.add(LogLevel.INFO, LogType.MSG_MR_UID_ADD, indent); PGPSignature cert = generateUserIdSignature(masterPrivateKey, masterPublicKey, userId, false); modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); @@ -237,6 +245,7 @@ public class PgpKeyOperation { // 2b. Add revocations for revoked user ids for (String userId : saveParcel.revokeUserIds) { + log.add(LogLevel.INFO, LogType.MSG_MR_UID_REVOKE, indent); PGPSignature cert = generateRevocationSignature(masterPrivateKey, masterPublicKey, userId); modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert); @@ -244,6 +253,7 @@ public class PgpKeyOperation { // 3. If primary user id changed, generate new certificates for both old and new if (saveParcel.changePrimaryUserId != null) { + log.add(LogLevel.INFO, LogType.MSG_MR_UID_PRIMARY, indent); // todo } @@ -254,70 +264,105 @@ public class PgpKeyOperation { sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); } - } - // 4a. For each subkey change, generate new subkey binding certificate - for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { - PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); - if (sKey == null) { - return null; - } - PGPPublicKey pKey = sKey.getPublicKey(); - - // generate and add new signature - PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, change.mFlags, change.mExpiry, passphrase); - pKey = PGPPublicKey.addCertification(pKey, sig); - sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); - } - - // 4b. For each subkey change, generate new subkey binding certificate - for (long revocation : saveParcel.revokeSubKeys) { - PGPSecretKey sKey = sKR.getSecretKey(revocation); - if (sKey == null) { - return null; - } - PGPPublicKey pKey = sKey.getPublicKey(); - - // generate and add new signature - PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); - - pKey = PGPPublicKey.addCertification(pKey, sig); - sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); - } - - // 5. Generate and add new subkeys - for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { - try { - PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); + for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) { + log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_CHANGE, + new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent); + PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId); + if (sKey == null) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING, + new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1); + return null; + } PGPPublicKey pKey = sKey.getPublicKey(); - PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, - sKey, pKey, add.mFlags, add.mExpiry, passphrase); - pKey = PGPPublicKey.addCertification(pKey, cert); - sKey = PGPSecretKey.replacePublicKey(sKey, pKey); + if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, + new String[]{PgpKeyHelper.convertKeyIdToHex(change.mKeyId)}, indent + 1); + return null; + } + + // generate and add new signature + PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, change.mFlags, change.mExpiry, passphrase); + pKey = PGPPublicKey.addCertification(pKey, sig); sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); - } catch (PgpGeneralMsgIdException e) { - return null; } + + // 4b. For each subkey revocation, generate new subkey revocation certificate + for (long revocation : saveParcel.revokeSubKeys) { + log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_REVOKE, + new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent); + PGPSecretKey sKey = sKR.getSecretKey(revocation); + if (sKey == null) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_MISSING, + new String[] { PgpKeyHelper.convertKeyIdToHex(revocation) }, indent+1); + return null; + } + PGPPublicKey pKey = sKey.getPublicKey(); + + // generate and add new signature + PGPSignature sig = generateRevocationSignature(masterPublicKey, masterPrivateKey, pKey); + + pKey = PGPPublicKey.addCertification(pKey, sig); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } + + // 5. Generate and add new subkeys + for (SaveKeyringParcel.SubkeyAdd add : saveParcel.addSubKeys) { + try { + + if (add.mExpiry != null && new Date(add.mExpiry).before(new Date())) { + log.add(LogLevel.ERROR, LogType.MSG_MR_SUBKEY_PAST_EXPIRY, indent +1); + return null; + } + + log.add(LogLevel.INFO, LogType.MSG_MR_SUBKEY_NEW, indent); + PGPSecretKey sKey = createKey(add.mAlgorithm, add.mKeysize, passphrase, false); + log.add(LogLevel.DEBUG, LogType.MSG_MR_SUBKEY_NEW_ID, + new String[] { PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()) }, indent+1); + + PGPPublicKey pKey = sKey.getPublicKey(); + PGPSignature cert = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, + sKey, pKey, add.mFlags, add.mExpiry, passphrase); + pKey = PGPPublicKey.addCertification(pKey, cert); + sKey = PGPSecretKey.replacePublicKey(sKey, pKey); + sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey)); + } catch (PgpGeneralMsgIdException e) { + return null; + } + } + + // 6. If requested, change passphrase + if (saveParcel.newPassphrase != null) { + log.add(LogLevel.INFO, LogType.MSG_MR_PASSPHRASE, indent); + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() + .get(HashAlgorithmTags.SHA1); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + // Build key encryptor based on new passphrase + PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + saveParcel.newPassphrase.toCharArray()); + + sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); + } + + // This one must only be thrown by + } catch (IOException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_ENCODE, indent+1); + return null; + } catch (PGPException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_PGP, indent+1); + return null; + } catch (SignatureException e) { + log.add(LogLevel.ERROR, LogType.MSG_MR_ERROR_SIG, indent+1); + return null; } - // 6. If requested, change passphrase - if (saveParcel.newPassphrase != null) { - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build() - .get(HashAlgorithmTags.SHA1); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - // Build key encryptor based on new passphrase - PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassphrase.toCharArray()); - - sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); - } - + log.add(LogLevel.OK, LogType.MSG_MR_SUCCESS, indent); return new UncachedKeyRing(sKR); } @@ -377,7 +422,7 @@ public class PgpKeyOperation { private static PGPSignature generateSubkeyBindingSignature( PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase) - throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException { + throws IOException, PGPException, SignatureException { // date for signing Date todayDate = new Date(); @@ -414,13 +459,10 @@ public class PgpKeyOperation { if (expiry != null) { Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC")); creationDate.setTime(pKey.getCreationTime()); - // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - // here we purposefully ignore partial days in each date - long type has - // no fractional part! - long numDays = (expiry / 86400000) - - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) { - throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation); + + // (Just making sure there's no programming error here, this MUST have been checked above!) + if (new Date(expiry).before(todayDate)) { + throw new RuntimeException("Bad subkey creation date, this is a bug!"); } hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis()); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index d3f46a7a4..25a9387f4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -486,7 +487,9 @@ public class KeychainIntentService extends IntentService String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); WrappedSecretKeyRing secRing = providerHelper.getWrappedSecretKeyRing(masterKeyId); - UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + OperationLog log = new OperationLog(); + UncachedKeyRing ring = keyOperations.modifySecretKeyRing(secRing, saveParcel, + passphrase, log, 0); setProgress(R.string.progress_saving_key_ring, 90, 100); providerHelper.saveSecretKeyRing(ring); } catch (ProviderHelper.NotFoundException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 0760aadf8..73f552c92 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -215,6 +215,24 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + + MSG_MR (R.string.msg_mr), + MSG_MR_ERROR_ENCODE (R.string.msg_mr_error_encode), + MSG_MR_ERROR_PGP (R.string.msg_mr_error_pgp), + MSG_MR_ERROR_SIG (R.string.msg_mr_error_sig), + MSG_MR_PASSPHRASE (R.string.msg_mr_passphrase), + MSG_MR_SUBKEY_CHANGE (R.string.msg_mr_subkey_change), + MSG_MR_SUBKEY_MISSING (R.string.msg_mr_subkey_missing), + MSG_MR_SUBKEY_NEW_ID (R.string.msg_mr_subkey_new_id), + MSG_MR_SUBKEY_NEW (R.string.msg_mr_subkey_new), + MSG_MR_SUBKEY_PAST_EXPIRY (R.string.msg_mr_subkey_past_expiry), + MSG_MR_SUBKEY_REVOKE (R.string.msg_mr_subkey_revoke), + MSG_MR_SUCCESS (R.string.msg_mr_success), + MSG_MR_UID_ADD (R.string.msg_mr_uid_add), + MSG_MR_UID_PRIMARY (R.string.msg_mr_uid_primary), + MSG_MR_UID_REVOKE (R.string.msg_mr_uid_revoke), + MSG_MR_UNLOCK_ERROR (R.string.msg_mr_unlock_error), + MSG_MR_UNLOCK (R.string.msg_mr_unlock), ; private final int mMsgId; @@ -264,6 +282,10 @@ public class OperationResultParcel implements Parcelable { add(new OperationResultParcel.LogEntryParcel(level, type, parameters, indent)); } + public void add(LogLevel level, LogType type, int indent) { + add(new OperationResultParcel.LogEntryParcel(level, type, null, indent)); + } + public boolean containsWarnings() { for(LogEntryParcel entry : new IterableIterator(iterator())) { if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) { diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index ed06f983b..bf3333458 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -600,6 +600,25 @@ Removing outdated revocation certificate for user id "%s" No valid self-certificate found for user id %s, removing from ring + + Modifying keyring %s + Encoding exception! + PGP internal exception! + Signature exception! + Changing passphrase + Modifying subkey %s + Tried to operate on missing subkey %s! + Generating new %1$s bit %2$s subkey + New subkey id: %s + Expiry date cannot be in the past! + Revoking subkey %s + Keyring successfully modified + Adding user id %s + Changing primary uid to %s + Revoking user id %s + Error unlocking keyring! + Unlocking keyring + Certifier Certificate Details From 6e4d0dede1517b25dd51cdea23aa81537249c0f8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 23:25:45 +0200 Subject: [PATCH 44/60] canonicalize: support secret keys --- .../keychain/pgp/UncachedKeyRing.java | 36 ++++++++++++------- .../keychain/provider/ProviderHelper.java | 6 ++++ .../service/OperationResultParcel.java | 4 ++- OpenKeychain/src/main/res/values/strings.xml | 4 ++- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index f22ea7697..78620405f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -178,8 +178,7 @@ public class UncachedKeyRing { return result; } - /** "Canonicalizes" a key, removing inconsistencies in the process. This operation can be - * applied to public keyrings only. + /** "Canonicalizes" a key, removing inconsistencies in the process. * * More specifically: * - Remove all non-verifying self-certificates @@ -193,6 +192,7 @@ public class UncachedKeyRing { * - certifications and certification revocations for user ids * - If a subkey retains no valid subkey binding certificate, remove it * - If a user id retains no valid self certificate, remove it + * - If the key is a secret key, remove all certificates by foreign keys * * This operation writes an OperationLog which can be used as part of a OperationResultParcel. * @@ -200,12 +200,8 @@ public class UncachedKeyRing { * */ public UncachedKeyRing canonicalize(OperationLog log, int indent) { - if (isSecret()) { - throw new RuntimeException("Tried to canonicalize non-secret keyring. " + - "This is a programming error and should never happen!"); - } - log.add(LogLevel.START, LogType.MSG_KC, + log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC, new String[]{PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())}, indent); indent += 1; @@ -213,7 +209,7 @@ public class UncachedKeyRing { int redundantCerts = 0, badCerts = 0; - PGPPublicKeyRing ring = (PGPPublicKeyRing) mRing; + PGPKeyRing ring = mRing; PGPPublicKey masterKey = mRing.getPublicKey(); final long masterKeyId = masterKey.getKeyID(); @@ -334,8 +330,15 @@ public class UncachedKeyRing { continue; } - // If this is a foreign signature, never mind any further + // If this is a foreign signature, ... if (certId != masterKeyId) { + // never mind any further for public keys, but remove them from secret ones + if (isSecret()) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_FOREIGN, + new String[] { PgpKeyHelper.convertKeyIdToHex(certId) }, indent); + modified = PGPPublicKey.removeCertification(modified, userId, zert); + badCerts += 1; + } continue; } @@ -433,7 +436,7 @@ public class UncachedKeyRing { } // Replace modified key in the keyring - ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + ring = replacePublicKey(ring, modified); indent -= 1; } @@ -578,7 +581,7 @@ public class UncachedKeyRing { // it is not properly bound? error! if (selfCert == null) { - ring = PGPPublicKeyRing.removePublicKey(ring, modified); + ring = replacePublicKey(ring, modified); log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT, new String[]{ PgpKeyHelper.convertKeyIdToHex(key.getKeyID()) }, indent); @@ -593,7 +596,7 @@ public class UncachedKeyRing { modified = PGPPublicKey.addCertification(modified, revocation); } // replace pubkey in keyring - ring = PGPPublicKeyRing.insertPublicKey(ring, modified); + ring = replacePublicKey(ring, modified); indent -= 1; } @@ -614,5 +617,14 @@ public class UncachedKeyRing { return new UncachedKeyRing(ring); } + private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) { + if (ring instanceof PGPPublicKeyRing) { + return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key); + } + PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; + PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); + sKey = PGPSecretKey.replacePublicKey(sKey, key); + return PGPSecretKeyRing.insertSecretKey(secRing, sKey); + } } 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 8688ecb6c..9573629e8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -609,6 +609,12 @@ public class ProviderHelper { return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); } + // Canonicalize this key, to assert a number of assumptions made about it. + keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } + long masterKeyId = keyRing.getMasterKeyId(); log(LogLevel.START, LogType.MSG_IS, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 0760aadf8..6c75907fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -179,7 +179,8 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization - MSG_KC (R.string.msg_kc), + MSG_KC_PUBLIC (R.string.msg_kc_public), + MSG_KC_SECRET (R.string.msg_kc_secret), MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid), MSG_KC_MASTER (R.string.msg_kc_master), MSG_KC_REVOKE_BAD_ERR (R.string.msg_kc_revoke_bad_err), @@ -212,6 +213,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), + MSG_KC_UID_FOREIGN (R.string.msg_kc_uid_foreign), MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index ed06f983b..342cec007 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -563,7 +563,8 @@ Successfully imported secret keyring - Canonicalizing keyring %s + Canonicalizing public keyring %s + Canonicalizing secret keyring %s Keyring canonicalization failed: Keyring has no valid user ids Processing master key Removing bad keyring revocation certificate @@ -596,6 +597,7 @@ Removing user id certificate of unknown type (%s) Removing bad self certificate for user id "%s" Removing outdated self certificate for user id "%s" + Removing foreign user id certificate by %s Removing redundant revocation certificate for user id "%s" Removing outdated revocation certificate for user id "%s" No valid self-certificate found for user id %s, removing from ring From 0013199b2de670b0bc3f28add5996a799525b9a2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jun 2014 23:46:02 +0200 Subject: [PATCH 45/60] handle database error on secret key insert --- .../sufficientlysecure/keychain/provider/ProviderHelper.java | 5 ++++- .../keychain/service/OperationResultParcel.java | 1 + OpenKeychain/src/main/res/values/strings.xml | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) 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 9573629e8..0218b457b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -630,7 +630,10 @@ public class ProviderHelper { values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); // insert new version of this keyRing Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - mContentResolver.insert(uri, values); + if (mContentResolver.insert(uri, values) == null) { + log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } } catch (IOException e) { Log.e(Constants.TAG, "Failed to encode key!", e); log(LogLevel.ERROR, LogType.MSG_IS_IO_EXCPTION); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 6c75907fd..f1f6c304a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -171,6 +171,7 @@ public class OperationResultParcel implements Parcelable { // import secret MSG_IS(R.string.msg_is), MSG_IS_BAD_TYPE_PUBLIC (R.string.msg_is_bad_type_public), + MSG_IS_DB_EXCEPTION (R.string.msg_is_db_exception), MSG_IS_IMPORTING_SUBKEYS (R.string.msg_is_importing_subkeys), MSG_IS_IO_EXCPTION (R.string.msg_is_io_excption), MSG_IS_SUBKEY_NONEXISTENT (R.string.msg_is_subkey_nonexistent), diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 342cec007..304ec8c2f 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -555,6 +555,7 @@ Importing secret key %s + Database error! Processing secret subkeys Error encoding keyring Subkey %s unavailable in public key From 8c7a360d6ed6875774c912e070cb18807e0e831d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 18 Jun 2014 00:03:06 +0200 Subject: [PATCH 46/60] Redesign Import, still some todos and regression bugs --- .../keychain/helper/OtherHelper.java | 9 + .../keychain/ui/ImportKeysActivity.java | 190 +++-- .../ui/ImportKeysClipboardFragment.java | 88 -- .../keychain/ui/ImportKeysFileFragment.java | 38 +- .../ui/ImportKeysKeybaseFragment.java | 16 +- .../keychain/ui/ImportKeysListFragment.java | 27 +- .../keychain/ui/ImportKeysNFCFragment.java | 70 -- .../keychain/ui/ImportKeysQrCodeFragment.java | 82 +- .../keychain/ui/ImportKeysServerFragment.java | 34 +- .../keychain/ui/ViewKeyActivity.java | 2 +- .../ui/adapter/ImportKeysListLoader.java | 2 +- .../drawable-hdpi/ic_action_collection.png | Bin 0 -> 422 bytes .../res/drawable-hdpi/ic_action_qr_code.png | Bin 0 -> 2680 bytes .../drawable-mdpi/ic_action_collection.png | Bin 0 -> 336 bytes .../res/drawable-mdpi/ic_action_qr_code.png | Bin 0 -> 2388 bytes .../drawable-xhdpi/ic_action_collection.png | Bin 0 -> 496 bytes .../res/drawable-xhdpi/ic_action_qr_code.png | Bin 0 -> 3339 bytes .../drawable-xxhdpi/ic_action_collection.png | Bin 0 -> 650 bytes .../res/drawable-xxhdpi/ic_action_qr_code.png | Bin 0 -> 3458 bytes .../main/res/layout/import_keys_activity.xml | 38 +- .../layout/import_keys_clipboard_fragment.xml | 20 - .../res/layout/import_keys_file_fragment.xml | 67 +- .../layout/import_keys_keybase_fragment.xml | 27 +- .../res/layout/import_keys_list_entry.xml | 8 +- .../res/layout/import_keys_nfc_fragment.xml | 30 - .../layout/import_keys_qr_code_fragment.xml | 49 +- .../layout/import_keys_server_fragment.xml | 72 +- .../res/layout/view_key_share_fragment.xml | 10 +- .../src/main/res/values-de/strings.xml | 2 - OpenKeychain/src/main/res/values/strings.xml | 23 +- README.md | 5 +- Resources/graphics/ic_action_qr_code.png | Bin 0 -> 5377 bytes Resources/graphics/ic_action_qr_code.svg | 753 ++++++++++++++++++ Resources/graphics/update-icon.sh | 2 +- 34 files changed, 1204 insertions(+), 460 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_action_collection.png create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_action_qr_code.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_action_collection.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_action_qr_code.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_action_collection.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_action_qr_code.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_collection.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_qr_code.png delete mode 100644 OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml delete mode 100644 OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml create mode 100644 Resources/graphics/ic_action_qr_code.png create mode 100644 Resources/graphics/ic_action_qr_code.svg diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java index d64587578..f04d84315 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.helper; +import android.content.Context; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -67,4 +68,12 @@ public class OtherHelper { return sb; } + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static int pxToDp(Context context, int px) { + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + } + } 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 f389726ff..54186e380 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -32,10 +32,14 @@ import android.os.Messenger; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; +import android.util.DisplayMetrics; +import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import com.github.johnpersano.supertoasts.SuperCardToast; @@ -45,18 +49,22 @@ 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.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.OperationResults.ImportResult; +import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; +import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; import java.util.Locale; -public class ImportKeysActivity extends ActionBarActivity implements ActionBar.OnNavigationListener { +public class ImportKeysActivity extends ActionBarActivity { public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY"; public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_QR_CODE"; @@ -90,23 +98,18 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O private String[] mNavigationStrings; private Fragment mCurrentFragment; private View mImportButton; + private ViewPager mViewPager; + private SlidingTabLayout mSlidingTabLayout; + private PagerTabStripAdapter mTabsAdapter; + + public static final int VIEW_PAGER_HEIGHT = 64; // dp - private static final Class[] NAVIGATION_CLASSES = new Class[]{ - ImportKeysServerFragment.class, - ImportKeysFileFragment.class, - ImportKeysQrCodeFragment.class, - ImportKeysClipboardFragment.class, - ImportKeysNFCFragment.class, - ImportKeysKeybaseFragment.class - }; private static final int NAV_SERVER = 0; - private static final int NAV_FILE = 1; - private static final int NAV_QR_CODE = 2; - private static final int NAV_CLIPBOARD = 3; - private static final int NAV_NFC = 4; - private static final int NAV_KEYBASE = 5; + private static final int NAV_QR_CODE = 1; + private static final int NAV_FILE = 2; + private static final int NAV_KEYBASE = 3; - private int mCurrentNavPosition = -1; + private int mSwitchToTab = NAV_SERVER; @Override protected void onCreate(Bundle savedInstanceState) { @@ -114,6 +117,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O setContentView(R.layout.import_keys_activity); + mViewPager = (ViewPager) findViewById(R.id.import_pager); + mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.import_sliding_tab_layout); + mImportButton = findViewById(R.id.import_import); mImportButton.setOnClickListener(new OnClickListener() { @Override @@ -127,18 +133,55 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { setTitle(R.string.nav_import); } else { - getSupportActionBar().setDisplayShowTitleEnabled(false); - - // set drop down navigation - Context context = getSupportActionBar().getThemedContext(); - ArrayAdapter navigationAdapter = ArrayAdapter.createFromResource(context, - R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item); - getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this); + initTabs(); } handleActions(savedInstanceState, getIntent()); + } + private void initTabs() { + mTabsAdapter = new PagerTabStripAdapter(this); + mViewPager.setAdapter(mTabsAdapter); + mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + // resize view pager back to 64 if keyserver settings have been collapsed + if (getViewPagerHeight() > VIEW_PAGER_HEIGHT) { + resizeViewPager(VIEW_PAGER_HEIGHT); + } + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + + Bundle serverBundle = new Bundle(); +// serverBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysServerFragment.class, + serverBundle, getString(R.string.import_tab_keyserver)); + + Bundle qrCodeBundle = new Bundle(); +// importBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysQrCodeFragment.class, + qrCodeBundle, getString(R.string.import_tab_qr_code)); + + Bundle fileBundle = new Bundle(); +// importBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysFileFragment.class, + fileBundle, getString(R.string.import_tab_direct)); + + Bundle keybaseBundle = new Bundle(); +// keybaseBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); + mTabsAdapter.addTab(ImportKeysKeybaseFragment.class, + keybaseBundle, getString(R.string.import_tab_keybase)); + + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } protected void handleActions(Bundle savedInstanceState, Intent intent) { @@ -164,7 +207,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O /* Keychain's own Actions */ // display file fragment - loadNavFragment(NAV_FILE, null); + mViewPager.setCurrentItem(NAV_FILE); if (dataUri != null) { // action: directly load data @@ -199,7 +242,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O // display keyserver fragment with query Bundle args = new Bundle(); args.putString(ImportKeysServerFragment.ARG_QUERY, query); - loadNavFragment(NAV_SERVER, args); +// loadNavFragment(NAV_SERVER, args); + //TODO: load afterwards! + mSwitchToTab = NAV_SERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -223,9 +268,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O return; } } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { - // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_FILE, null); + mSwitchToTab = NAV_FILE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -233,26 +277,28 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O // also exposed in AndroidManifest // NOTE: this only displays the appropriate fragment, no actions are taken - loadNavFragment(NAV_QR_CODE, null); + mSwitchToTab = NAV_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 - loadNavFragment(NAV_NFC, null); + mSwitchToTab = NAV_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 - loadNavFragment(NAV_KEYBASE, null); + mSwitchToTab = NAV_KEYBASE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else { startListFragment(savedInstanceState, null, null, null); } + + mViewPager.setCurrentItem(mSwitchToTab); } private void startListFragment(Bundle savedInstanceState, byte[] bytes, Uri dataUri, String serverQuery) { @@ -275,54 +321,16 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O getSupportFragmentManager().executePendingTransactions(); } - /** - * "Basically, when using a list navigation, onNavigationItemSelected() is automatically - * called when your activity is created/re-created, whether you like it or not. To prevent - * your Fragment's onCreateView() from being called twice, this initial automatic call to - * onNavigationItemSelected() should check whether the Fragment is already in existence - * inside your Activity." - *

- * from http://stackoverflow.com/a/14295474 - *

- * In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint, - * the fragment would be loaded twice resulting in the query being empty after the second load. - *

- * Our solution: - * To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment - * checks against mCurrentNavPosition. - * - * @param itemPosition - * @param itemId - * @return - */ - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - Log.d(Constants.TAG, "onNavigationItemSelected"); - - loadNavFragment(itemPosition, null); - - return true; + public void resizeViewPager(int dp) { + ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); + params.height = OtherHelper.dpToPx(this, dp); + // update layout after operations + mSlidingTabLayout.setViewPager(mViewPager); } - private void loadNavFragment(int itemPosition, Bundle args) { - if (mCurrentNavPosition != itemPosition) { - if (ActionBar.NAVIGATION_MODE_LIST == getSupportActionBar().getNavigationMode()) { - getSupportActionBar().setSelectedNavigationItem(itemPosition); - } - loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]); - mCurrentNavPosition = itemPosition; - } - } - - private void loadFragment(Class clss, Bundle args, String tag) { - mCurrentFragment = Fragment.instantiate(this, clss.getName(), args); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - // Replace whatever is in the fragment container with this fragment - // and give the fragment a tag name equal to the string at the position selected - ft.replace(R.id.import_navigation_fragment, mCurrentFragment, tag); - // Apply changes - ft.commit(); + public int getViewPagerHeight() { + ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); + return OtherHelper.pxToDp(this, params.height); } public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) { @@ -349,7 +357,9 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O Bundle args = new Bundle(); args.putString(ImportKeysServerFragment.ARG_QUERY, query); args.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); - loadNavFragment(NAV_SERVER, args); +// loadNavFragment(NAV_SERVER, args); + + //TODO // action: search directly startListFragment(savedInstanceState, null, null, query); @@ -437,15 +447,16 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O toast.setButtonTextColor(getResources().getColor(R.color.black)); toast.setTextColor(getResources().getColor(R.color.black)); toast.setOnClickWrapper(new OnClickWrapper("supercardtoast", - new SuperToast.OnClickListener() { - @Override - public void onClick(View view, Parcelable token) { - Intent intent = new Intent( - ImportKeysActivity.this, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); - startActivity(intent); + new SuperToast.OnClickListener() { + @Override + public void onClick(View view, Parcelable token) { + Intent intent = new Intent( + ImportKeysActivity.this, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); + startActivity(intent); + } } - })); + )); toast.show(); /* @@ -560,9 +571,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O super.onResume(); // Check to see if the Activity started due to an Android Beam - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { - handleActionNdefDiscovered(getIntent()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { + handleActionNdefDiscovered(getIntent()); + } else { + Log.d(Constants.TAG, "NFC: No NDEF discovered!"); + } } else { Log.e(Constants.TAG, "Android Beam not supported by Android < 4.1"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java deleted file mode 100644 index f331358fa..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2013-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.net.Uri; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; - -import java.util.Locale; - -public class ImportKeysClipboardFragment extends Fragment { - - private ImportKeysActivity mImportActivity; - private BootstrapButton mButton; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysClipboardFragment newInstance() { - ImportKeysClipboardFragment frag = new ImportKeysClipboardFragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.import_keys_clipboard_fragment, container, false); - - mButton = (BootstrapButton) view.findViewById(R.id.import_clipboard_button); - mButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); - String sendText = ""; - if (clipboardText != null) { - sendText = clipboardText.toString(); - if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { - mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); - return; - } - } - mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mImportActivity = (ImportKeysActivity) getActivity(); - } - -} 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 51f961aab..060e9bab2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -19,21 +19,24 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.FileHelper; +import java.util.Locale; + public class ImportKeysFileFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mBrowse; + private View mBrowse; + private View mClipboardButton; public static final int REQUEST_CODE_FILE = 0x00007003; @@ -56,26 +59,45 @@ public class ImportKeysFileFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_file_fragment, container, false); - mBrowse = (BootstrapButton) view.findViewById(R.id.import_keys_file_browse); + mBrowse = view.findViewById(R.id.import_keys_file_browse); mBrowse.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // open .asc or .gpg files - // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc + // setting it to text/plain prevents Cyanogenmod's file manager from selecting asc // or gpg types! FileHelper.openFile(ImportKeysFileFragment.this, Constants.Path.APP_DIR + "/", "*/*", REQUEST_CODE_FILE); } }); + mClipboardButton = view.findViewById(R.id.import_clipboard_button); + mClipboardButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity()); + String sendText = ""; + if (clipboardText != null) { + sendText = clipboardText.toString(); + if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { + mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); + return; + } + } + mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null); + } + }); + + return view; } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onAttach(Activity activity) { + super.onAttach(activity); - mImportActivity = (ImportKeysActivity) getActivity(); + mImportActivity = (ImportKeysActivity) activity; } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java index a639fe0e0..9264829ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -29,8 +30,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.R; /** @@ -40,7 +39,7 @@ import org.sufficientlysecure.keychain.R; public class ImportKeysKeybaseFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mSearchButton; + private View mSearchButton; private EditText mQueryEditText; public static final String ARG_QUERY = "query"; @@ -66,7 +65,7 @@ public class ImportKeysKeybaseFragment extends Fragment { mQueryEditText = (EditText) view.findViewById(R.id.import_keybase_query); - mSearchButton = (BootstrapButton) view.findViewById(R.id.import_keybase_search); + mSearchButton = view.findViewById(R.id.import_keybase_search); mSearchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -101,8 +100,6 @@ public class ImportKeysKeybaseFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mImportActivity = (ImportKeysActivity) getActivity(); - // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { @@ -112,6 +109,13 @@ public class ImportKeysKeybaseFragment extends Fragment { } } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mImportActivity = (ImportKeysActivity) activity; + } + private void search(String query) { mImportActivity.loadCallback(null, null, null, null, query); } 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 d91c55da3..7d8dc4a6c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; @@ -97,7 +98,7 @@ public class ImportKeysListFragment extends ListFragment implements public ArrayList getSelectedData() { ArrayList result = new ArrayList(); - for(ImportKeysListEntry entry : getSelectedEntries()) { + for (ImportKeysListEntry entry : getSelectedEntries()) { result.add(mCachedKeyData.get(entry.getKeyId())); } return result; @@ -273,17 +274,15 @@ public class ImportKeysListFragment extends ListFragment implements // No error mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); } else if (error instanceof ImportKeysListLoader.FileHasNoContent) { - AppMsg.makeText(getActivity(), R.string.error_import_file_no_content, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_import_file_no_content, Notify.Style.ERROR); } else if (error instanceof ImportKeysListLoader.NonPgpPart) { - AppMsg.makeText(getActivity(), + Notify.showNotify(getActivity(), ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources(). getQuantityString(R.plurals.error_import_non_pgp_part, ((ImportKeysListLoader.NonPgpPart) error).getCount()), - new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.confirm)).show(); + Notify.Style.OK); } else { - AppMsg.makeText(getActivity(), R.string.error_generic_report_bug, - new AppMsg.Style(AppMsg.LENGTH_LONG, R.color.alert)).show(); + Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR); } break; @@ -292,23 +291,17 @@ public class ImportKeysListFragment extends ListFragment implements // TODO: possibly fine-tune message building for these two cases if (error == null) { - AppMsg.makeText( - getActivity(), getResources().getQuantityString(R.plurals.keys_found, - mAdapter.getCount(), mAdapter.getCount()), - AppMsg.STYLE_INFO - ).show(); + // No error } else if (error instanceof Keyserver.QueryTooShortException) { - AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_keyserver_insufficient_query, Notify.Style.ERROR); } else if (error instanceof Keyserver.TooManyResponsesException) { - AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses, - AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), R.string.error_keyserver_too_many_responses, Notify.Style.ERROR); } else if (error instanceof Keyserver.QueryFailedException) { Log.d(Constants.TAG, "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); String alert = getActivity().getString(R.string.error_searching_keys); alert = alert + " (" + error.getLocalizedMessage() + ")"; - AppMsg.makeText(getActivity(), alert, AppMsg.STYLE_ALERT).show(); + Notify.showNotify(getActivity(), alert, Notify.Style.ERROR); } break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java deleted file mode 100644 index 45f464b1c..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2013-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.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; - -import com.beardedhen.androidbootstrap.BootstrapButton; - -import org.sufficientlysecure.keychain.R; - -public class ImportKeysNFCFragment extends Fragment { - - private BootstrapButton mButton; - - /** - * Creates new instance of this fragment - */ - public static ImportKeysNFCFragment newInstance() { - ImportKeysNFCFragment frag = new ImportKeysNFCFragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.import_keys_nfc_fragment, container, false); - - mButton = (BootstrapButton) view.findViewById(R.id.import_nfc_button); - mButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - // show nfc help - Intent intent = new Intent(getActivity(), HelpActivity.class); - intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); - startActivityForResult(intent, 0); - } - }); - - return view; - } - -} 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 22b56e1ab..0cbb51c77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -17,47 +17,47 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; -import com.devspark.appmsg.AppMsg; import com.google.zxing.integration.android.IntentResult; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Notify; import java.util.ArrayList; import java.util.Locale; public class ImportKeysQrCodeFragment extends Fragment { - private ImportKeysActivity mImportActivity; - private BootstrapButton mButton; - private TextView mText; - private ProgressBar mProgress; + private View mNfcButton; - private String[] mScannedContent; + private View mQrCodeButton; + private TextView mQrCodeText; + private ProgressBar mQrCodeProgress; + + private String[] mQrCodeContent; /** * Creates new instance of this fragment */ - public static ImportKeysQrCodeFragment newInstance() { - ImportKeysQrCodeFragment frag = new ImportKeysQrCodeFragment(); + public static ImportKeysFileFragment newInstance() { + ImportKeysFileFragment frag = new ImportKeysFileFragment(); Bundle args = new Bundle(); - frag.setArguments(args); + frag.setArguments(args); return frag; } @@ -68,11 +68,23 @@ public class ImportKeysQrCodeFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_qr_code_fragment, container, false); - mButton = (BootstrapButton) view.findViewById(R.id.import_qrcode_button); - mText = (TextView) view.findViewById(R.id.import_qrcode_text); - mProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress); + mNfcButton = view.findViewById(R.id.import_nfc_button); + mNfcButton.setOnClickListener(new View.OnClickListener() { - mButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // show nfc help + Intent intent = new Intent(getActivity(), HelpActivity.class); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); + startActivityForResult(intent, 0); + } + }); + + mQrCodeButton = view.findViewById(R.id.import_qrcode_button); + mQrCodeText = (TextView) view.findViewById(R.id.import_qrcode_text); + mQrCodeProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress); + + mQrCodeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -85,10 +97,10 @@ public class ImportKeysQrCodeFragment extends Fragment { } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onAttach(Activity activity) { + super.onAttach(activity); - mImportActivity = (ImportKeysActivity) getActivity(); + mImportActivity = (ImportKeysActivity) activity; } @Override @@ -122,8 +134,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } // fail... - AppMsg.makeText(getActivity(), R.string.import_qr_code_wrong, AppMsg.STYLE_ALERT) - .show(); + Notify.showNotify(getActivity(), R.string.import_qr_code_wrong, Notify.Style.ERROR); } break; @@ -136,6 +147,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } } + public void importFingerprint(Uri dataUri) { mImportActivity.loadFromFingerprintUri(null, dataUri); } @@ -151,32 +163,31 @@ public class ImportKeysQrCodeFragment extends Fragment { // first qr code -> setup if (counter == 0) { - mScannedContent = new String[size]; - mProgress.setMax(size); - mProgress.setVisibility(View.VISIBLE); - mText.setVisibility(View.VISIBLE); + mQrCodeContent = new String[size]; + mQrCodeProgress.setMax(size); + mQrCodeProgress.setVisibility(View.VISIBLE); + mQrCodeText.setVisibility(View.VISIBLE); } - if (mScannedContent == null || counter > mScannedContent.length) { - AppMsg.makeText(getActivity(), R.string.import_qr_code_start_with_one, AppMsg.STYLE_ALERT) - .show(); + if (mQrCodeContent == null || counter > mQrCodeContent.length) { + Notify.showNotify(getActivity(), R.string.import_qr_code_start_with_one, Notify.Style.ERROR); return; } // save scanned content - mScannedContent[counter] = content; + mQrCodeContent[counter] = content; // get missing numbers ArrayList missing = new ArrayList(); - for (int i = 0; i < mScannedContent.length; i++) { - if (mScannedContent[i] == null) { + for (int i = 0; i < mQrCodeContent.length; i++) { + if (mQrCodeContent[i] == null) { missing.add(i); } } // update progress and text - int alreadyScanned = mScannedContent.length - missing.size(); - mProgress.setProgress(alreadyScanned); + int alreadyScanned = mQrCodeContent.length - missing.size(); + mQrCodeProgress.setProgress(alreadyScanned); String missingString = ""; for (int m : missing) { @@ -188,17 +199,16 @@ public class ImportKeysQrCodeFragment extends Fragment { String missingText = getResources().getQuantityString(R.plurals.import_qr_code_missing, missing.size(), missingString); - mText.setText(missingText); + mQrCodeText.setText(missingText); // finished! if (missing.size() == 0) { - mText.setText(R.string.import_qr_code_finished); + mQrCodeText.setText(R.string.import_qr_code_finished); String result = ""; - for (String in : mScannedContent) { + for (String in : mQrCodeContent) { result += in; } mImportActivity.loadCallback(result.getBytes(), null, null, null, null); } } - } 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 9e3d88ff5..eabc8348c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -32,8 +33,6 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import com.beardedhen.androidbootstrap.BootstrapButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; @@ -46,8 +45,10 @@ public class ImportKeysServerFragment extends Fragment { private ImportKeysActivity mImportActivity; - private BootstrapButton mSearchButton; + private View mSearchButton; private EditText mQueryEditText; + private View mConfigButton; + private View mConfigLayout; private Spinner mServerSpinner; private ArrayAdapter mServerAdapter; @@ -73,14 +74,17 @@ public class ImportKeysServerFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.import_keys_server_fragment, container, false); - mSearchButton = (BootstrapButton) view.findViewById(R.id.import_server_search); + mSearchButton = view.findViewById(R.id.import_server_search); mQueryEditText = (EditText) view.findViewById(R.id.import_server_query); + mConfigButton = view.findViewById(R.id.import_server_config_button); + mConfigLayout = view.findViewById(R.id.import_server_config); mServerSpinner = (Spinner) view.findViewById(R.id.import_server_spinner); // add keyservers to spinner mServerAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_item, Preferences.getPreferences(getActivity()) - .getKeyServers()); + .getKeyServers() + ); mServerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mServerSpinner.setAdapter(mServerAdapter); if (mServerAdapter.getCount() > 0) { @@ -118,6 +122,17 @@ public class ImportKeysServerFragment extends Fragment { } }); + mConfigButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mImportActivity.getViewPagerHeight() > ImportKeysActivity.VIEW_PAGER_HEIGHT) { + mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT); + } else { + mImportActivity.resizeViewPager(ImportKeysActivity.VIEW_PAGER_HEIGHT + 41); + } + } + }); + return view; } @@ -125,8 +140,6 @@ public class ImportKeysServerFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mImportActivity = (ImportKeysActivity) getActivity(); - // set displayed values if (getArguments() != null) { if (getArguments().containsKey(ARG_QUERY)) { @@ -150,6 +163,13 @@ public class ImportKeysServerFragment extends Fragment { } } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + mImportActivity = (ImportKeysActivity) activity; + } + private void search(String query, String keyServer) { mImportActivity.loadCallback(null, null, query, keyServer, null); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 463c800d2..c3ca0334f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -158,7 +158,7 @@ public class ViewKeyActivity extends ActionBarActivity implements Bundle shareBundle = new Bundle(); shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri); mTabsAdapter.addTab(ViewKeyShareFragment.class, - mainBundle, getString(R.string.key_view_tab_share)); + shareBundle, getString(R.string.key_view_tab_share)); // update layout after operations mSlidingTabLayout.setViewPager(mViewPager); 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 03a82696d..c2712e89e 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 @@ -133,7 +133,7 @@ public class ImportKeysListLoader // read all available blocks... (asc files can contain many blocks with BEGIN END) while (bufferedInput.available() > 0) { - // todo deal with non-keyring objects? + // TODO: deal with non-keyring objects? List rings = UncachedKeyRing.fromStream(bufferedInput); for(UncachedKeyRing key : rings) { ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key); diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_collection.png new file mode 100644 index 0000000000000000000000000000000000000000..8de91173c5e07ceb48cb4e722a3fc69cafeb6436 GIT binary patch literal 422 zcmV;X0a^ZuP)-QyGJs4zqzoW+)>Q>-q2H0o z-vHvhaDEwdIk5(S)KSIK5^Gop#T;nUL-l%HD&_!`;J*lbEp6fIB7o<&ef^QS4FS~N zyXXYy&i_@%07*qoM6N<$f|Sp+S^xk5 literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_action_qr_code.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_action_qr_code.png new file mode 100644 index 0000000000000000000000000000000000000000..e15a055db46ffba7cdfb59a2cfbc947587dacfbd GIT binary patch literal 2680 zcmZ`*i$BxrAODW2#mO=0P^M;dJ4ecWD-q{Ams7dri#Tq%voIE!+YYJeq9m88aM+Bm zP;v=Lzo^M?jbd&K)oPl{?vFM<_-$c zi*h<9l#&=27XlS<<>vvPPL_ z#P=Qh9loNx_MgNt+QxZz|HM2vw+F8={NquNiSJ=-ZjzXqHFK=jII3<&f}|}hEHoARrxAEX+q{!)0atW8y=pMs zkaZuG&oTrflK3?*OC#yah;n|cC7i;gKW|FRU>B9~HTi%r;JgJsjHN69< z8lKs$`0P!Ey}hTW=MArpt54_CW82Sl=ppY}r>7yFDFBb_WJ3L0su}Ntz3T;YlCz>1 z1ps|e;qe?2@-f(~SdYa~`BuFahfPhHPQ=|cF)@i3dT{!e+H3NCbl94`>fOtvEbrQ1h^~#*>E3oA z40m&74Vk8QUMC|kn`ro)Op-G5fe}|${95#o+EEv%<%VuAYQ61qfT)IkXe7b|^q%cO z+)BEco(f_RTIwxujA}B1jzxEF=rA-KwqVR(rknw7gHCo_^v!Z5h%p-8Hm(}?nqSqu z_44x@%FRV9&=T4^+?h9o_{lxxnO=R{&P7JG2u_qt-P7tWFl`%;)}8dEV`ua>jc;Pv zfE8e)JyahiaEAbc2}2)c^dQOH+P8+k3`Y)%DeLZ`8KIAoVro6K6=^IMtMkt`N)4kTMzs?V+WjQ!xcN z)UyxWy7LnqG9;}8ncb5HrM~4Q8ly4@cU%pKE?SrT%(p7OzXD5?XsfHMVJXH=_>p@)@@HQhchy+3DTK}&rkdql>KMo{5_N&1Hc1#`w3}@3QmdJ|S2iNa zlpX6+q}_Wp!cBRd;}a&@Tsz^=ER-XZii>l8x$fF0-3ycrh@XU%Ud(c#W0Qr1_`K!3 zW8Ky+d>ydy1D4TxGhQRNUj0op;`-ul1@CGB#erJ8#2=W}6|g@NNUo~A zyO;4#}gAPzm; z9egiaum4BX5K%w?Vlh)v#Stp%xG$(o6tbM9wH`AF*`uhdDmPP$Sw?c{Gq$vRq7B&D z*;y4a7}>pCMnGg_H=$4iqbuN#WhJ0UP zELD4x37Y;54>OVmugL5s*Jc}h)a9scvN`NxRikE;mzSq^-L4|HzIFTPMF#u_h7+}1 zq5?MFhsP-rPMUxBcQj5noCs-VfAJqB(CLa-qIo%KO9{Q_K-prG^JKUMyO*y|#Tkq7yiBlhR<<4?7~YnfnP4^2 zN9K^t^Z(^XHr?ntY^fx_4+q0B5_o?u&bVE>g=Bt*bImAK5D))Z&tCtAz_oF{=MOv+ zO$!EZu9!%JsX6LS z-W2wHCY0aeb%VM2`7>nobj4_+ZHr($wud;N?6~*v^^^HgADQU_F@<_WYy-z!EtmVt zW8t0)IYLWhK%ty=?l4pBno)X=@B*54Bu9z|1|yp~{T|1D@IAUL^oXhGg+RYOnyXxN<$P(HRs0w{bn|;#LYqgZM_7mWaTT5#nEmM@L63dbzIW9%9}tY^Rtk!g2SC zi^ueE)|=((?7a2cug_J{1a0Jhsx8+dl|Bc&-yVD|i6fcY8tg~>2EwyKyx%FPE1KL0 zf2W-)EF1pJgx8&*%h!ygM3Ht;S+|YqSh~wdrl?-lWFplIp))f%l9uWJsK;(?UxNrss^kT-8|>X4?N1V zyX6F4VkBLLCrrs~n5>4plY`F@x}5cF2Jt^IRa+cLzFPi88NKJm zlnXQiQV3>)cW@0wPm2RZo!LFg=p~qoAme|T#uYdK literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_action_collection.png new file mode 100644 index 0000000000000000000000000000000000000000..b89ea93ff275bad1133017ea963c5cd9593fddd6 GIT binary patch literal 336 zcmV-W0k8gvP)F6XV1NN5h%h!b76wwn z1k}Dgd-m*GYCC`hN?QT34*|8efVh|14qzfdtr{?-)CST4AP`2YC?FQd0z<(UNZ%yG zF05&Yg$#pyh)6Hkz!o6hOpF6St~^eI9Cwr0C;+AQH_)6xO#n*7$oyl#C^^o8!=)h0 z)o5G>?ZuS~p3uSaZ$RH)z~+F*KrBOt68IvnasU+n26S-1O?(be8^{m43-R^#Q#daEsFFm{M?jARPdzjvo)C18(7~4Udu8$RH^M iM*9GxU{C^p00RJ*%{nd>@PY0C0000JIK~!jg?O9!H9Mu*6&Y79@+F+-SO#&)OoAQ&Uq=ER}*lnOoVtemBrw^VD8^^oexTX7AeYNM%*;upQaKC&*4jP*dz)ib^`ffg+xa4*lhtbVrK=*Ks$XN~O#lQy4ZyIf z)&L9x@Blsouv%3oh-hsP1R)WTF(w4C2EfL?zP=m)IOjHs$VqE$l%}aG7K@?x{w`HL z4Pc0f&Jocn0Kp=@NLA^opFjpllH}1&2F92@M6?zF;yB)*s!xc>i)os+j(gM?b3Zdb z7sv54*&?B;<7t}yvD5t4+WnobFGTU zcxR*07zo3Vnm)oXeDCn#!{?S7+Z6%l+-<75mxyYrI?T-Cy?-0PiV*PL-_OjORFze= zrmDlvxdQ-JhQM-Rt=$a(Ns=5SqPK`>Vs>`+bZ>8Ouu?w0PyztHogEJ*NpcFnDF7&y zN&_q9U_}X3tJM#TF^>_^53RNP^ZER5qbNE90M^?306gfu|6NZ{&yuf3x9Exh5q%~i z$Fkgf8d+{$Xs7e7ZKF^qoDq@nsi~=pM~@zL*)?Y7Fp8opiYF0$=)G5FCRLqQ)zi%U z*@_X!vQ5_kn-#3X-P*S+Qr%!F0%VNY!^|@xl2_FkBFYny649K9^bpa_O=D~Au&V9~ zg5b?crE+X3=R|aKHvg=u=2Vpy@zu77bwz-gcZf)hnTPRtl@wL|l$lqn>Y1i7GY_ch z4)6U0fF%h4_zE-MsjBC)|Gb#5xLs@0Z=D30R|jYdPFDB|X^TCIMd9rnF#35dv#gCN)l zpkptjs=HKm$H|i?_bt5UoclQdBuVmQ)(hR%ZXRPysH#seb4^u;!!WF=>aa29F#wIL zCeVsfsdQ_j(I^0z1CSdV8~bzf-3)@@P4E4Lb8crChJD@4*F>>c+~Azs8b#6TX_}rL z85wzzh*ksmj;bD0)pKKGW4_ZIU2SMY zm#Y3f2!ggZp<@smV*+M=NL9aZlx-rvK_4-E|sUGZ8h1|s?{fFA%rxm^Ar zP16?ue3OVCNYnI%BuQRw^+iN~X^H`hq2m)s)6~Ur{ETz%JODW7Uh3)Tc{NFrOF>0M zhluFk>({TJK5^p26_vPHEDm|^AHjU#8pwW^ftml*Xf*NwV2s&BL=Q6aKZ)ovUspxI zTH9AF7DE8F-rio6N~M9`-rgAiGsR-DfBW|B>YSr|K0oW6s~tReP>eAyTj;c^PR-5D z34kDqq9Fhg5#0*ll&a3O`Vx_;o}Qiy0HCTlA{uHm8jXBDU+Ywbfet}zt$iH8CL(Ir zV0}tNtD6BE1i`)|M~+M_IvIeh5hUuh!cAsyYr}Sp)#g>o(r|`@5CzEk;KB`u$?`I1aRdoP9S+#1_@zK#y*OVu#)oL^QG*q>1L-&QK(<37z zKO&;%;y7O0$u3Efed^Ou5i?jkcEXcYu7uYf#4L}9BCp{jof!?50VZQVG` zRN@s=)3U-;U2vM1**4qhCek!bJI%Ee0k+oeUs#FJ@_~bhCYr`uw{HD<5Cr>(sMb7J zDwWnc=bj{@zg4T%m#nqF0vi(pKWwf2X@7tJFP62TX_fl` zOk-YIm{!#ZeEx~)Z1$O`=A3g;6wQdpxQLu*=84(a*;xR{<#OXhbQS^$8M=1u1N1rCcVI$-5wEOeb=DpdNGMVQ7XiX||0j9KU`PJAvwa3*00000 z002y(xZcx`1w_5$E`nW$7a?h%-Yei) zz~ueqWsM!vp)G2*0t6J?b3h?1{By2=vMXvN%$}l|Ex=LuM?xYX=N^S?GFA&&{O%Ia zt^Q9!1Qc8WrJ(Jvx^_)}OF+@y=0wsCKVyrby^y2t)%+h4a1=86ZZS996fn}Cz7_$g zW&wWaO|^6ZgV*s^bN*`(;4}-!$9$w=HHW`+0Zy}k>elH`g8=m}N1bK?HRn6U%WCjT mfPk|B00000000c}FTemCe`~b)HL4&00000N8ILy>J%svPm z7U>z{14KqfD*FfC4fXQ8@1q4ZPXqus)6C(<4pC1&yCJj0UjK563LpLS<-JSM ztPq%2FjGP*H;VcN^Q3{-1#W8vhiCA7c&xw=F;sxCD%uGuv)OVR7}@Z1m!!$Gaj_3P zy#8306)_k#IwwkHRdP<3fRP&oc1B_%Dk@%}xLv2J@K32v7cWh)9Kq<@)d}=~#mVjJ zLzJU{MyXK(Z#2hW{)p$;C$cJQZYkS1kb|NeVyM@!4TBNAb&scmSO2m%g1 z&$rE!KbyNr3$v!^9?q|)|KVLG9bJK3|nXT_6@8R#AQTiTFeKDFQ` z5Fv8!7AWg-LOqii5!=nYnl&li#mx5`z97pLiB<*y8Y;^_u8RP-hx7==cemElD%nah zF0~&n#tzPtF{0}HXeMrl(}`?7E&MsuzRCVF0L}eCQU~{Y>UwmW8f`Q-FK=*_by>#%3=E?~Wx+`m zF;k?efC@k+q`mG~jPD`zk_Ld1Gs_X5un~%vB1Fb7{g`i<6R&0} z{1yS3Bh|smpW6+1i-?$xS9y|QoN(Kgi6*5hu}l6N4PG0+vkeGe7Y9kmiuL``(^Rt! zlFVNIF}+Q13m4`aHisgBuBIAV^cx+p0hwKrP_ITa2-oM4w8icmCk`MRa0-?-BMO3# zHR}8Y!cM;KN@p*NoyU%kkK4|KCsqumZJ44H_<(~?RO#XMJ2U#6C4~N4Ns>Bywr`$X zm}y}uboq67fnz58W(r9Z&b)LuAAbqC^=EJ#^3mSi+wIopQgoS;c@cC72{r~vetfj# zH-E6%@{*Cg_V#uiuXnn+E*|H|m}l7pGun76ytrI5X%l6`S67T0_AGHC zb?`{$`i3c<9kpMCM{<-FIMKpuVgYTngQWzMratE6@NFqe6WqR^C{9dFOsR540!Bh9 zd%fub*R^2qmOkEFzCm=_dQc@R`Q~0UUD&*Mg?5tXzZjuw} zb!lm7`u7GU#l}bQNl*J8Rm(+Cz7PIt9{yUDJW;Q}sl2MH>IME=NqKtzmZP;rv=w+a zOG@P?bmq+@hJUy-?A5uU$a%)^R$H1XoUj&uzg^IlEAIfDIw48$>Tr6Jq@Q$Z)!L1- z6qxBV$;rzHvEF9|cx|2QXMW_EVmCK81DdIpYe{()#A!vXM$zo$xEy?tjSQb{lbJMO zQ%RjKr+ugMnA?wXSN2_FH&4%B65sc-S&ReD^N#D^2>8g4wPXkP+)phIxO}9^q8bd> zyplXyu~NvB^!|`C6uzM1oAV}EEHjNCUxvGQW$>-qhnx96vJ+#NdGa^sLB$1rb^)A5 z$`%KOHUdMCgO&n8Mt|H{Q{~Ln_T*(AMvW$WDojB*fUY6gnQ%qGgYvANgTRVH>7AdZQ&zS4OJkzCoDUh}YmqAyz}gnHbee2+ldY%0OZF4xOD^Ss94__) zv~+O-9;OJW(j2)ewM8CYr(A)qZUbm+?`}c=iq6O~yu~$V{efgug7be}myMulXh0}D zG9!w$@LLk^N0q;GCrMIO&$NlPf_kpWcv(G9!buXI)@`5KOxO(=&XdIz)ptuMbuT!& zjF-oYG2(hrW4E21qq^I}JAylU?s0SteqRiFAlb?^NiN|y=YToj7&C(gjoX_6T%U88 z0rcX)<=Ct=O+&C0is7Ey34&6+g-$0oFjth6z_}9G%p(&bLEb0d$0z2|1x8uj8TLoL zroCiCe(f}@k1PdCji}P}8qi6Aj9g_V8T1ZZb0r*^01q>!@tV|_K@G=zGBY#hPQr{V z{$TQ0Y!7#>kMZvsI*clwVZu6*!kvP|!~}04LGYF$d}4`zUvP2n+=%(t&P2|x#^ESS zD=Py|BVQ#~fIk7gpR{vaUzP&}CuIUdztAQCgWME^b{TFdWv1s3xRjr1{UD^@orAa1&LDut@$92xH!F5 zd*xgvf&!#NlXgscq=|RNe2-LVf5dd0 z62=Sq(l_KBFR)7;RNiHKUo(Efj}4yciv-COoiZvZNf%)P-i@hsI$r-&V;4re>2FnQ)V%KyiW~ZC`$Je13v0p zlBm>R`RO+zJdAD#l0kTCw1bS9)1#8(V>qNMYV8mJ5GL02GS*a+P?hdmhLfNA z-buO#p?FVj_haP<`3AASmR5aoQgt|Q29zdP?Y*LE9Gt6m+hLT$UVOg)f zs*fBPnuMbhVjue}X#9AAsy|#w)Uqusc@m7=!R@4MQ{MRhPLI+5*ka#KPb<8}G(D8P z=3eVW)%)s5tO9#{c236Qg(n(kuSS>s^4_ZyVAvmgzoxvRyc`8Q_ID5AWqvh16Ciuy z3ixS?@L|1WPex!nan`Wq3l}a}fua`Q?>7GjITE4*I2QOm;^`bpxnODHaOc&J)2FtA z@%9sB%n$mo_R>(;!GmR;N5(`Q^-xS;U|>QnLiew92L|l>Vi$bZGRUhL30!wmgM)nNp$&pQf-DNHK<*hDtcHK5bmmb02`%|Jx|EF4;KA3kE1L&2j@n zClU~KjYq>Rw3YsgbWm#XeX(_8NDMq@+rXUgA$k!7U5(Uot95!d?Gj{fZ~sV@_Mp19 zxpT*GKJ_7+zst0B>@N%!M>mf$Q2R>A+bi18Tfc!VhP`})Y@F)mgSJU91*_G4v48ba z9IY%$KMG9kzJ28RHouWBXg6T{ps(v6w1nRxMrn}B4WWko;%f?{sDIH^X;8Pe^8z#Q zH~ewJpB9PMEY3fK6`?+_MxF7-xot$8{(J^OA)QLTUE3TG%8VUR@_`%`;6lvbbdH0~ z=H~iIP@>-GkhyDtihCfYE7ZP>d*Gsw^1qd20{Ynnp7mH~!usEEInlgQ%%}+eHlFct O0nAOU;B_V*N&f`-A1Sc_ literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_collection.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_action_collection.png new file mode 100644 index 0000000000000000000000000000000000000000..c41ca8c8b2450e10e653afcbbf36893883d2cdc1 GIT binary patch literal 650 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGok|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m~=c{978H@y_vJokI7M>&EMJ8+c$gl$;L>-v=x`~zPMbi@2Gcn>i zf(fTjdCpKe(6{V|H_MHp=}F>8R2FFdV`j>U++tnGVlb`mu*#wRFVB}a28z7?ukj$e z-G_ydNq`|RzUBU%#YUh_D*TQv=ZrEjTtf=}~@0;tIvqkUF-9Pz{(!FvIKT;2I@cdIzT(ELuxZs7| z6S)|TwnhDJ$oq3<^TfPFmjfT94eL&=sNen9Xa}7nQiXpM9NJ}>cpg8$GXJ&G0fRaLfyRb~yOr#W{;{`oOgL-~k53>& cVx~VM8-xAZSn{w`TRz}&5nLU%n zSsBNnlXHWF2@}&MhX@sN-oELNub=(<<6Gae*7H8=ec!d7=YH08-Pd*B@xR%ef$Wvt z3jhGb$`Xy`uW3J~h!Fodb$YCpzX%4IS=o#5N0`V}kpC_kV0k$R0Mz1s%w34jyd?fd znP7`c!FK+h!J+s-4NK~|0L=55hq%^q-*?=5b4jk-w>pU`EmOC>fv9U$`d$GRw z(Wc9Vb6v$3Ez!@l{zQb-3$RDyAkBI&gxqpm1+;A{B4Mh7CP@bfO0A+O$#FqBkQDXb ze^arXL>lD+yxGr!G(wcl>UuO_;O8DINn^IsQ2P4hwWUvez7{=)^#WWU^?H9k>%+v& z&1CyGk$+_N2>~90DjUcOOkCgOAx`9i5$x_5o9g z!;c+`d8*Vi_i!~_BT>K~HvGyARFvz<+Y|91arnn6*RvGe-GMKW zT?UCC)RKPQ{HPyzbRncdggN0#|%6SrLRwu z^VV*CF)S};+O&wmZ;FYDJ+&&um40XMS(S8q>I_^`rP_0UNxCQi4T^s4S|ViDtV{3j z8z%?=CYUujptj&>8F$WDEhf$C*Oc)G+_@0EqGKsX2GC3IpPZaLyeq;h8Fdg~UG1Ga zMDGuhBH37~aQ66dX?zHIGYtbSn!`v?Am-z}D|JYU&T zHUrzzvpDPausGFwxP}p9F~F;a8Pp*DxAZ^B5>_D9J3Bjj+gnGLA@2!qPNh+fspukZ z=7K3Ix@h*10%6lHX`FRY<+8@As-?E$8;#j(W-{Jna-A^HNSoYfQjM|P`a1@EOi|yo zQc9pEOpzI0OwKa)WW$-A@C<&}{S6j3Vv1?CtiqMPOn>MIPoQ3uOFlg#iD%+QI23TZ z&xdJzT|S$Qk03pr6Y4ZzRHm~w<{OHEiu%4S08m0e8oUXFotwf|WFKqmEj>6gY}P=o zoCREooH+rpFy5&sa7E^uFNRU=aT^m2J_>48S$JBPa?w3z{xXs%07u-?S0nsl6=q3z zCecy%V|7JW)WKTbrqCI*;*enjixs9|T+$f9uwD3{8TA-(-{84y%quSZ8C?zx|kM2vdP&-j#X?*)`)j;4&4 zXXab@ZjoSdXRZ8*MB+!BakY+Y@?J#eSbK7=HyrJwTF+a^he^2z8OHvf8gcX9s?$sM z_a11u3xKPtuN^(EAa7qd)7%iwyZs!@j&+Wr1fGJ@)gzR~im)>SG?cFHfaB%M9Zu6@ z9OYvpHVVgs@I9?A3GiQtY-=sN0!6hYfFh;oN$* z9e_PxcIglv7WQeifFX8wi>pdqSZm5x*GU2W2M;E|GTw^o3~Ju~Q!79O55pNHf~g3% z8SX)-M;VJ_O!$1R-j)!g5k;BnG+AS?t1-WX)ZOPS?F zYEN1$9&G)o^`Eo$dNeKEe%Jq~uyFhMpd~j3?3T3kg!)|xog-g4mNMSId>_0a0en8s zSGV%@rc-#s2HCc+YUYlq(Qfv-CvPr42XUj6b{YiB1W&8Gqzsh5I$rpf)= zEdLHA(8f0xGX+wo8-u30GjRKQ;_B+^vUh!$wdz`Aoxjf?KeX!^nj$L;Sllx$_o=C=Gbl-5^Z6pn-X@ri z)!zqji4QUKKvgK+7zxc4j-W(RuonR(W@2MSg_c;W(bA8d)h1XBa^A{R4e!?P1dqiV zd)r+egk(rd&zn_^bkYGn;dgsq9kz4ymT?aoj-wQvKTHNj&7$8+Bs)Q+Fdn zVZa2M>Fhq!_g|CUNCdF zI|K2k^y$=STL)rVs1bHFq+!GXNL!E7SsnUI?zD@s{IXc&6uA%Q8zkNUqz%P8U=&X1 zs!b$LDuPqw^RHmX0{+_J*|1Pdu?ls?E{bzk@@0`TqO-w}CJgABH<0@GVboT{;E&RA z&v@G(b3;e#vic8)eR&~QJTP<43Ig?SCwmz2X((RGcMYt;#LDRJ{^6dUt?!xL@42Sv z%*7@raC$fF0n@wG9}=ZB&|^~=L<)#zBF9m0>ajC*+BLhP9sNB|lkbCvPZVDjf!jn? z4oaEH>x5EwDcPyYLj6^#E;4>Ocakl(RFFty=*Q;kY-a*`iCv3V8nazISVZ}DS*vr{ z+S*!PAVPvLdRg#1{%1wjY(uFv2WlBw0eboB^k7n^0$A%K<b|bQ0J;v)$Lc2)x>41?n6teA4C7ACF~ z2?VGxAR*K)Y>;#YR(TGsJeGy^`S|;I=(NK7ITmsE z5uBd0Cqg>u&HLb#96cc7PglxQJB-ozn=Z4RdGezkWq~0NM~L~cIf=hn(D4~+!|8ZZSvN7@_S@)^;zWE!#n-Q-B|!?>t1o7zQf^OGk%S+r>xMzfuk2KB?VJ`O1rWJ94CzynidpInpM+fp<5c_uXx<5bn_!I ztEFnKq+^ibBxuhWcs$HK} z%D89u(@Cb?2lAC%ke$Cz^AD2ai|XHMDS@p{0VlG%D!Y&GjMJkmQgheyN?KZI&D!v= ztWNeH;gwDVcVX3&U9oHrWEqx`qeQhYLqe~ibqsC}tonPSvi%{%b?%V`j$G+7neV^y zY})r8*F8zvg21fc>d}O}@&O-4&v9*STb~!#4^)lxUd=T8|KR-Z&BA|riB-k@hxIO) Ty7nF6_e+44g$=sQ%r)xYNU~hq literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/layout/import_keys_activity.xml b/OpenKeychain/src/main/res/layout/import_keys_activity.xml index 0486b6bd6..fc9d21e23 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_activity.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_activity.xml @@ -3,7 +3,7 @@ android:id="@+id/content_frame" android:layout_marginLeft="@dimen/drawer_content_padding" android:layout_width="match_parent" - android:layout_height="fill_parent" + android:layout_height="match_parent" android:orientation="vertical"> - + android:layout_height="wrap_content" /> + + + + + + + + + android:layout_weight="1" + android:background="@android:color/white" /> + android:paddingRight="16dp" + android:background="@android:color/white"> - - - - - \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml index c07d2bb40..b1056dab3 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml @@ -1,21 +1,66 @@ - + android:layout_height="?android:attr/listPreferredItemHeight" + android:clickable="true" + style="@style/SelectableItem" + android:orientation="horizontal"> + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml index ceba0e1ce..bf00b77e7 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml @@ -1,16 +1,12 @@ - - + + + style="@style/SelectableItem" /> \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml index c91335a5b..56f34e2eb 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml @@ -17,9 +17,13 @@ + android:singleLine="true" + android:paddingLeft="8dp" + android:paddingRight="16dp" + android:paddingTop="4dp" + android:paddingBottom="4dp"> - - - - - - - \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml index 590f7f797..09a31b4a8 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml @@ -1,21 +1,48 @@ - + android:layout_height="?android:attr/listPreferredItemHeight" + android:clickable="true" + style="@style/SelectableItem" + android:orientation="horizontal"> + + + + + +