aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormar-v-in <github@rvin.mooo.com>2014-07-31 23:19:01 +0200
committermar-v-in <github@rvin.mooo.com>2014-07-31 23:19:01 +0200
commiteae7c711a3f588a24ef621a8b82bcd793bcb7e70 (patch)
tree6c1017380b9234a193cfc6c6460ebfd44923837d
parent0c7eea225b7c04549e92c8c7729bf0e7a04aa5c7 (diff)
parent50aea621ba4de844cf6eee077d1f5b14d0247f14 (diff)
downloadopen-keychain-eae7c711a3f588a24ef621a8b82bcd793bcb7e70.tar.gz
open-keychain-eae7c711a3f588a24ef621a8b82bcd793bcb7e70.tar.bz2
open-keychain-eae7c711a3f588a24ef621a8b82bcd793bcb7e70.zip
Merge branch 'master' into improve-file-more
Conflicts: .gitmodules OpenKeychain/build.gradle OpenKeychain/src/main/AndroidManifest.xml OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
-rw-r--r--.gitignore3
-rw-r--r--.gitmodules6
-rw-r--r--.travis.yml7
-rw-r--r--OpenKeychain-Test/build.gradle118
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java254
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java363
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/PgpVerifyTestingHelper.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java)18
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java39
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java142
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpDecryptVerifyTest.java (renamed from OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java)19
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java770
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/ProviderHelperKeyringTest.java (renamed from OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java)29
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java627
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java397
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java125
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING13
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README26
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_keybin0 -> 171 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sigbin0 -> 113 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sigbin0 -> 113 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sigbin0 -> 220 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sigbin0 -> 158 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sigbin0 -> 96 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkeybin0 -> 171 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sigbin0 -> 195 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_keybin0 -> 1201 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sigbin0 -> 123 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sigbin0 -> 130 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sigbin0 -> 186 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkeybin0 -> 608 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sigbin0 -> 105 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_keybin0 -> 421 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sigbin0 -> 99 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sigbin0 -> 132 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sigbin0 -> 96 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_keybin0 -> 143 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sigbin0 -> 192 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sigbin0 -> 72 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attributebin0 -> 1761 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sigbin0 -> 192 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkeybin0 -> 272 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sigbin0 -> 161 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_keybin0 -> 610 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sigbin0 -> 220 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkeybin0 -> 611 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sigbin0 -> 195 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_keybin0 -> 1275 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sigbin0 -> 123 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sigbin0 -> 130 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkeybin0 -> 698 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sigbin0 -> 104 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_keybin0 -> 484 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sigbin0 -> 99 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sigbin0 -> 106 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_keybin0 -> 513 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id1
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sigbin0 -> 192 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attributebin0 -> 1761 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sigbin0 -> 192 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkeybin0 -> 961 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sigbin0 -> 161 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trustbin0 -> 4 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sigbin0 -> 363 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sigbin0 -> 1089 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sigbin0 -> 1089 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpgbin0 -> 442 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpgbin0 -> 322 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpgbin0 -> 324 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sigbin0 -> 15 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpgbin0 -> 179272 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpgbin0 -> 9329 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encryptedbin0 -> 528 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpgbin0 -> 150 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpgbin0 -> 150 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpgbin0 -> 236 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/public-key-canonicalize.blobbin0 -> 1224 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/public-key-for-sample.blob (renamed from OpenKeychain/src/test/resources/public-key-for-sample.blob)bin35198 -> 35198 bytes
-rw-r--r--OpenKeychain-Test/src/test/resources/sample-altered.txt (renamed from OpenKeychain/src/test/resources/sample-altered.txt)0
-rw-r--r--OpenKeychain-Test/src/test/resources/sample.txt (renamed from OpenKeychain/src/test/resources/sample.txt)0
-rw-r--r--OpenKeychain/build.gradle10
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml213
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java34
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java)36
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java)4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java)55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java)22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java)55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java409
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java246
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java96
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java164
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java138
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java176
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java56
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java44
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java44
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java245
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java214
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java744
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java131
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java114
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java16
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java88
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java466
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java127
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java187
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java110
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java46
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java111
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java12
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml16
-rw-r--r--OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml17
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.pngbin0 -> 2259 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.pngbin1268 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.pngbin1252 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.pngbin898 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.pngbin900 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.pngbin757 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.pngbin1278 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.pngbin0 -> 675 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.pngbin0 -> 748 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.pngbin0 -> 675 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.pngbin0 -> 723 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.pngbin0 -> 789 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.pngbin0 -> 528 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.pngbin0 -> 444 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.pngbin0 -> 714 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.pngbin0 -> 861 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.pngbin0 -> 640 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.pngbin0 -> 740 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.pngbin0 -> 536 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.pngbin0 -> 813 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.pngbin0 -> 541 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.pngbin0 -> 695 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.pngbin652 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.pngbin646 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.pngbin507 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.pngbin663 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.pngbin0 -> 1766 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.pngbin884 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.pngbin862 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.pngbin627 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.pngbin688 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.pngbin555 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.pngbin853 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.pngbin0 -> 528 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.pngbin0 -> 622 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.pngbin0 -> 522 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.pngbin0 -> 601 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.pngbin0 -> 643 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.pngbin0 -> 463 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.pngbin0 -> 410 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.pngbin0 -> 613 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.pngbin0 -> 685 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.pngbin0 -> 517 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.pngbin0 -> 589 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.pngbin0 -> 469 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.pngbin0 -> 667 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.pngbin0 -> 476 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.pngbin0 -> 557 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.pngbin0 -> 3176 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.pngbin1676 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.pngbin1606 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.pngbin1203 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.pngbin1091 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.pngbin940 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.pngbin1646 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.pngbin0 -> 911 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.pngbin0 -> 1008 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.pngbin0 -> 911 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.pngbin0 -> 1049 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.pngbin0 -> 1179 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.pngbin0 -> 736 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.pngbin0 -> 676 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.pngbin0 -> 1033 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.pngbin0 -> 1218 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.pngbin0 -> 906 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.pngbin0 -> 1043 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.pngbin0 -> 706 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.pngbin0 -> 1166 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.pngbin0 -> 784 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.pngbin0 -> 1017 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.pngbin0 -> 4039 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.pngbin2381 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.pngbin2364 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.pngbin2217 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.pngbin1366 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.pngbin1282 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.pngbin2215 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.pngbin0 -> 1160 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.pngbin0 -> 1316 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.pngbin0 -> 1165 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.pngbin0 -> 1429 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.pngbin0 -> 1590 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.pngbin0 -> 840 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.pngbin0 -> 694 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.pngbin0 -> 1353 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.pngbin0 -> 1660 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.pngbin0 -> 1231 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.pngbin0 -> 1377 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.pngbin0 -> 946 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.pngbin0 -> 1555 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.pngbin0 -> 1012 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.pngbin0 -> 1319 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable/first_time_1.pngbin0 -> 43898 bytes
-rw-r--r--OpenKeychain/src/main/res/layout/api_account_settings_activity.xml30
-rw-r--r--OpenKeychain/src/main/res/layout/api_app_settings_activity.xml50
-rw-r--r--OpenKeychain/src/main/res/layout/certify_key_activity.xml331
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_activity.xml (renamed from OpenKeychain/src/main/res/layout/edit_key_activity_new.xml)4
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_final_fragment.xml154
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_input_fragment.xml129
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_result_include.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/edit_key_activity.xml53
-rw-r--r--OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/first_time_activity.xml130
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_activity.xml13
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_item.xml30
-rw-r--r--OpenKeychain/src/main/res/layout/notify_area.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/qr_code_activity.xml14
-rw-r--r--OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml19
-rw-r--r--OpenKeychain/src/main/res/layout/upload_key_activity.xml101
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_activity.xml46
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_main_fragment.xml36
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_subkey_item.xml (renamed from OpenKeychain/src/main/res/layout/view_key_keys_item.xml)42
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_user_id_item.xml (renamed from OpenKeychain/src/main/res/layout/view_key_userids_item.xml)4
-rw-r--r--OpenKeychain/src/main/res/layout/wizard_activity.xml98
-rw-r--r--OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml41
-rw-r--r--OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml43
-rw-r--r--OpenKeychain/src/main/res/layout/wizard_start_fragment.xml63
-rw-r--r--OpenKeychain/src/main/res/menu/key_list.xml11
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml4
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-it/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml4
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml6
-rw-r--r--OpenKeychain/src/main/res/values/colors.xml15
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml88
-rw-r--r--OpenKeychain/src/main/res/xml/adv_preferences.xml8
m---------OpenKeychain/src/test/resources/extern/OpenPGP-Haskell0
-rw-r--r--README.md18
-rw-r--r--Resources/graphics/create_key_robot.svg102
-rw-r--r--Resources/graphics/first_time_1.pngbin0 -> 43898 bytes
-rw-r--r--Resources/graphics/first_time_1.svg351
-rw-r--r--Resources/graphics/originals/gnupg-infographic/README (renamed from Resources/gnupg-infographic/README)0
-rw-r--r--Resources/graphics/originals/gnupg-infographic/gnupg-infographic.png (renamed from Resources/gnupg-infographic/gnupg-infographic.png)bin486199 -> 486199 bytes
-rw-r--r--Resources/graphics/originals/gnupg-infographic/gnupg-infographic.svg (renamed from Resources/gnupg-infographic/gnupg-infographic.svg)0
-rw-r--r--Resources/graphics/originals/ic_action_nfc/NFC.png (renamed from Resources/graphics/ic_action_nfc/NFC.png)bin88038 -> 88038 bytes
-rw-r--r--Resources/graphics/originals/ic_action_qr_code/ic_menu_qr_code.svg (renamed from Resources/graphics/ic_action_qr_code/ic_menu_qr_code.svg)0
-rw-r--r--Resources/graphics/originals/ic_launcher/AUTHORS (renamed from Resources/graphics/ic_launcher/AUTHORS)0
-rw-r--r--Resources/graphics/originals/ic_launcher/COPYING (renamed from Resources/graphics/ic_launcher/COPYING)0
-rw-r--r--Resources/graphics/originals/ic_launcher/kgpg_key2_kopete.svgz (renamed from Resources/graphics/ic_launcher/kgpg_key2_kopete.svgz)bin36830 -> 36830 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/README.md97
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.pngbin0 -> 4406 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.pngbin0 -> 6746 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.pngbin0 -> 9753 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.pngbin0 -> 17387 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error.pngbin0 -> 4627 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.pngbin0 -> 7152 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.pngbin0 -> 10220 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.pngbin0 -> 18697 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open.pngbin0 -> 4426 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.pngbin0 -> 6812 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.pngbin0 -> 9704 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.pngbin0 -> 17498 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.pngbin0 -> 7848 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.pngbin0 -> 14346 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.pngbin0 -> 21322 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.pngbin0 -> 38093 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key.pngbin0 -> 4176 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.pngbin0 -> 5987 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.pngbin0 -> 8197 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.pngbin0 -> 14384 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.pngbin0 -> 5003 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.pngbin0 -> 7965 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.pngbin0 -> 11515 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.pngbin0 -> 21420 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.pngbin0 -> 5037 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.pngbin0 -> 7936 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.pngbin0 -> 11420 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.pngbin0 -> 20650 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.pngbin0 -> 3643 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.pngbin0 -> 5165 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.pngbin0 -> 7712 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.pngbin0 -> 14332 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.pngbin0 -> 4024 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.pngbin0 -> 5969 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.pngbin0 -> 8833 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.pngbin0 -> 15993 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.pngbin0 -> 5061 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.pngbin0 -> 7818 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.pngbin0 -> 11578 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.pngbin0 -> 22423 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.pngbin0 -> 5083 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.pngbin0 -> 7928 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.pngbin0 -> 11727 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.pngbin0 -> 22153 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.pngbin0 -> 4845 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.pngbin0 -> 7571 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.pngbin0 -> 10941 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.pngbin0 -> 20160 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.pngbin0 -> 4723 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.pngbin0 -> 7337 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.pngbin0 -> 10570 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.pngbin0 -> 19036 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.pngbin0 -> 5004 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.pngbin0 -> 7911 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.pngbin0 -> 11425 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.pngbin0 -> 20909 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.pngbin0 -> 4236 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.pngbin0 -> 6283 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.pngbin0 -> 8964 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.pngbin0 -> 15762 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.pngbin0 -> 4976 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.pngbin0 -> 8034 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.pngbin0 -> 12079 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.pngbin0 -> 23287 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.pngbin0 -> 4374 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.pngbin0 -> 6840 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.pngbin0 -> 9773 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.pngbin0 -> 17688 bytes
-rw-r--r--Resources/graphics/status_lock_closed.svg12
-rw-r--r--Resources/graphics/status_lock_error.svg12
-rw-r--r--Resources/graphics/status_lock_open.svg12
-rw-r--r--Resources/graphics/status_signature_expired.svg12
-rw-r--r--Resources/graphics/status_signature_expired_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_invalid.svg12
-rw-r--r--Resources/graphics/status_signature_invalid_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_revoked.svg12
-rw-r--r--Resources/graphics/status_signature_revoked_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_unknown.svg12
-rw-r--r--Resources/graphics/status_signature_unknown_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_unverified.svg12
-rw-r--r--Resources/graphics/status_signature_unverified_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_verified.svg12
-rw-r--r--Resources/graphics/status_signature_verified_cutout.svg12
-rwxr-xr-xResources/graphics/update-drawables.sh19
-rw-r--r--build.gradle18
m---------extern/AppMsg0
m---------extern/openkeychain-api-lib0
m---------extern/openpgp-api-lib0
m---------extern/spongycastle0
-rwxr-xr-xprepare-tests.sh30
-rw-r--r--settings.gradle1
439 files changed, 8194 insertions, 3168 deletions
diff --git a/.gitignore b/.gitignore
index 1dfe84d5a..68f5b5a9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,9 @@ ant.properties
.gradle
build
gradle.properties
+# this is in here because the prepare-tests thing modifies it, and we DON'T
+# want this to be commited. use git add -f to work on this file.
+settings.gradle
#Maven
target
diff --git a/.gitmodules b/.gitmodules
index b01f9a0ff..064be5619 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,9 +7,6 @@
[submodule "extern/zxing-qr-code"]
path = extern/zxing-qr-code
url = https://github.com/open-keychain/zxing-qr-code.git
-[submodule "extern/AppMsg"]
- path = extern/AppMsg
- url = https://github.com/open-keychain/Android-AppMsg.git
[submodule "extern/spongycastle"]
path = extern/spongycastle
url = https://github.com/open-keychain/spongycastle.git
@@ -31,9 +28,6 @@
[submodule "extern/minidns"]
path = extern/minidns
url = https://github.com/open-keychain/minidns.git
-[submodule "OpenKeychain/src/test/resources/extern/OpenPGP-Haskell"]
- path = OpenKeychain/src/test/resources/extern/OpenPGP-Haskell
- url = https://github.com/singpolyma/OpenPGP-Haskell.git
[submodule "extern/TokenAutoComplete"]
path = extern/TokenAutoComplete
url = https://github.com/open-keychain/TokenAutoComplete
diff --git a/.travis.yml b/.travis.yml
index 5da831e61..ef69eb556 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
language: java
-jdk: oraclejdk7
+jdk: openjdk7
before_install:
# Install base Android SDK
- sudo apt-get update -qq
@@ -12,6 +12,9 @@ before_install:
# Install required Android components.
#- 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
- ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --no-ui --all --force --filter build-tools-19.1.0,android-19,platform-tools,extra-android-support,extra-android-m2repository
+ - ./prepare-tests.sh
install: echo "Installation done"
-script: gradle assemble -S -q
+script:
+ - gradle assemble -S -q
+ - gradle --info OpenKeychain-Test:testDebug
diff --git a/OpenKeychain-Test/build.gradle b/OpenKeychain-Test/build.gradle
new file mode 100644
index 000000000..a98a79dc1
--- /dev/null
+++ b/OpenKeychain-Test/build.gradle
@@ -0,0 +1,118 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ // need this for com.novoda:gradle-android-test-plugin:0.9.9-SNAPSHOT below (0.9.3 in repos doesn't work!)
+ // run ./install-custom-gradle-test-plugin.sh to pull the thing into the local repository
+ mavenLocal()
+ }
+
+ 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.novoda:gradle-android-test-plugin:0.9.9-SNAPSHOT'
+ }
+}
+
+apply plugin: 'java'
+apply plugin: 'android-test'
+apply plugin: 'jacoco'
+
+dependencies {
+ testCompile 'junit:junit:4.11'
+ testCompile 'com.google.android:android:4.1.1.4'
+ testCompile('com.squareup:fest-android:1.0.+') { exclude module: 'support-v4' }
+ testCompile ('org.robolectric:robolectric:2.3') {
+ exclude module: 'classworlds'
+ exclude module: 'maven-artifact'
+ exclude module: 'maven-artifact-manager'
+ exclude module: 'maven-error-diagnostics'
+ exclude module: 'maven-model'
+ exclude module: 'maven-plugin-registry'
+ exclude module: 'maven-profile'
+ exclude module: 'maven-project'
+ exclude module: 'maven-settings'
+ exclude module: 'nekohtml'
+ exclude module: 'plexus-container-default'
+ exclude module: 'plexus-interpolation'
+ exclude module: 'plexus-utils'
+ exclude module: 'support-v4' // crazy but my android studio don't like this dependency and to fix it remove .idea and re import project
+ exclude module: 'wagon-file'
+ exclude module: 'wagon-http-lightweight'
+ exclude module: 'wagon-http-shared'
+ exclude module: 'wagon-provider-api'
+ }
+}
+
+android {
+ projectUnderTest ':OpenKeychain'
+}
+
+jacoco {
+ toolVersion = "0.7.0.201403182114"
+}
+
+coverageSourceDirs = [
+ '../OpenKeychain/src/main/java',
+ '../OpenKeychain/src/gen',
+ '../OpenKeychain/build/source/apt/debug',
+ '../OpenKeychain/build/source/generated/buildConfig/debug',
+ '../OpenKeychain/build/source/generated/r/debug'
+ ]
+
+jacocoTestReport {
+ reports {
+ xml.enabled = true
+ html.destination "${buildDir}/jacocoHtml"
+ }
+ // class R is used, but usage will not be covered, so ignore this class from report
+ classDirectories = fileTree(dir: '../OpenKeychain/build/intermediates/classes/debug/org/sufficientlysecure/keychain', exclude: 'R*.class')
+ additionalSourceDirs = files(coverageSourceDirs)
+ executionData = files('build/jacoco/testDebug.exec')
+}
+
+// new workaround to force add custom output dirs for android studio
+task addTest {
+ def file = file(project.name + ".iml")
+ doLast {
+ try {
+ def parsedXml = (new XmlParser()).parse(file)
+ def node = parsedXml.component[1]
+ def outputNode = parsedXml.component[1].output[0]
+ def outputTestNode = parsedXml.component[1].'output-test'[0]
+ def rewrite = false
+
+ new Node(node, 'sourceFolder', ['url': 'file://$MODULE_DIR$/' + "${it}", 'isTestSource': "true"])
+
+ if(outputNode == null) {
+ new Node(node, 'output', ['url': 'file://$MODULE_DIR$/build/resources/testDebug'])
+ } else {
+ if(outputNode.attributes['url'] != 'file://$MODULE_DIR$/build/resources/testDebug') {
+ outputNode.attributes = ['url': 'file://$MODULE_DIR$/build/resources/testDebug']
+ rewrite = true
+ }
+ }
+
+ if(outputTestNode == null) {
+ new Node(node, 'output-test', ['url': 'file://$MODULE_DIR$/build/test-classes/debug'])
+ } else {
+ if(outputTestNode.attributes['url'] != 'file://$MODULE_DIR$/build/test-classes/debug') {
+ outputTestNode.attributes = ['url': 'file://$MODULE_DIR$/build/test-classes/debug']
+ rewrite = true
+ }
+ }
+
+ if(rewrite) {
+ def writer = new StringWriter()
+ new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
+ file.text = writer.toString()
+ }
+ } catch (FileNotFoundException e) {
+ // iml not found, common on command line only builds
+ }
+
+ }
+}
+
+// always do the addtest on prebuild
+gradle.projectsEvaluated {
+ testDebugClasses.dependsOn(addTest)
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java
new file mode 100644
index 000000000..94193bbcb
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringBuilder.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) Art O Cathain
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.support;
+
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
+import org.spongycastle.bcpg.ContainedPacket;
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.bcpg.MPInteger;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
+import org.spongycastle.bcpg.PublicKeyPacket;
+import org.spongycastle.bcpg.PublicSubkeyPacket;
+import org.spongycastle.bcpg.RSAPublicBCPGKey;
+import org.spongycastle.bcpg.SignaturePacket;
+import org.spongycastle.bcpg.SignatureSubpacket;
+import org.spongycastle.bcpg.SignatureSubpacketInputStream;
+import org.spongycastle.bcpg.SignatureSubpacketTags;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.bcpg.UserIDPacket;
+import org.spongycastle.bcpg.sig.Features;
+import org.spongycastle.bcpg.sig.IssuerKeyID;
+import org.spongycastle.bcpg.sig.KeyExpirationTime;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.bcpg.sig.PreferredAlgorithms;
+import org.spongycastle.bcpg.sig.SignatureCreationTime;
+import org.spongycastle.openpgp.PGPSignature;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helps create correct and incorrect keyrings for tests.
+ *
+ * The original "correct" keyring was generated by GnuPG.
+ */
+public class KeyringBuilder {
+
+
+ private static final BigInteger PUBLIC_KEY_MODULUS = new BigInteger(
+ "cbab78d90d5f2cc0c54dd3c3953005a1e6b521f1ffa5465a102648bf7b91ec72" +
+ "f9c180759301587878caeb73332156209f81ca5b3b94309d96110f6972cfc56a" +
+ "37fd6279f61d71f19b8f64b288e338299dce133520f5b9b4253e6f4ba31ca36a" +
+ "fd87c2081b15f0b283e9350e370e181a23d31379101f17a23ae9192250db6540" +
+ "2e9cab2a275bc5867563227b197c8b136c832a94325b680e144ed864fb00b9b8" +
+ "b07e13f37b40d5ac27dae63cd6a470a7b40fa3c7479b5b43e634850cc680b177" +
+ "8dd6b1b51856f36c3520f258f104db2f96b31a53dd74f708ccfcefccbe420a90" +
+ "1c37f1f477a6a4b15f5ecbbfd93311a647bcc3f5f81c59dfe7252e3cd3be6e27"
+ , 16
+ );
+
+ private static final BigInteger PUBLIC_SUBKEY_MODULUS = new BigInteger(
+ "e8e2e2a33102649f19f8a07486fb076a1406ca888d72ae05d28f0ef372b5408e" +
+ "45132c69f6e5cb6a79bb8aed84634196731393a82d53e0ddd42f28f92cc15850" +
+ "8ce3b7ca1a9830502745aee774f86987993df984781f47c4a2910f95cf4c950c" +
+ "c4c6cccdc134ad408a0c5418b5e360c9781a8434d366053ea6338b975fae88f9" +
+ "383a10a90e7b2caa9ddb95708aa9d8a90246e29b04dbd6136613085c9a287315" +
+ "c6e9c7ff4012defc1713875e3ff6073333a1c93d7cd75ebeaaf16b8b853d96ba" +
+ "7003258779e8d2f70f1bc0bcd3ef91d7a9ccd8e225579b2d6fcae32799b0a6c0" +
+ "e7305fc65dc4edc849c6130a0d669c90c193b1e746c812510f9d600a208be4a5"
+ , 16
+ );
+
+ private static final Date SIGNATURE_DATE = new Date(1404566755000L);
+
+ private static final BigInteger EXPONENT = BigInteger.valueOf(0x010001);
+
+ private static final String USER_ID_STRING = "OpenKeychain User (NOT A REAL KEY) <openkeychain@example.com>";
+
+ public static final BigInteger CORRECT_SIGNATURE = new BigInteger(
+ "b065c071d3439d5610eb22e5b4df9e42ed78b8c94f487389e4fc98e8a75a043f" +
+ "14bf57d591811e8e7db2d31967022d2ee64372829183ec51d0e20c42d7a1e519" +
+ "e9fa22cd9db90f0fd7094fd093b78be2c0db62022193517404d749152c71edc6" +
+ "fd48af3416038d8842608ecddebbb11c5823a4321d2029b8993cb017fa8e5ad7" +
+ "8a9a618672d0217c4b34002f1a4a7625a514b6a86475e573cb87c64d7069658e" +
+ "627f2617874007a28d525e0f87d93ca7b15ad10dbdf10251e542afb8f9b16cbf" +
+ "7bebdb5fe7e867325a44e59cad0991cb239b1c859882e2ebb041b80e5cdc3b40" +
+ "ed259a8a27d63869754c0881ccdcb50f0564fecdc6966be4a4b87a3507a9d9be"
+ , 16
+ );
+ public static final BigInteger CORRECT_SUBKEY_SIGNATURE = new BigInteger(
+ "9c40543e646cfa6d3d1863d91a4e8f1421d0616ddb3187505df75fbbb6c59dd5" +
+ "3136b866f246a0320e793cb142c55c8e0e521d1e8d9ab864650f10690f5f1429" +
+ "2eb8402a3b1f82c01079d12f5c57c43fce524a530e6f49f6f87d984e26db67a2" +
+ "d469386dac87553c50147ebb6c2edd9248325405f737b815253beedaaba4f5c9" +
+ "3acd5d07fe6522ceda1027932d849e3ec4d316422cd43ea6e506f643936ab0be" +
+ "8246e546bb90d9a83613185047566864ffe894946477e939725171e0e15710b2" +
+ "089f78752a9cb572f5907323f1b62f14cb07671aeb02e6d7178f185467624ec5" +
+ "74e4a73c439a12edba200a4832106767366a1e6f63da0a42d593fa3914deee2b"
+ , 16
+ );
+ public static final BigInteger KEY_ID = BigInteger.valueOf(0x15130BCF071AE6BFL);
+
+ public static UncachedKeyRing correctRing() {
+ return convertToKeyring(correctKeyringPackets());
+ }
+
+ public static UncachedKeyRing ringWithExtraIncorrectSignature() {
+ List<ContainedPacket> packets = correctKeyringPackets();
+ SignaturePacket incorrectSignaturePacket = createSignaturePacket(CORRECT_SIGNATURE.subtract(BigInteger.ONE));
+ packets.add(2, incorrectSignaturePacket);
+ return convertToKeyring(packets);
+ }
+
+ private static UncachedKeyRing convertToKeyring(List<ContainedPacket> packets) {
+ try {
+ return UncachedKeyRing.decodeFromData(TestDataUtil.concatAll(packets));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static List<ContainedPacket> correctKeyringPackets() {
+ PublicKeyPacket publicKey = createPgpPublicKey(PUBLIC_KEY_MODULUS);
+ UserIDPacket userId = createUserId(USER_ID_STRING);
+ SignaturePacket signaturePacket = createSignaturePacket(CORRECT_SIGNATURE);
+ PublicKeyPacket subKey = createPgpPublicSubKey(PUBLIC_SUBKEY_MODULUS);
+ SignaturePacket subKeySignaturePacket = createSubkeySignaturePacket();
+
+ return new ArrayList<ContainedPacket>(Arrays.asList(
+ publicKey,
+ userId,
+ signaturePacket,
+ subKey,
+ subKeySignaturePacket
+ ));
+ }
+
+ private static SignaturePacket createSignaturePacket(BigInteger signature) {
+ MPInteger[] signatureArray = new MPInteger[]{
+ new MPInteger(signature)
+ };
+
+ int signatureType = PGPSignature.POSITIVE_CERTIFICATION;
+ int keyAlgorithm = SignaturePacket.RSA_GENERAL;
+ int hashAlgorithm = HashAlgorithmTags.SHA1;
+
+ SignatureSubpacket[] hashedData = new SignatureSubpacket[]{
+ new SignatureCreationTime(false, SIGNATURE_DATE),
+ new KeyFlags(false, KeyFlags.CERTIFY_OTHER + KeyFlags.SIGN_DATA),
+ new KeyExpirationTime(false, TimeUnit.DAYS.toSeconds(2)),
+ new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_SYM_ALGS, false, new int[]{
+ SymmetricKeyAlgorithmTags.AES_256,
+ SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128,
+ SymmetricKeyAlgorithmTags.CAST5,
+ SymmetricKeyAlgorithmTags.TRIPLE_DES
+ }),
+ new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_HASH_ALGS, false, new int[]{
+ HashAlgorithmTags.SHA256,
+ HashAlgorithmTags.SHA1,
+ HashAlgorithmTags.SHA384,
+ HashAlgorithmTags.SHA512,
+ HashAlgorithmTags.SHA224
+ }),
+ new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_COMP_ALGS, false, new int[]{
+ CompressionAlgorithmTags.ZLIB,
+ CompressionAlgorithmTags.BZIP2,
+ CompressionAlgorithmTags.ZIP
+ }),
+ new Features(false, Features.FEATURE_MODIFICATION_DETECTION),
+ createPreferencesSignatureSubpacket()
+ };
+ SignatureSubpacket[] unhashedData = new SignatureSubpacket[]{
+ new IssuerKeyID(false, KEY_ID.toByteArray())
+ };
+ byte[] fingerPrint = new BigInteger("522c", 16).toByteArray();
+
+ return new SignaturePacket(signatureType,
+ KEY_ID.longValue(),
+ keyAlgorithm,
+ hashAlgorithm,
+ hashedData,
+ unhashedData,
+ fingerPrint,
+ signatureArray);
+ }
+
+ /**
+ * There is no Preferences subpacket in BouncyCastle, so we have
+ * to create one manually.
+ */
+ private static SignatureSubpacket createPreferencesSignatureSubpacket() {
+ SignatureSubpacket prefs;
+ try {
+ prefs = new SignatureSubpacketInputStream(new ByteArrayInputStream(
+ new byte[]{2, SignatureSubpacketTags.KEY_SERVER_PREFS, (byte) 0x80})
+ ).readPacket();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ return prefs;
+ }
+
+ private static SignaturePacket createSubkeySignaturePacket() {
+ int signatureType = PGPSignature.SUBKEY_BINDING;
+ int keyAlgorithm = SignaturePacket.RSA_GENERAL;
+ int hashAlgorithm = HashAlgorithmTags.SHA1;
+
+ SignatureSubpacket[] hashedData = new SignatureSubpacket[]{
+ new SignatureCreationTime(false, SIGNATURE_DATE),
+ new KeyFlags(false, KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE),
+ new KeyExpirationTime(false, TimeUnit.DAYS.toSeconds(2)),
+ };
+ SignatureSubpacket[] unhashedData = new SignatureSubpacket[]{
+ new IssuerKeyID(false, KEY_ID.toByteArray())
+ };
+ byte[] fingerPrint = new BigInteger("234a", 16).toByteArray();
+ MPInteger[] signature = new MPInteger[]{
+ new MPInteger(CORRECT_SUBKEY_SIGNATURE)
+ };
+ return new SignaturePacket(signatureType,
+ KEY_ID.longValue(),
+ keyAlgorithm,
+ hashAlgorithm,
+ hashedData,
+ unhashedData,
+ fingerPrint,
+ signature);
+ }
+
+ private static PublicKeyPacket createPgpPublicKey(BigInteger modulus) {
+ return new PublicKeyPacket(PublicKeyAlgorithmTags.RSA_GENERAL, SIGNATURE_DATE, new RSAPublicBCPGKey(modulus, EXPONENT));
+ }
+
+ private static PublicKeyPacket createPgpPublicSubKey(BigInteger modulus) {
+ return new PublicSubkeyPacket(PublicKeyAlgorithmTags.RSA_GENERAL, SIGNATURE_DATE, new RSAPublicBCPGKey(modulus, EXPONENT));
+ }
+
+ private static UserIDPacket createUserId(String userId) {
+ return new UserIDPacket(userId);
+ }
+
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
new file mode 100644
index 000000000..015e134ea
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) Art O Cathain, Vincent Breitmoser
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.sufficientlysecure.keychain.support;
+
+import android.content.Context;
+
+import org.spongycastle.util.Arrays;
+import org.sufficientlysecure.keychain.pgp.NullProgressable;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.OperationResults;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/** Helper methods for keyring tests. */
+public class KeyringTestingHelper {
+
+ private final Context context;
+
+ public KeyringTestingHelper(Context robolectricContext) {
+ this.context = robolectricContext;
+ }
+
+ public boolean addKeyring(Collection<String> blobFiles) throws Exception {
+
+ ProviderHelper providerHelper = new ProviderHelper(context);
+
+ byte[] data = TestDataUtil.readAllFully(blobFiles);
+ UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
+ long masterKeyId = ring.getMasterKeyId();
+
+ // Should throw an exception; key is not yet saved
+ retrieveKeyAndExpectNotFound(providerHelper, masterKeyId);
+
+ OperationResults.SaveKeyringResult saveKeyringResult = providerHelper.savePublicKeyRing(ring, new NullProgressable());
+
+ boolean saveSuccess = saveKeyringResult.success();
+
+ // Now re-retrieve the saved key. Should not throw an exception.
+ providerHelper.getCanonicalizedPublicKeyRing(masterKeyId);
+
+ // A different ID should still fail
+ retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1);
+
+ return saveSuccess;
+ }
+
+ public static UncachedKeyRing removePacket(UncachedKeyRing ring, int position)
+ throws IOException, PgpGeneralException {
+ return UncachedKeyRing.decodeFromData(removePacket(ring.getEncoded(), position));
+ }
+
+ public static byte[] removePacket(byte[] ring, int position) throws IOException {
+ Iterator<RawPacket> it = parseKeyring(ring);
+ ByteArrayOutputStream out = new ByteArrayOutputStream(ring.length);
+
+ int i = 0;
+ while(it.hasNext()) {
+ // at the right position, skip the packet
+ if(i++ == position) {
+ it.next();
+ continue;
+ }
+ // write the old one
+ out.write(it.next().buf);
+ }
+
+ if (i <= position) {
+ throw new IndexOutOfBoundsException("injection index did not not occur in stream!");
+ }
+
+ return out.toByteArray();
+ }
+
+ public static UncachedKeyRing injectPacket(UncachedKeyRing ring, byte[] inject, int position)
+ throws IOException, PgpGeneralException {
+ return UncachedKeyRing.decodeFromData(injectPacket(ring.getEncoded(), inject, position));
+ }
+
+ public static byte[] injectPacket(byte[] ring, byte[] inject, int position) throws IOException {
+
+ Iterator<RawPacket> it = parseKeyring(ring);
+ ByteArrayOutputStream out = new ByteArrayOutputStream(ring.length + inject.length);
+
+ int i = 0;
+ while(it.hasNext()) {
+ // at the right position, inject the new packet
+ if(i++ == position) {
+ out.write(inject);
+ }
+ // write the old one
+ out.write(it.next().buf);
+ }
+
+ if (i <= position) {
+ throw new IndexOutOfBoundsException("injection index did not not occur in stream!");
+ }
+
+ return out.toByteArray();
+
+ }
+
+ /** This class contains a single pgp packet, together with information about its position
+ * in the keyring and its packet tag.
+ */
+ public static class RawPacket {
+ public int position;
+
+ // packet tag for convenience, this can also be read from the header
+ public int tag;
+
+ public int headerLength, length;
+ // this buf includes the header, so its length is headerLength + length!
+ public byte[] buf;
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof RawPacket && Arrays.areEqual(this.buf, ((RawPacket) other).buf);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(buf);
+ }
+ }
+
+ /** A comparator which compares RawPackets by their position */
+ public static final Comparator<RawPacket> packetOrder = new Comparator<RawPacket>() {
+ public int compare(RawPacket left, RawPacket right) {
+ return Integer.compare(left.position, right.position);
+ }
+ };
+
+ /** Diff two keyrings, returning packets only present in one keyring in its associated List.
+ *
+ * Packets in the returned lists are annotated and ordered by their original order of appearance
+ * in their origin keyrings.
+ *
+ * @return true if keyrings differ in at least one packet
+ */
+ public static boolean diffKeyrings(byte[] ringA, byte[] ringB,
+ List<RawPacket> onlyA, List<RawPacket> onlyB)
+ throws IOException {
+ Iterator<RawPacket> streamA = parseKeyring(ringA);
+ Iterator<RawPacket> streamB = parseKeyring(ringB);
+
+ HashSet<RawPacket> a = new HashSet<RawPacket>(), b = new HashSet<RawPacket>();
+
+ RawPacket p;
+ int pos = 0;
+ while(true) {
+ p = streamA.next();
+ if (p == null) {
+ break;
+ }
+ p.position = pos++;
+ a.add(p);
+ }
+ pos = 0;
+ while(true) {
+ p = streamB.next();
+ if (p == null) {
+ break;
+ }
+ p.position = pos++;
+ b.add(p);
+ }
+
+ onlyA.clear();
+ onlyB.clear();
+
+ onlyA.addAll(a);
+ onlyA.removeAll(b);
+ onlyB.addAll(b);
+ onlyB.removeAll(a);
+
+ Collections.sort(onlyA, packetOrder);
+ Collections.sort(onlyB, packetOrder);
+
+ return !onlyA.isEmpty() || !onlyB.isEmpty();
+ }
+
+ /** Creates an iterator of RawPackets over a binary keyring. */
+ public static Iterator<RawPacket> parseKeyring(byte[] ring) {
+
+ final InputStream stream = new ByteArrayInputStream(ring);
+
+ return new Iterator<RawPacket>() {
+ RawPacket next;
+
+ @Override
+ public boolean hasNext() {
+ if (next == null) try {
+ next = readPacket(stream);
+ } catch (IOException e) {
+ return false;
+ }
+ return next != null;
+ }
+
+ @Override
+ public RawPacket next() {
+ if (!hasNext()) {
+ return null;
+ }
+ try {
+ return next;
+ } finally {
+ next = null;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ }
+
+ /** Read a single (raw) pgp packet from an input stream.
+ *
+ * Note that the RawPacket.position field is NOT set here!
+ *
+ * Variable length packets are not handled here. we don't use those in our test classes, and
+ * otherwise rely on BouncyCastle's own unit tests to handle those correctly.
+ */
+ private static RawPacket readPacket(InputStream in) throws IOException {
+
+ // save here. this is tag + length, max 6 bytes
+ in.mark(6);
+
+ int hdr = in.read();
+ int headerLength = 1;
+
+ if (hdr < 0) {
+ return null;
+ }
+
+ if ((hdr & 0x80) == 0) {
+ throw new IOException("invalid header encountered");
+ }
+
+ boolean newPacket = (hdr & 0x40) != 0;
+ int tag;
+ int bodyLen;
+
+ if (newPacket) {
+ tag = hdr & 0x3f;
+
+ int l = in.read();
+ headerLength += 1;
+
+ if (l < 192) {
+ bodyLen = l;
+ } else if (l <= 223) {
+ int b = in.read();
+ headerLength += 1;
+
+ bodyLen = ((l - 192) << 8) + (b) + 192;
+ } else if (l == 255) {
+ bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ headerLength += 4;
+ } else {
+ // bodyLen = 1 << (l & 0x1f);
+ throw new IOException("no support for partial bodies in test classes");
+ }
+ } else {
+ int lengthType = hdr & 0x3;
+
+ tag = (hdr & 0x3f) >> 2;
+
+ switch (lengthType) {
+ case 0:
+ bodyLen = in.read();
+ headerLength += 1;
+ break;
+ case 1:
+ bodyLen = (in.read() << 8) | in.read();
+ headerLength += 2;
+ break;
+ case 2:
+ bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
+ headerLength += 4;
+ break;
+ case 3:
+ // bodyLen = 1 << (l & 0x1f);
+ throw new IOException("no support for partial bodies in test classes");
+ default:
+ throw new IOException("unknown length type encountered");
+ }
+ }
+
+ in.reset();
+
+ // read the entire packet INCLUDING the header here
+ byte[] buf = new byte[headerLength+bodyLen];
+ if (in.read(buf) != headerLength+bodyLen) {
+ throw new IOException("read length mismatch!");
+ }
+ RawPacket p = new RawPacket();
+ p.tag = tag;
+ p.headerLength = headerLength;
+ p.length = bodyLen;
+ p.buf = buf;
+ return p;
+
+ }
+
+ public static <E> E getNth(Iterator<E> it, int position) {
+ while(position-- > 0) {
+ it.next();
+ }
+ return it.next();
+ }
+
+ public static long getSubkeyId(UncachedKeyRing ring, int position) {
+ return getNth(ring.getPublicKeys(), position).getKeyId();
+ }
+
+ private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) {
+ try {
+ providerHelper.getCanonicalizedPublicKeyRing(masterKeyId);
+ throw new AssertionError("Was expecting the previous call to fail!");
+ } catch (ProviderHelper.NotFoundException expectedException) {
+ // good
+ }
+ }
+
+ public static <E> List<E> itToList(Iterator<E> it) {
+ List<E> result = new ArrayList<E>();
+ while(it.hasNext()) {
+ result.add(it.next());
+ }
+ return result;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/PgpVerifyTestingHelper.java
index 1ab5878cc..dd5786512 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/PgpVerifyTestingHelper.java
@@ -1,4 +1,20 @@
-package org.sufficientlysecure.keychain.testsupport;
+package org.sufficientlysecure.keychain.support;
+/*
+ * Copyright (C) Art O Cathain
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
import android.content.Context;
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
new file mode 100644
index 000000000..2cd0c67a2
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Art O Cathain
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.support;
+
+import android.content.Context;
+import android.net.Uri;
+
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+
+/**
+ * Created by art on 21/06/14.
+ */
+class ProviderHelperStub extends ProviderHelper {
+ public ProviderHelperStub(Context context) {
+ super(context);
+ }
+
+ @Override
+ public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri id) throws NotFoundException {
+ byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob"));
+ return new CanonicalizedPublicKeyRing(data, 0);
+ }
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java
new file mode 100644
index 000000000..f2b3c0996
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/TestDataUtil.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) Art O Cathain
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.support;
+
+import org.spongycastle.bcpg.ContainedPacket;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Misc support functions. Would just use Guava / Apache Commons but
+ * avoiding extra dependencies.
+ */
+public class TestDataUtil {
+ public static byte[] readFully(InputStream input) {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ appendToOutput(input, output);
+ return output.toByteArray();
+ }
+
+ public static void appendToOutput(InputStream input, OutputStream output) {
+ byte[] buffer = new byte[8192];
+ int bytesRead;
+ try {
+ while ((bytesRead = input.read(buffer)) != -1) {
+ output.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static byte[] readAllFully(Collection<String> inputResources) {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+ for (String inputResource : inputResources) {
+ appendToOutput(getResourceAsStream(inputResource), output);
+ }
+ return output.toByteArray();
+ }
+
+ public static InputStream getResourceAsStream(String resourceName) {
+ return TestDataUtil.class.getResourceAsStream(resourceName);
+ }
+
+ /**
+ * Null-safe equivalent of {@code a.equals(b)}.
+ */
+ public static boolean equals(Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+
+ public static <T> boolean iterEquals(Iterator<T> a, Iterator<T> b, EqualityChecker<T> comparator) {
+ while (a.hasNext()) {
+ T aObject = a.next();
+ if (!b.hasNext()) {
+ return false;
+ }
+ T bObject = b.next();
+ if (!comparator.areEquals(aObject, bObject)) {
+ return false;
+ }
+ }
+
+ if (b.hasNext()) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ public static <T> boolean iterEquals(Iterator<T> a, Iterator<T> b) {
+ return iterEquals(a, b, new EqualityChecker<T>() {
+ @Override
+ public boolean areEquals(T lhs, T rhs) {
+ return TestDataUtil.equals(lhs, rhs);
+ }
+ });
+ }
+
+ public static interface EqualityChecker<T> {
+ public boolean areEquals(T lhs, T rhs);
+ }
+
+
+ public static byte[] concatAll(java.util.List<ContainedPacket> packets) {
+ byte[][] byteArrays = new byte[packets.size()][];
+ try {
+ for (int i = 0; i < packets.size(); i++) {
+ byteArrays[i] = packets.get(i).getEncoded();
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ return concatAll(byteArrays);
+ }
+
+ public static byte[] concatAll(byte[]... byteArrays) {
+ if (byteArrays.length == 1) {
+ return byteArrays[0];
+ } else if (byteArrays.length == 2) {
+ return concat(byteArrays[0], byteArrays[1]);
+ } else {
+ byte[] first = concat(byteArrays[0], byteArrays[1]);
+ byte[][] remainingArrays = new byte[byteArrays.length - 1][];
+ remainingArrays[0] = first;
+ System.arraycopy(byteArrays, 2, remainingArrays, 1, byteArrays.length - 2);
+ return concatAll(remainingArrays);
+ }
+ }
+
+ private static byte[] concat(byte[] a, byte[] b) {
+ int aLen = a.length;
+ int bLen = b.length;
+ byte[] c = new byte[aLen + bLen];
+ System.arraycopy(a, 0, c, 0, aLen);
+ System.arraycopy(b, 0, c, aLen, bLen);
+ return c;
+ }
+
+}
diff --git a/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpDecryptVerifyTest.java
index d759bce05..158650012 100644
--- a/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpDecryptVerifyTest.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) Art O Cathain
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
package tests;
import org.junit.Assert;
@@ -5,7 +22,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.*;
import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper;
+import org.sufficientlysecure.keychain.support.PgpVerifyTestingHelper;
@RunWith(RobolectricTestRunner.class)
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java
new file mode 100644
index 000000000..4f6694049
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java
@@ -0,0 +1,770 @@
+package org.sufficientlysecure.keychain.tests;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.robolectric.*;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.BCPGInputStream;
+import org.spongycastle.bcpg.Packet;
+import org.spongycastle.bcpg.PacketTags;
+import org.spongycastle.bcpg.SecretSubkeyPacket;
+import org.spongycastle.bcpg.SignaturePacket;
+import org.spongycastle.bcpg.UserIDPacket;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPSignature;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Constants.choice.algorithm;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
+import org.sufficientlysecure.keychain.support.KeyringBuilder;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+import org.sufficientlysecure.keychain.support.TestDataUtil;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Random;
+
+@RunWith(RobolectricTestRunner.class)
+@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+public class PgpKeyOperationTest {
+
+ static UncachedKeyRing staticRing;
+ static String passphrase;
+
+ UncachedKeyRing ring;
+ PgpKeyOperation op;
+ SaveKeyringParcel parcel;
+ ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
+ ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
+
+ @BeforeClass public static void setUpOnce() throws Exception {
+ ShadowLog.stream = System.out;
+
+ {
+ String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
+ Random r = new Random();
+ StringBuilder passbuilder = new StringBuilder();
+ // 20% chance for an empty passphrase
+ for(int i = 0, j = r.nextInt(10) > 2 ? r.nextInt(20) : 0; i < j; i++) {
+ passbuilder.append(chars.charAt(r.nextInt(chars.length())));
+ }
+ passphrase = passbuilder.toString();
+ System.out.println("Passphrase is '" + passphrase + "'");
+ }
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.ENCRYPT_COMMS, null));
+
+ parcel.mAddUserIds.add("twi");
+ parcel.mAddUserIds.add("pink");
+ parcel.mNewPassphrase = passphrase;
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ staticRing = op.createSecretKeyRing(parcel).getRing();
+
+ Assert.assertNotNull("initial test key creation must succeed", staticRing);
+
+ // we sleep here for a second, to make sure all new certificates have different timestamps
+ Thread.sleep(1000);
+ }
+
+ @Before public void setUp() throws Exception {
+ // show Log.x messages in system.out
+ ShadowLog.stream = System.out;
+ ring = staticRing;
+
+ // setting up some parameters just to reduce code duplication
+ op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
+
+ // set this up, gonna need it more than once
+ parcel = new SaveKeyringParcel();
+ parcel.mMasterKeyId = ring.getMasterKeyId();
+ parcel.mFingerprint = ring.getFingerprint();
+
+ }
+
+ @Test
+ public void createSecretKeyRingTests() {
+
+ {
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, new Random().nextInt(256)+255, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddUserIds.add("shy");
+ parcel.mNewPassphrase = passphrase;
+
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
+
+ Assert.assertNull("creating ring with < 512 bytes keysize should fail", ring);
+ }
+
+ {
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.elgamal, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddUserIds.add("shy");
+ parcel.mNewPassphrase = passphrase;
+
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
+
+ Assert.assertNull("creating ring with ElGamal master key should fail", ring);
+ }
+
+ {
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ 12345, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddUserIds.add("shy");
+ parcel.mNewPassphrase = passphrase;
+
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
+ Assert.assertNull("creating ring with bad algorithm choice should fail", ring);
+ }
+
+ {
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ parcel.mAddUserIds.add("shy");
+ parcel.mNewPassphrase = passphrase;
+
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
+ Assert.assertNull("creating ring with non-certifying master key should fail", ring);
+ }
+
+ {
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mNewPassphrase = passphrase;
+
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
+ Assert.assertNull("creating ring without user ids should fail", ring);
+ }
+
+ {
+ parcel.reset();
+ parcel.mAddUserIds.add("shy");
+ parcel.mNewPassphrase = passphrase;
+
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
+ Assert.assertNull("creating ring without subkeys should fail", ring);
+ }
+
+ }
+
+ @Test
+ // this is a special case since the flags are in user id certificates rather than
+ // subkey binding certificates
+ public void testMasterFlags() throws Exception {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, null));
+ parcel.mAddUserIds.add("luna");
+ ring = op.createSecretKeyRing(parcel).getRing();
+
+ Assert.assertEquals("the keyring should contain only the master key",
+ 1, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
+ Assert.assertEquals("first (master) key must have both flags",
+ KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, ring.getPublicKey().getKeyUsage());
+
+ }
+
+ @Test
+ public void testCreatedKey() throws Exception {
+
+ // an empty modification should change nothing. this also ensures the keyring
+ // is constant through canonicalization.
+ // applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+
+ Assert.assertNotNull("key creation failed", ring);
+
+ Assert.assertNull("primary user id must be empty",
+ ring.getPublicKey().getPrimaryUserId());
+
+ Assert.assertEquals("number of user ids must be two",
+ 2, ring.getPublicKey().getUnorderedUserIds().size());
+
+ Assert.assertEquals("number of subkeys must be three",
+ 3, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
+
+ Assert.assertTrue("key ring should have been created in the last 120 seconds",
+ ring.getPublicKey().getCreationTime().after(new Date(new Date().getTime()-1000*120)));
+
+ Assert.assertNull("key ring should not expire",
+ ring.getPublicKey().getExpiryTime());
+
+ Iterator<UncachedPublicKey> it = ring.getPublicKeys();
+
+ Assert.assertEquals("first (master) key can certify",
+ KeyFlags.CERTIFY_OTHER, it.next().getKeyUsage());
+
+ UncachedPublicKey signingKey = it.next();
+ Assert.assertEquals("second key can sign",
+ KeyFlags.SIGN_DATA, signingKey.getKeyUsage());
+ ArrayList<WrappedSignature> sigs = signingKey.getSignatures().next().getEmbeddedSignatures();
+ Assert.assertEquals("signing key signature should have one embedded signature",
+ 1, sigs.size());
+ Assert.assertEquals("embedded signature should be of primary key binding type",
+ PGPSignature.PRIMARYKEY_BINDING, sigs.get(0).getSignatureType());
+ Assert.assertEquals("primary key binding signature issuer should be signing subkey",
+ signingKey.getKeyId(), sigs.get(0).getKeyId());
+
+ Assert.assertEquals("third key can encrypt",
+ KeyFlags.ENCRYPT_COMMS, it.next().getKeyUsage());
+
+ }
+
+ @Test
+ public void testBadKeyModification() throws Exception {
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ // off by one
+ parcel.mMasterKeyId = ring.getMasterKeyId() -1;
+ parcel.mFingerprint = ring.getFingerprint();
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("keyring modification with bad master key id should fail", modified);
+ }
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ // off by one
+ parcel.mMasterKeyId = null;
+ parcel.mFingerprint = ring.getFingerprint();
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("keyring modification with null master key id should fail", modified);
+ }
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mMasterKeyId = ring.getMasterKeyId();
+ parcel.mFingerprint = ring.getFingerprint();
+ // some byte, off by one
+ parcel.mFingerprint[5] += 1;
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("keyring modification with bad fingerprint should fail", modified);
+ }
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mMasterKeyId = ring.getMasterKeyId();
+ parcel.mFingerprint = null;
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("keyring modification with null fingerprint should fail", modified);
+ }
+
+ {
+ String badphrase = "";
+ if (badphrase.equals(passphrase)) {
+ badphrase = "a";
+ }
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, badphrase).getRing();
+
+ Assert.assertNull("keyring modification with bad passphrase should fail", modified);
+ }
+
+ }
+
+ @Test
+ public void testSubkeyAdd() throws Exception {
+
+ long expiry = new Date().getTime() / 1000 + 159;
+ int flags = KeyFlags.SIGN_DATA;
+ int bits = 1024 + new Random().nextInt(8);
+ parcel.mAddSubKeys.add(new SubkeyAdd(algorithm.rsa, bits, flags, expiry));
+
+ UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+
+ Assert.assertEquals("no extra packets in original", 0, onlyA.size());
+ Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size());
+
+ Packet p;
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("first new packet must be secret subkey", p instanceof SecretSubkeyPacket);
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(1).buf)).readPacket();
+ Assert.assertTrue("second new packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("signature type must be subkey binding certificate",
+ PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ // get new key from ring. it should be the last one (add a check to make sure?)
+ UncachedPublicKey newKey = null;
+ {
+ Iterator<UncachedPublicKey> it = modified.getPublicKeys();
+ while (it.hasNext()) {
+ newKey = it.next();
+ }
+ }
+
+ Assert.assertNotNull("new key is not null", newKey);
+ Assert.assertNotNull("added key must have an expiry date",
+ newKey.getExpiryTime());
+ Assert.assertEquals("added key must have expected expiry date",
+ expiry, newKey.getExpiryTime().getTime()/1000);
+ Assert.assertEquals("added key must have expected flags",
+ flags, newKey.getKeyUsage());
+ Assert.assertEquals("added key must have expected bitsize",
+ bits, newKey.getBitStrength());
+
+ { // bad keysize should fail
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SubkeyAdd(
+ algorithm.rsa, new Random().nextInt(512), KeyFlags.SIGN_DATA, null));
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("creating a subkey with keysize < 512 should fail", modified);
+ }
+
+ { // a past expiry should fail
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.SIGN_DATA,
+ new Date().getTime()/1000-10));
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("creating subkey with past expiry date should fail", modified);
+ }
+
+ }
+
+ @Test
+ public void testSubkeyModify() throws Exception {
+
+ long expiry = new Date().getTime()/1000 + 1024;
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
+
+ UncachedKeyRing modified = ring;
+ {
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry));
+ modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
+
+ Assert.assertEquals("one extra packet in original", 1, onlyA.size());
+ Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
+
+ Assert.assertEquals("old packet must be signature",
+ PacketTags.SIGNATURE, onlyA.get(0).tag);
+
+ Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("first new packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("signature type must be subkey binding certificate",
+ PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ Assert.assertNotNull("modified key must have an expiry date",
+ modified.getPublicKey(keyId).getExpiryTime());
+ Assert.assertEquals("modified key must have expected expiry date",
+ expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
+ Assert.assertEquals("modified key must have same flags as before",
+ ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());
+ }
+
+ {
+ int flags = KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS;
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, flags, null));
+ modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
+
+ Assert.assertEquals("old packet must be signature",
+ PacketTags.SIGNATURE, onlyA.get(0).tag);
+
+ Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("first new packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("signature type must be subkey binding certificate",
+ PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ Assert.assertEquals("modified key must have expected flags",
+ flags, modified.getPublicKey(keyId).getKeyUsage());
+ Assert.assertNotNull("key must retain its expiry",
+ modified.getPublicKey(keyId).getExpiryTime());
+ Assert.assertEquals("key expiry must be unchanged",
+ expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
+ }
+
+ { // a past expiry should fail
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10));
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("setting subkey expiry to a past date should fail", modified);
+ }
+
+ { // modifying nonexistent keyring should fail
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(123, null, null));
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("modifying non-existent subkey should fail", modified);
+ }
+
+ }
+
+ @Test
+ public void testSubkeyRevoke() throws Exception {
+
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
+ int flags = ring.getPublicKey(keyId).getKeyUsage();
+
+ UncachedKeyRing modified;
+
+ {
+
+ parcel.reset();
+ parcel.mRevokeSubKeys.add(123L);
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("revoking a nonexistent subkey should fail", otherModified);
+
+ }
+
+ { // revoked second subkey
+
+ parcel.reset();
+ parcel.mRevokeSubKeys.add(keyId);
+
+ modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+
+ Assert.assertEquals("no extra packets in original", 0, onlyA.size());
+ Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
+
+ Packet p;
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("first new packet must be secret subkey", p instanceof SignaturePacket);
+ Assert.assertEquals("signature type must be subkey binding certificate",
+ PGPSignature.SUBKEY_REVOCATION, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ Assert.assertTrue("subkey must actually be revoked",
+ modified.getPublicKey(keyId).isRevoked());
+ }
+
+ { // re-add second subkey
+
+ parcel.reset();
+ parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, null));
+
+ modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
+
+ Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
+ Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
+
+ Packet p;
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
+ Assert.assertTrue("first outdated packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("first outdated signature type must be subkey binding certification",
+ PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("first outdated signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
+ Assert.assertTrue("second outdated packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("second outdated signature type must be subkey revocation",
+ PGPSignature.SUBKEY_REVOCATION, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("second outdated signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("new packet must be signature ", p instanceof SignaturePacket);
+ Assert.assertEquals("new signature type must be subkey binding certification",
+ PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ Assert.assertFalse("subkey must no longer be revoked",
+ modified.getPublicKey(keyId).isRevoked());
+ Assert.assertEquals("subkey must have the same usage flags as before",
+ flags, modified.getPublicKey(keyId).getKeyUsage());
+
+ }
+ }
+
+ @Test
+ public void testUserIdRevoke() throws Exception {
+
+ UncachedKeyRing modified;
+ String uid = ring.getPublicKey().getUnorderedUserIds().get(1);
+
+ { // revoke second user id
+
+ parcel.mRevokeUserIds.add(uid);
+
+ modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+
+ Assert.assertEquals("no extra packets in original", 0, onlyA.size());
+ Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
+
+ Packet p;
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("first new packet must be secret subkey", p instanceof SignaturePacket);
+ Assert.assertEquals("signature type must be subkey binding certificate",
+ PGPSignature.CERTIFICATION_REVOCATION, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ }
+
+ { // re-add second user id
+
+ parcel.reset();
+ parcel.mChangePrimaryUserId = uid;
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
+ UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("setting primary user id to a revoked user id should fail", otherModified);
+
+ }
+
+ { // re-add second user id
+
+ parcel.reset();
+ parcel.mAddUserIds.add(uid);
+
+ applyModificationWithChecks(parcel, modified, onlyA, onlyB);
+
+ Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
+ Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
+
+ Packet p;
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
+ Assert.assertTrue("first outdated packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("first outdated signature type must be positive certification",
+ PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("first outdated signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
+ Assert.assertTrue("second outdated packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("second outdated signature type must be certificate revocation",
+ PGPSignature.CERTIFICATION_REVOCATION, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("second outdated signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("new packet must be signature ", p instanceof SignaturePacket);
+ Assert.assertEquals("new signature type must be positive certification",
+ PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
+ Assert.assertEquals("signature must have been created by master key",
+ ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
+ }
+
+ }
+
+ @Test
+ public void testUserIdAdd() throws Exception {
+
+ {
+ parcel.mAddUserIds.add("");
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+ Assert.assertNull("adding an empty user id should fail", modified);
+ }
+
+ parcel.reset();
+ parcel.mAddUserIds.add("rainbow");
+
+ UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+
+ Assert.assertTrue("keyring must contain added user id",
+ modified.getPublicKey().getUnorderedUserIds().contains("rainbow"));
+
+ Assert.assertEquals("no extra packets in original", 0, onlyA.size());
+ Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size());
+
+ Assert.assertTrue("keyring must contain added user id",
+ modified.getPublicKey().getUnorderedUserIds().contains("rainbow"));
+
+ Packet p;
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
+ Assert.assertTrue("first new packet must be user id", p instanceof UserIDPacket);
+ Assert.assertEquals("user id packet must match added user id",
+ "rainbow", ((UserIDPacket) p).getID());
+
+ p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(1).buf)).readPacket();
+ Assert.assertTrue("second new packet must be signature", p instanceof SignaturePacket);
+ Assert.assertEquals("signature type must be positive certification",
+ PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
+
+ }
+
+ @Test
+ public void testUserIdPrimary() throws Exception {
+
+ UncachedKeyRing modified = ring;
+ String uid = ring.getPublicKey().getUnorderedUserIds().get(1);
+
+ { // first part, add new user id which is also primary
+ parcel.mAddUserIds.add("jack");
+ parcel.mChangePrimaryUserId = "jack";
+
+ modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
+
+ Assert.assertEquals("primary user id must be the one added",
+ "jack", modified.getPublicKey().getPrimaryUserId());
+ }
+
+ { // second part, change primary to a different one
+ parcel.reset();
+ parcel.mChangePrimaryUserId = uid;
+
+ modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
+
+ Assert.assertEquals("old keyring must have two outdated certificates", 2, onlyA.size());
+ Assert.assertEquals("new keyring must have two new packets", 2, onlyB.size());
+
+ Assert.assertEquals("primary user id must be the one changed to",
+ "pink", modified.getPublicKey().getPrimaryUserId());
+ }
+
+ { // third part, change primary to a non-existent one
+ parcel.reset();
+ //noinspection SpellCheckingInspection
+ parcel.mChangePrimaryUserId = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ if (parcel.mChangePrimaryUserId.equals(passphrase)) {
+ parcel.mChangePrimaryUserId += "A";
+ }
+
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+
+ Assert.assertNull("changing primary user id to a non-existent one should fail", modified);
+ }
+
+ // check for revoked primary user id already done in revoke test
+
+ }
+
+
+ private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
+ UncachedKeyRing ring,
+ ArrayList<RawPacket> onlyA,
+ ArrayList<RawPacket> onlyB) {
+ return applyModificationWithChecks(parcel, ring, onlyA, onlyB, true, true);
+ }
+
+ // applies a parcel modification while running some integrity checks
+ private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
+ UncachedKeyRing ring,
+ ArrayList<RawPacket> onlyA,
+ ArrayList<RawPacket> onlyB,
+ boolean canonicalize,
+ boolean constantCanonicalize) {
+
+ try {
+
+ Assert.assertTrue("modified keyring must be secret", ring.isSecret());
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+
+ PgpKeyOperation op = new PgpKeyOperation(null);
+ UncachedKeyRing rawModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+ Assert.assertNotNull("key modification failed", rawModified);
+
+ if (!canonicalize) {
+ Assert.assertTrue("keyring must differ from original", KeyringTestingHelper.diffKeyrings(
+ ring.getEncoded(), rawModified.getEncoded(), onlyA, onlyB));
+ return rawModified;
+ }
+
+ CanonicalizedKeyRing modified = rawModified.canonicalize(new OperationLog(), 0);
+ if (constantCanonicalize) {
+ Assert.assertTrue("key must be constant through canonicalization",
+ !KeyringTestingHelper.diffKeyrings(
+ modified.getEncoded(), rawModified.getEncoded(), onlyA, onlyB)
+ );
+ }
+ Assert.assertTrue("keyring must differ from original", KeyringTestingHelper.diffKeyrings(
+ ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
+
+ return modified.getUncachedKeyRing();
+
+ } catch (IOException e) {
+ throw new AssertionFailedError("error during encoding!");
+ }
+ }
+
+ @Test
+ public void testVerifySuccess() throws Exception {
+
+ UncachedKeyRing expectedKeyRing = KeyringBuilder.correctRing();
+ UncachedKeyRing inputKeyRing = KeyringBuilder.ringWithExtraIncorrectSignature();
+
+ CanonicalizedKeyRing canonicalized = inputKeyRing.canonicalize(new OperationLog(), 0);
+ Assert.assertNotNull("canonicalization must succeed", canonicalized);
+
+ ArrayList onlyA = new ArrayList<RawPacket>();
+ ArrayList onlyB = new ArrayList<RawPacket>();
+ //noinspection unchecked
+ Assert.assertTrue("keyrings differ", !KeyringTestingHelper.diffKeyrings(
+ expectedKeyRing.getEncoded(), expectedKeyRing.getEncoded(), onlyA, onlyB));
+
+ }
+
+ /**
+ * Just testing my own test code. Should really be using a library for this.
+ */
+ @Test
+ public void testConcat() throws Exception {
+ byte[] actual = TestDataUtil.concatAll(new byte[]{1}, new byte[]{2,-2}, new byte[]{5},new byte[]{3});
+ byte[] expected = new byte[]{1,2,-2,5,3};
+ Assert.assertEquals(java.util.Arrays.toString(expected), java.util.Arrays.toString(actual));
+ }
+
+
+}
diff --git a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/ProviderHelperKeyringTest.java
index 3d48c2f97..665e8ef2b 100644
--- a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/ProviderHelperKeyringTest.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) Art O Cathain
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
package tests;
import java.util.Collections;
@@ -9,9 +26,7 @@ import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.*;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.sufficientlysecure.keychain.testsupport.KeyringTestingHelper;
-import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
@RunWith(RobolectricTestRunner.class)
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
@@ -24,7 +39,7 @@ public class ProviderHelperKeyringTest {
)));
}
- @Test
+ // @Test
public void testSavePublicKeyringRsa() throws Exception {
Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList(
"000001-006.public_key",
@@ -45,7 +60,7 @@ public class ProviderHelperKeyringTest {
))));
}
- @Test
+ // @Test
public void testSavePublicKeyringDsa() throws Exception {
Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList(
"000016-006.public_key",
@@ -62,7 +77,7 @@ public class ProviderHelperKeyringTest {
))));
}
- @Test
+ // @Test
public void testSavePublicKeyringDsa2() throws Exception {
Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring(prependResourcePath(Arrays.asList(
"000027-006.public_key",
@@ -79,7 +94,7 @@ public class ProviderHelperKeyringTest {
private static Collection<String> prependResourcePath(Collection<String> files) {
Collection<String> prependedFiles = new ArrayList<String>();
for (String file: files) {
- prependedFiles.add("/extern/OpenPGP-Haskell/tests/data/" + file);
+ prependedFiles.add("/OpenPGP-Haskell/tests/data/" + file);
}
return prependedFiles;
}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java
new file mode 100644
index 000000000..97e0d8a68
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java
@@ -0,0 +1,627 @@
+package org.sufficientlysecure.keychain.tests;
+
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.Before;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.BCPGInputStream;
+import org.spongycastle.bcpg.Packet;
+import org.spongycastle.bcpg.PacketTags;
+import org.spongycastle.bcpg.UserIDPacket;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+
+/** Tests for the UncachedKeyring.canonicalize method.
+ *
+ * This is a complex and crypto-relevant method, which takes care of sanitizing keyrings.
+ * Test cases are made for all its assertions.
+ */
+
+@RunWith(RobolectricTestRunner.class)
+@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+public class UncachedKeyringCanonicalizeTest {
+
+ static UncachedKeyRing staticRing;
+ static int totalPackets;
+ UncachedKeyRing ring;
+ ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
+ ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ PGPSignatureSubpacketGenerator subHashedPacketsGen;
+ PGPSecretKey secretKey;
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ ShadowLog.stream = System.out;
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.ENCRYPT_COMMS, null));
+
+ parcel.mAddUserIds.add("twi");
+ parcel.mAddUserIds.add("pink");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ Assert.assertTrue("initial test key creation must succeed", result.success());
+ staticRing = result.getRing();
+ Assert.assertNotNull("initial test key creation must succeed", staticRing);
+
+ // just for later reference
+ totalPackets = 9;
+
+ // we sleep here for a second, to make sure all new certificates have different timestamps
+ Thread.sleep(1000);
+ }
+
+ @Before public void setUp() throws Exception {
+ // show Log.x messages in system.out
+ ShadowLog.stream = System.out;
+ ring = staticRing;
+
+ subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ secretKey = new PGPSecretKeyRing(ring.getEncoded(), new JcaKeyFingerprintCalculator())
+ .getSecretKey();
+ }
+
+ /** Make sure the assumptions made about the generated ring packet structure are valid. */
+ @Test public void testGeneratedRingStructure() throws Exception {
+
+ Iterator<RawPacket> it = KeyringTestingHelper.parseKeyring(ring.getEncoded());
+
+ Assert.assertEquals("packet #1 should be secret key",
+ PacketTags.SECRET_KEY, it.next().tag);
+
+ Assert.assertEquals("packet #2 should be user id",
+ PacketTags.USER_ID, it.next().tag);
+ Assert.assertEquals("packet #3 should be signature",
+ PacketTags.SIGNATURE, it.next().tag);
+
+ Assert.assertEquals("packet #4 should be user id",
+ PacketTags.USER_ID, it.next().tag);
+ Assert.assertEquals("packet #5 should be signature",
+ PacketTags.SIGNATURE, it.next().tag);
+
+ Assert.assertEquals("packet #6 should be secret subkey",
+ PacketTags.SECRET_SUBKEY, it.next().tag);
+ Assert.assertEquals("packet #7 should be signature",
+ PacketTags.SIGNATURE, it.next().tag);
+
+ Assert.assertEquals("packet #8 should be secret subkey",
+ PacketTags.SECRET_SUBKEY, it.next().tag);
+ Assert.assertEquals("packet #9 should be signature",
+ PacketTags.SIGNATURE, it.next().tag);
+
+ Assert.assertFalse("exactly 9 packets total", it.hasNext());
+
+ Assert.assertArrayEquals("created keyring should be constant through canonicalization",
+ ring.getEncoded(), ring.canonicalize(log, 0).getEncoded());
+
+ }
+
+ @Test public void testUidSignature() throws Exception {
+
+ UncachedPublicKey masterKey = ring.getPublicKey();
+ final WrappedSignature sig = masterKey.getSignaturesForId("twi").next();
+
+ byte[] raw = sig.getEncoded();
+ // destroy the signature
+ raw[raw.length - 5] += 1;
+ final WrappedSignature brokenSig = WrappedSignature.fromBytes(raw);
+
+ { // bad certificates get stripped
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, brokenSig.getEncoded(), 3);
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+
+ Assert.assertTrue("canonicalized keyring with invalid extra sig must be same as original one",
+ !KeyringTestingHelper.diffKeyrings(
+ ring.getEncoded(), canonicalized.getEncoded(), onlyA, onlyB));
+ }
+
+ // remove user id certificate for one user
+ final UncachedKeyRing base = KeyringTestingHelper.removePacket(ring, 2);
+
+ { // user id without certificate should be removed
+ CanonicalizedKeyRing modified = base.canonicalize(log, 0);
+ Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings(
+ ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
+
+ Assert.assertEquals("two packets should be stripped after canonicalization", 2, onlyA.size());
+ Assert.assertEquals("no new packets after canonicalization", 0, onlyB.size());
+
+ Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
+ Assert.assertTrue("first stripped packet must be user id", p instanceof UserIDPacket);
+ Assert.assertEquals("missing user id must be the expected one",
+ "twi", ((UserIDPacket) p).getID());
+
+ Assert.assertArrayEquals("second stripped packet must be signature we removed",
+ sig.getEncoded(), onlyA.get(1).buf);
+
+ }
+
+ { // add error to signature
+
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(base, brokenSig.getEncoded(), 3);
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+
+ Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings(
+ ring.getEncoded(), canonicalized.getEncoded(), onlyA, onlyB));
+
+ Assert.assertEquals("two packets should be missing after canonicalization", 2, onlyA.size());
+ Assert.assertEquals("no new packets after canonicalization", 0, onlyB.size());
+
+ Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
+ Assert.assertTrue("first stripped packet must be user id", p instanceof UserIDPacket);
+ Assert.assertEquals("missing user id must be the expected one",
+ "twi", ((UserIDPacket) p).getID());
+
+ Assert.assertArrayEquals("second stripped packet must be signature we removed",
+ sig.getEncoded(), onlyA.get(1).buf);
+ }
+
+ }
+
+ @Test public void testUidDestroy() throws Exception {
+
+ // signature for "twi"
+ ring = KeyringTestingHelper.removePacket(ring, 2);
+ // signature for "pink"
+ ring = KeyringTestingHelper.removePacket(ring, 3);
+
+ // canonicalization should fail, because there are no valid uids left
+ CanonicalizedKeyRing canonicalized = ring.canonicalize(log, 0);
+ Assert.assertNull("canonicalization of keyring with no valid uids should fail", canonicalized);
+
+ }
+
+ @Test public void testRevocationRedundant() throws Exception {
+
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.KEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey());
+
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 1);
+
+ // try to add the same packet again, it should be rejected in all positions
+ injectEverywhere(modified, revocation.getEncoded());
+
+ // an older (but different!) revocation should be rejected as well
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ revocation = forgeSignature(
+ secretKey, PGPSignature.KEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey());
+
+ injectEverywhere(modified, revocation.getEncoded());
+
+ }
+
+ @Test public void testUidRedundant() throws Exception {
+
+ // an older uid certificate should be rejected
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.DEFAULT_CERTIFICATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ injectEverywhere(ring, revocation.getEncoded());
+
+ }
+
+ @Test public void testUidRevocationOutdated() throws Exception {
+ // an older uid revocation cert should be rejected
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ injectEverywhere(ring, revocation.getEncoded());
+
+ }
+
+ @Test public void testUidRevocationRedundant() throws Exception {
+
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ // add that revocation to the base, and check if the redundant one will be rejected as well
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 2);
+
+ injectEverywhere(modified, revocation.getEncoded());
+
+ // an older (but different!) uid revocation should be rejected as well
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ revocation = forgeSignature(
+ secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ injectEverywhere(modified, revocation.getEncoded());
+
+ }
+
+ @Test public void testSignatureBroken() throws Exception {
+
+ injectEverytype(secretKey, ring, subHashedPacketsGen, true);
+
+ }
+
+ @Test public void testForeignSignature() throws Exception {
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddUserIds.add("trix");
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ UncachedKeyRing foreign = op.createSecretKeyRing(parcel).getRing();
+
+ Assert.assertNotNull("initial test key creation must succeed", foreign);
+ PGPSecretKey foreignSecretKey =
+ new PGPSecretKeyRing(foreign.getEncoded(), new JcaKeyFingerprintCalculator())
+ .getSecretKey();
+
+ injectEverytype(foreignSecretKey, ring, subHashedPacketsGen);
+
+ }
+
+ @Test public void testSignatureFuture() throws Exception {
+
+ // generate future
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() + 1000 * 1000));
+
+ injectEverytype(secretKey, ring, subHashedPacketsGen);
+
+
+ }
+
+ @Test public void testSignatureLocal() throws Exception {
+
+ // generate future
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setExportable(false, false);
+
+ injectEverytype(secretKey, ring, subHashedPacketsGen);
+
+ }
+
+ @Test public void testSubkeyDestroy() throws Exception {
+
+ // signature for second key (first subkey)
+ UncachedKeyRing modified = KeyringTestingHelper.removePacket(ring, 6);
+
+ // canonicalization should fail, because there are no valid uids left
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertTrue("keyring with missing subkey binding sig should differ from intact one after canonicalization",
+ KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+
+ Assert.assertEquals("canonicalized keyring should have two extra packets", 2, onlyA.size());
+ Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size());
+
+ Assert.assertEquals("first missing packet should be the subkey",
+ PacketTags.SECRET_SUBKEY, onlyA.get(0).tag);
+ Assert.assertEquals("second missing packet should be subkey's signature",
+ PacketTags.SIGNATURE, onlyA.get(1).tag);
+ Assert.assertEquals("second missing packet should be next to subkey",
+ onlyA.get(0).position + 1, onlyA.get(1).position);
+
+ }
+
+ @Test public void testSubkeyBindingNoPKB() throws Exception {
+
+ UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 1);
+ Assert.assertTrue("second subkey must be able to sign", pKey.canSign());
+
+ PGPSignature sig;
+
+ subHashedPacketsGen.setKeyFlags(false, KeyFlags.SIGN_DATA);
+
+ {
+ // forge a (newer) signature, which has the sign flag but no primary key binding sig
+ PGPSignatureSubpacketGenerator unhashedSubs = new PGPSignatureSubpacketGenerator();
+
+ // just add any random signature, because why not
+ unhashedSubs.setEmbeddedSignature(false, forgeSignature(
+ secretKey, PGPSignature.POSITIVE_CERTIFICATION, subHashedPacketsGen,
+ secretKey.getPublicKey()
+ )
+ );
+
+ sig = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, unhashedSubs,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ // inject in the right position
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig.getEncoded(), 6);
+
+ // canonicalize, and check if we lose the bad signature
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertFalse("subkey binding signature should be gone after canonicalization",
+ KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+ }
+
+ { // now try one with a /bad/ primary key binding signature
+
+ PGPSignatureSubpacketGenerator unhashedSubs = new PGPSignatureSubpacketGenerator();
+ // this one is signed by the primary key itself, not the subkey - but it IS primary binding
+ unhashedSubs.setEmbeddedSignature(false, forgeSignature(
+ secretKey, PGPSignature.PRIMARYKEY_BINDING, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey()
+ )
+ );
+
+ sig = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, unhashedSubs,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ // inject in the right position
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig.getEncoded(), 6);
+
+ // canonicalize, and check if we lose the bad signature
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertFalse("subkey binding signature should be gone after canonicalization",
+ KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+ }
+
+ }
+
+ @Test public void testSubkeyBindingRedundant() throws Exception {
+
+ UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 2);
+
+ subHashedPacketsGen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS);
+ PGPSignature sig2 = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ PGPSignature sig1 = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_REVOCATION, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -100*1000));
+ PGPSignature sig3 = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig1.getEncoded(), 8);
+ modified = KeyringTestingHelper.injectPacket(modified, sig2.getEncoded(), 9);
+ modified = KeyringTestingHelper.injectPacket(modified, sig1.getEncoded(), 10);
+ modified = KeyringTestingHelper.injectPacket(modified, sig3.getEncoded(), 11);
+
+ // canonicalize, and check if we lose the bad signature
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertTrue("subkey binding signature should be gone after canonicalization",
+ KeyringTestingHelper.diffKeyrings(modified.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+
+ Assert.assertEquals("canonicalized keyring should have lost two packets", 3, onlyA.size());
+ Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size());
+
+ Assert.assertEquals("first missing packet should be the subkey",
+ PacketTags.SIGNATURE, onlyA.get(0).tag);
+ Assert.assertEquals("second missing packet should be a signature",
+ PacketTags.SIGNATURE, onlyA.get(1).tag);
+ Assert.assertEquals("second missing packet should be a signature",
+ PacketTags.SIGNATURE, onlyA.get(2).tag);
+
+ }
+
+ private static final int[] sigtypes_direct = new int[] {
+ PGPSignature.KEY_REVOCATION,
+ PGPSignature.DIRECT_KEY,
+ };
+ private static final int[] sigtypes_uid = new int[] {
+ PGPSignature.DEFAULT_CERTIFICATION,
+ PGPSignature.NO_CERTIFICATION,
+ PGPSignature.CASUAL_CERTIFICATION,
+ PGPSignature.POSITIVE_CERTIFICATION,
+ PGPSignature.CERTIFICATION_REVOCATION,
+ };
+ private static final int[] sigtypes_subkey = new int[] {
+ PGPSignature.SUBKEY_BINDING,
+ PGPSignature.PRIMARYKEY_BINDING,
+ PGPSignature.SUBKEY_REVOCATION,
+ };
+
+ private static void injectEverytype(PGPSecretKey secretKey,
+ UncachedKeyRing ring,
+ PGPSignatureSubpacketGenerator subHashedPacketsGen)
+ throws Exception {
+ injectEverytype(secretKey, ring, subHashedPacketsGen, false);
+ }
+
+ private static void injectEverytype(PGPSecretKey secretKey,
+ UncachedKeyRing ring,
+ PGPSignatureSubpacketGenerator subHashedPacketsGen,
+ boolean breakSig)
+ throws Exception {
+
+ for (int sigtype : sigtypes_direct) {
+ PGPSignature sig = forgeSignature(
+ secretKey, sigtype, subHashedPacketsGen, secretKey.getPublicKey());
+ byte[] encoded = sig.getEncoded();
+ if (breakSig) {
+ encoded[encoded.length-10] += 1;
+ }
+ injectEverywhere(ring, encoded);
+ }
+
+ for (int sigtype : sigtypes_uid) {
+ PGPSignature sig = forgeSignature(
+ secretKey, sigtype, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ byte[] encoded = sig.getEncoded();
+ if (breakSig) {
+ encoded[encoded.length-10] += 1;
+ }
+ injectEverywhere(ring, encoded);
+ }
+
+ for (int sigtype : sigtypes_subkey) {
+ PGPSignature sig = forgeSignature(
+ secretKey, sigtype, subHashedPacketsGen,
+ secretKey.getPublicKey(), secretKey.getPublicKey());
+
+ byte[] encoded = sig.getEncoded();
+ if (breakSig) {
+ encoded[encoded.length-10] += 1;
+ }
+ injectEverywhere(ring, encoded);
+ }
+
+ }
+
+ private static void injectEverywhere(UncachedKeyRing ring, byte[] packet) throws Exception {
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+
+ byte[] encodedRing = ring.getEncoded();
+
+ for(int i = 0; i < totalPackets; i++) {
+
+ byte[] brokenEncoded = KeyringTestingHelper.injectPacket(encodedRing, packet, i);
+
+ try {
+
+ UncachedKeyRing brokenRing = UncachedKeyRing.decodeFromData(brokenEncoded);
+
+ CanonicalizedKeyRing canonicalized = brokenRing.canonicalize(log, 0);
+ if (canonicalized == null) {
+ System.out.println("ok, canonicalization failed.");
+ continue;
+ }
+
+ Assert.assertArrayEquals("injected bad signature must be gone after canonicalization",
+ ring.getEncoded(), canonicalized.getEncoded());
+
+ } catch (Exception e) {
+ System.out.println("ok, rejected with: " + e.getMessage());
+ }
+ }
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator subpackets,
+ PGPPublicKey publicKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(subpackets.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(publicKey);
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator subpackets,
+ String userId, PGPPublicKey publicKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(subpackets.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(userId, publicKey);
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator subpackets,
+ PGPPublicKey publicKey, PGPPublicKey signedKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(subpackets.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(publicKey, signedKey);
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator hashedSubs,
+ PGPSignatureSubpacketGenerator unhashedSubs,
+ PGPPublicKey publicKey, PGPPublicKey signedKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(hashedSubs.generate());
+ sGen.setUnhashedSubpackets(unhashedSubs.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(publicKey, signedKey);
+
+ }
+
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java
new file mode 100644
index 000000000..977ddfc52
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java
@@ -0,0 +1,397 @@
+package org.sufficientlysecure.keychain.tests;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.PacketTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/** Tests for the UncachedKeyring.merge method.
+ *
+ * This is another complex, crypto-related method. It merges information from one keyring into
+ * another, keeping information from the base (ie, called object) keyring in case of conflicts.
+ * The types of keys may be Public or Secret and can be mixed, For mixed types the result type
+ * will be the same as the base keyring.
+ *
+ * Test cases:
+ * - Merging keyrings with different masterKeyIds should fail
+ * - Merging a key with itself should be a no-operation
+ * - Merging a key with an extra revocation certificate, it should have that certificate
+ * - Merging a key with an extra user id, it should have that extra user id and its certificates
+ * - Merging a key with an extra user id certificate, it should have that certificate
+ * - Merging a key with an extra subkey, it should have that subkey
+ * - Merging a key with an extra subkey certificate, it should have that certificate
+ * - All of the above operations should work regardless of the key types. This means in particular
+ * that for new subkeys, an equivalent subkey of the proper type must be generated.
+ * - In case of two secret keys with the same id but different S2K, the key of the base keyring
+ * should be preferred (TODO or should it?)
+ *
+ * Note that the merge operation does not care about certificate validity, a bad certificate or
+ * packet will be copied regardless. Filtering out bad packets is done with canonicalization.
+ *
+ */
+@RunWith(RobolectricTestRunner.class)
+@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+public class UncachedKeyringMergeTest {
+
+ static UncachedKeyRing staticRingA, staticRingB;
+ UncachedKeyRing ringA, ringB;
+ ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
+ ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ PgpKeyOperation op;
+ SaveKeyringParcel parcel;
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ ShadowLog.stream = System.out;
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+
+ parcel.mAddUserIds.add("twi");
+ parcel.mAddUserIds.add("pink");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ staticRingA = result.getRing();
+ }
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+
+ parcel.mAddUserIds.add("shy");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ staticRingB = result.getRing();
+ }
+
+ Assert.assertNotNull("initial test key creation must succeed", staticRingA);
+ Assert.assertNotNull("initial test key creation must succeed", staticRingB);
+
+ // we sleep here for a second, to make sure all new certificates have different timestamps
+ Thread.sleep(1000);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ // show Log.x messages in system.out
+ ShadowLog.stream = System.out;
+ ringA = staticRingA;
+ ringB = staticRingB;
+
+ // setting up some parameters just to reduce code duplication
+ op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
+
+ // set this up, gonna need it more than once
+ parcel = new SaveKeyringParcel();
+ parcel.mMasterKeyId = ringA.getMasterKeyId();
+ parcel.mFingerprint = ringA.getFingerprint();
+ }
+
+ public void testSelfNoOp() throws Exception {
+
+ UncachedKeyRing merged = mergeWithChecks(ringA, ringA, null);
+ Assert.assertArrayEquals("keyring merged with itself must be identical",
+ ringA.getEncoded(), merged.getEncoded()
+ );
+
+ }
+
+ @Test
+ public void testDifferentMasterKeyIds() throws Exception {
+
+ Assert.assertNotEquals("generated key ids must be different",
+ ringA.getMasterKeyId(), ringB.getMasterKeyId());
+
+ Assert.assertNull("merging keys with differing key ids must fail",
+ ringA.merge(ringB, log, 0));
+ Assert.assertNull("merging keys with differing key ids must fail",
+ ringB.merge(ringA, log, 0));
+
+ }
+
+ @Test
+ public void testAddedUserId() throws Exception {
+
+ UncachedKeyRing modifiedA, modifiedB; {
+ CanonicalizedSecretKeyRing secretRing =
+ new CanonicalizedSecretKeyRing(ringA.getEncoded(), false, 0);
+
+ parcel.reset();
+ parcel.mAddUserIds.add("flim");
+ modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+
+ parcel.reset();
+ parcel.mAddUserIds.add("flam");
+ modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+ }
+
+ { // merge A into base
+ UncachedKeyRing merged = mergeWithChecks(ringA, modifiedA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained two packets", 2, onlyB.size());
+ Assert.assertTrue("merged keyring must contain new user id",
+ merged.getPublicKey().getUnorderedUserIds().contains("flim"));
+ }
+
+ { // merge A into B
+ UncachedKeyRing merged = mergeWithChecks(modifiedA, modifiedB, ringA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained four packets", 4, onlyB.size());
+ Assert.assertTrue("merged keyring must contain first new user id",
+ merged.getPublicKey().getUnorderedUserIds().contains("flim"));
+ Assert.assertTrue("merged keyring must contain second new user id",
+ merged.getPublicKey().getUnorderedUserIds().contains("flam"));
+
+ }
+
+ }
+
+ @Test
+ public void testAddedSubkeyId() throws Exception {
+
+ UncachedKeyRing modifiedA, modifiedB;
+ long subKeyIdA, subKeyIdB;
+ {
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), false, 0);
+
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+ modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+
+ subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
+ subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
+
+ }
+
+ {
+ UncachedKeyRing merged = mergeWithChecks(ringA, modifiedA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained two packets", 2, onlyB.size());
+
+ long mergedKeyId = KeyringTestingHelper.getSubkeyId(merged, 2);
+ Assert.assertEquals("merged keyring must contain the new subkey", subKeyIdA, mergedKeyId);
+ }
+
+ {
+ UncachedKeyRing merged = mergeWithChecks(modifiedA, modifiedB, ringA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained four packets", 4, onlyB.size());
+
+ Iterator<UncachedPublicKey> it = merged.getPublicKeys();
+ it.next(); it.next();
+ Assert.assertEquals("merged keyring must contain the new subkey",
+ subKeyIdA, it.next().getKeyId());
+ Assert.assertEquals("merged keyring must contain both new subkeys",
+ subKeyIdB, it.next().getKeyId());
+ }
+
+ }
+
+ @Test
+ public void testAddedKeySignature() throws Exception {
+
+ final UncachedKeyRing modified; {
+ parcel.reset();
+ parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
+ ringA.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+ }
+
+ {
+ UncachedKeyRing merged = ringA.merge(modified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertFalse(
+ "merging keyring with extra signatures into its base should yield that same keyring",
+ KeyringTestingHelper.diffKeyrings(merged.getEncoded(), modified.getEncoded(), onlyA, onlyB)
+ );
+ }
+
+ }
+
+ @Test
+ public void testAddedUserIdSignature() throws Exception {
+
+ final UncachedKeyRing pubRing = ringA.extractPublicKeyRing();
+
+ final UncachedKeyRing modified; {
+ CanonicalizedPublicKeyRing publicRing = new CanonicalizedPublicKeyRing(
+ pubRing.getEncoded(), 0);
+
+ CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing(
+ ringB.getEncoded(), false, 0).getSecretKey();
+ secretKey.unlock("");
+ // sign all user ids
+ modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds());
+ }
+
+ {
+ UncachedKeyRing merged = ringA.merge(modified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertArrayEquals("foreign signatures should not be merged into secret key",
+ ringA.getEncoded(), merged.getEncoded()
+ );
+ }
+
+ {
+ byte[] sig = KeyringTestingHelper.getNth(
+ modified.getPublicKey().getSignaturesForId("twi"), 1).getEncoded();
+
+ // inject the (foreign!) signature into subkey signature position
+ UncachedKeyRing moreModified = KeyringTestingHelper.injectPacket(modified, sig, 1);
+
+ UncachedKeyRing merged = ringA.merge(moreModified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertArrayEquals("foreign signatures should not be merged into secret key",
+ ringA.getEncoded(), merged.getEncoded()
+ );
+
+ merged = pubRing.merge(moreModified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertTrue(
+ "merged keyring should contain new signature",
+ KeyringTestingHelper.diffKeyrings(pubRing.getEncoded(), merged.getEncoded(), onlyA, onlyB)
+ );
+ Assert.assertEquals("merged keyring should be missing no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring should contain exactly two more packets", 2, onlyB.size());
+ Assert.assertEquals("first added packet should be a signature",
+ PacketTags.SIGNATURE, onlyB.get(0).tag);
+ Assert.assertEquals("first added packet should be in the position we injected it at",
+ 1, onlyB.get(0).position);
+ Assert.assertEquals("second added packet should be a signature",
+ PacketTags.SIGNATURE, onlyB.get(1).tag);
+
+ }
+
+ {
+ UncachedKeyRing merged = pubRing.merge(modified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertFalse(
+ "merging keyring with extra signatures into its base should yield that same keyring",
+ KeyringTestingHelper.diffKeyrings(merged.getEncoded(), modified.getEncoded(), onlyA, onlyB)
+ );
+ }
+ }
+
+ private UncachedKeyRing mergeWithChecks(UncachedKeyRing a, UncachedKeyRing b)
+ throws Exception {
+ return mergeWithChecks(a, b, a);
+ }
+
+ private UncachedKeyRing mergeWithChecks(UncachedKeyRing a, UncachedKeyRing b,
+ UncachedKeyRing base)
+ throws Exception {
+
+ Assert.assertTrue("merging keyring must be secret type", a.isSecret());
+ Assert.assertTrue("merged keyring must be secret type", b.isSecret());
+
+ final UncachedKeyRing resultA;
+ UncachedKeyRing resultB;
+
+ { // sec + sec
+ resultA = a.merge(b, log, 0);
+ Assert.assertNotNull("merge must succeed as sec(a)+sec(b)", resultA);
+
+ resultB = b.merge(a, log, 0);
+ Assert.assertNotNull("merge must succeed as sec(b)+sec(a)", resultB);
+
+ // check commutativity, if requested
+ Assert.assertFalse("result of merge must be commutative",
+ KeyringTestingHelper.diffKeyrings(
+ resultA.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+ }
+
+ final UncachedKeyRing pubA = a.extractPublicKeyRing();
+ final UncachedKeyRing pubB = b.extractPublicKeyRing();
+
+ { // sec + pub, pub + sec, and pub + pub
+
+ try {
+ resultB = a.merge(pubB, log, 0);
+ Assert.assertNotNull("merge must succeed as sec(a)+pub(b)", resultA);
+
+ Assert.assertFalse("result of sec(a)+pub(b) must be same as sec(a)+sec(b)",
+ KeyringTestingHelper.diffKeyrings(
+ resultA.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+ } catch (RuntimeException e) {
+ System.out.println("special case, dummy key generation not in yet");
+ }
+
+ final UncachedKeyRing pubResult = resultA.extractPublicKeyRing();
+
+ resultB = pubA.merge(b, log, 0);
+ Assert.assertNotNull("merge must succeed as pub(a)+sec(b)", resultA);
+
+ Assert.assertFalse("result of pub(a)+sec(b) must be same as pub(sec(a)+sec(b))",
+ KeyringTestingHelper.diffKeyrings(
+ pubResult.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+
+ resultB = pubA.merge(pubB, log, 0);
+ Assert.assertNotNull("merge must succeed as pub(a)+pub(b)", resultA);
+
+ Assert.assertFalse("result of pub(a)+pub(b) must be same as pub(sec(a)+sec(b))",
+ KeyringTestingHelper.diffKeyrings(
+ pubResult.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+
+ }
+
+ if (base != null) {
+ // set up onlyA and onlyB to be a diff to the base
+ Assert.assertTrue("merged keyring must differ from base",
+ KeyringTestingHelper.diffKeyrings(
+ base.getEncoded(), resultA.getEncoded(), onlyA, onlyB)
+ );
+ }
+
+ return resultA;
+
+ }
+
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java
new file mode 100644
index 000000000..15aaa4c5d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java
@@ -0,0 +1,125 @@
+package org.sufficientlysecure.keychain.tests;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+public class UncachedKeyringTest {
+
+ static UncachedKeyRing staticRing, staticPubRing;
+ UncachedKeyRing ring, pubRing;
+ ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
+ ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
+ PgpKeyOperation op;
+ SaveKeyringParcel parcel;
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ ShadowLog.stream = System.out;
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.ENCRYPT_COMMS, null));
+
+ parcel.mAddUserIds.add("twi");
+ parcel.mAddUserIds.add("pink");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ staticRing = result.getRing();
+ staticPubRing = staticRing.extractPublicKeyRing();
+
+ Assert.assertNotNull("initial test key creation must succeed", staticRing);
+
+ // we sleep here for a second, to make sure all new certificates have different timestamps
+ Thread.sleep(1000);
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ // show Log.x messages in system.out
+ ShadowLog.stream = System.out;
+ ring = staticRing;
+ pubRing = staticPubRing;
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testPublicKeyItRemove() throws Exception {
+ Iterator<UncachedPublicKey> it = ring.getPublicKeys();
+ it.remove();
+ }
+
+ @Test(expected = PgpGeneralException.class)
+ public void testDecodeFromEmpty() throws Exception {
+ UncachedKeyRing.decodeFromData(new byte[0]);
+ }
+
+ @Test
+ public void testArmorIdentity() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ring.encodeArmored(out, "OpenKeychain");
+
+ Assert.assertArrayEquals("armor encoded and decoded ring should be identical to original",
+ ring.getEncoded(),
+ UncachedKeyRing.decodeFromData(out.toByteArray()).getEncoded());
+ }
+
+ @Test(expected = PgpGeneralException.class)
+ public void testDecodeEncodeMulti() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ // encode secret and public ring in here
+ ring.encodeArmored(out, "OpenKeychain");
+ pubRing.encodeArmored(out, "OpenKeychain");
+
+ Iterator<UncachedKeyRing> it =
+ UncachedKeyRing.fromStream(new ByteArrayInputStream(out.toByteArray()));
+ Assert.assertTrue("there should be two rings in the stream", it.hasNext());
+ Assert.assertArrayEquals("first ring should be the first we put in",
+ ring.getEncoded(), it.next().getEncoded());
+ Assert.assertTrue("there should be two rings in the stream", it.hasNext());
+ Assert.assertArrayEquals("second ring should be the second we put in",
+ pubRing.getEncoded(), it.next().getEncoded());
+ Assert.assertFalse("there should be two rings in the stream", it.hasNext());
+
+ // this should fail with PgpGeneralException, since it expects exactly one ring
+ UncachedKeyRing.decodeFromData(out.toByteArray());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testPublicExtractPublic() throws Exception {
+ // can't do this, either!
+ pubRing.extractPublicKeyRing();
+ }
+
+}
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING
new file mode 100644
index 000000000..55234e7a0
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/COPYING
@@ -0,0 +1,13 @@
+Copyright © 2011, Stephen Paul Weber <singpolyma.net>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README
new file mode 100644
index 000000000..cff696c83
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/README
@@ -0,0 +1,26 @@
+These test files were copied from the OpenPGP Haskell project.
+
+
+
+Original README
+===============
+
+This is an OpenPGP library inspired by my work on OpenPGP libraries in
+Ruby <https://github.com/singpolyma/openpgp>,
+PHP <http://github.com/singpolyma/openpgp-php>,
+and Python <https://github.com/singpolyma/OpenPGP-Python>.
+
+It defines types to represent OpenPGP messages as a series of packets
+and then defines instances of Data.Binary for each to facilitate
+encoding/decoding.
+
+For performing cryptography, see
+<http://hackage.haskell.org/package/openpgp-crypto-api> or
+<http://hackage.haskell.org/package/openpgp-Crypto>
+
+For dealing with ASCII armor, see
+<http://hackage.haskell.org/package/openpgp-asciiarmor>
+
+It is intended that you use qualified imports with this library.
+
+> import qualified Data.OpenPGP as OpenPGP
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key
new file mode 100644
index 000000000..7cbab1782
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000001-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id
new file mode 100644
index 000000000..759449bb4
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000002-013.user_id
@@ -0,0 +1 @@
+´$Test Key (RSA) <testkey@example.org> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig
new file mode 100644
index 000000000..1e0656d27
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000003-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000004-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig
new file mode 100644
index 000000000..108b99842
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000005-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000006-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig
new file mode 100644
index 000000000..14276d0a5
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000007-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000008-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig
new file mode 100644
index 000000000..4a282dd68
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000009-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000010-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig
new file mode 100644
index 000000000..cae1b7391
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000011-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000012-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey
new file mode 100644
index 000000000..08676d067
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000013-014.public_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig
new file mode 100644
index 000000000..dd601807f
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000014-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000015-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key
new file mode 100644
index 000000000..c9dccbf1e
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000016-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig
new file mode 100644
index 000000000..e734505a7
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000017-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000018-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id
new file mode 100644
index 000000000..ab3f51d91
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000019-013.user_id
@@ -0,0 +1 @@
+´$Test Key (DSA) <testkey@example.com> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig
new file mode 100644
index 000000000..8588489a7
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000020-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000021-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig
new file mode 100644
index 000000000..fefcb5fea
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000022-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000023-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey
new file mode 100644
index 000000000..2e8deea28
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000024-014.public_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig
new file mode 100644
index 000000000..a3eea0a20
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000025-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000026-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key
new file mode 100644
index 000000000..5817e0037
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000027-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig
new file mode 100644
index 000000000..5194b7840
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000028-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000029-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id
new file mode 100644
index 000000000..fb3f49e0d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000030-013.user_id
@@ -0,0 +1 @@
+´+Test Key (DSA sign-only) <test@example.net> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig
new file mode 100644
index 000000000..f69f6875b
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000031-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000032-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig
new file mode 100644
index 000000000..2bb55d4fe
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000033-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000034-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key
new file mode 100644
index 000000000..5980638c4
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000035-006.public_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id
new file mode 100644
index 000000000..5d0d46e5d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000036-013.user_id
@@ -0,0 +1 @@
+´.Test Key (RSA sign-only) <testkey@example.net> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig
new file mode 100644
index 000000000..833b563b2
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000037-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000038-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig
new file mode 100644
index 000000000..89c34fa5d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000039-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000040-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute
new file mode 100644
index 000000000..a21a82fb1
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000041-017.attribute
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig
new file mode 100644
index 000000000..fc6267fd0
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000042-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000043-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey
new file mode 100644
index 000000000..06bf50e4f
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000044-014.public_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig
new file mode 100644
index 000000000..336eb0f24
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000045-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust
new file mode 100644
index 000000000..ffa57e57a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000046-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key
new file mode 100644
index 000000000..77b5d428a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000047-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id
new file mode 100644
index 000000000..759449bb4
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000048-013.user_id
@@ -0,0 +1 @@
+´$Test Key (RSA) <testkey@example.org> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig
new file mode 100644
index 000000000..14276d0a5
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000049-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000050-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey
new file mode 100644
index 000000000..b4e65c92f
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000051-007.secret_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig
new file mode 100644
index 000000000..dd601807f
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000052-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000053-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key
new file mode 100644
index 000000000..f153e5932
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000054-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig
new file mode 100644
index 000000000..e734505a7
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000055-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000056-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id
new file mode 100644
index 000000000..ab3f51d91
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000057-013.user_id
@@ -0,0 +1 @@
+´$Test Key (DSA) <testkey@example.com> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig
new file mode 100644
index 000000000..8588489a7
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000058-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000059-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey
new file mode 100644
index 000000000..9df45f395
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000060-007.secret_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig
new file mode 100644
index 000000000..639494223
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000061-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000062-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key
new file mode 100644
index 000000000..2f4268ee1
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000063-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig
new file mode 100644
index 000000000..5194b7840
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000064-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000065-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id
new file mode 100644
index 000000000..fb3f49e0d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000066-013.user_id
@@ -0,0 +1 @@
+´+Test Key (DSA sign-only) <test@example.net> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig
new file mode 100644
index 000000000..d354e79df
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000067-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000068-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key
new file mode 100644
index 000000000..17a2c354d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000069-005.secret_key
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id
new file mode 100644
index 000000000..5d0d46e5d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000070-013.user_id
@@ -0,0 +1 @@
+´.Test Key (RSA sign-only) <testkey@example.net> \ No newline at end of file
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig
new file mode 100644
index 000000000..833b563b2
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000071-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000072-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute
new file mode 100644
index 000000000..a21a82fb1
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000073-017.attribute
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig
new file mode 100644
index 000000000..fc6267fd0
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000074-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000075-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey
new file mode 100644
index 000000000..b380339a4
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000076-007.secret_subkey
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig
new file mode 100644
index 000000000..336eb0f24
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000077-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust
new file mode 100644
index 000000000..b1eeabb95
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/000078-012.ring_trust
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig
new file mode 100644
index 000000000..2bc6679f4
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/002182-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig
new file mode 100644
index 000000000..94055af66
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000005-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig
new file mode 100644
index 000000000..b22f23b91
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/3F5BBA0B0694BEB6000017-002.sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg
new file mode 100644
index 000000000..87539dbe8
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-bzip2.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg
new file mode 100644
index 000000000..4da4dfa99
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig-zlib.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg
new file mode 100644
index 000000000..dd617de13
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/compressedsig.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig
new file mode 100644
index 000000000..87b2895ea
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/onepass_sig
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg
new file mode 100644
index 000000000..a1519ee74
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/pubring.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg
new file mode 100644
index 000000000..13598756a
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/secring.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted
new file mode 100644
index 000000000..129155aa2
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/symmetrically_encrypted
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg
new file mode 100644
index 000000000..39828fcae
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa-sha384.txt.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg
new file mode 100644
index 000000000..97e7a267b
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-dsa.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg
new file mode 100644
index 000000000..7ae453da6
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/OpenPGP-Haskell/tests/data/uncompressed-ops-rsa.gpg
Binary files differ
diff --git a/OpenKeychain-Test/src/test/resources/public-key-canonicalize.blob b/OpenKeychain-Test/src/test/resources/public-key-canonicalize.blob
new file mode 100644
index 000000000..3450824c1
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/public-key-canonicalize.blob
Binary files differ
diff --git a/OpenKeychain/src/test/resources/public-key-for-sample.blob b/OpenKeychain-Test/src/test/resources/public-key-for-sample.blob
index 4aa91510b..4aa91510b 100644
--- a/OpenKeychain/src/test/resources/public-key-for-sample.blob
+++ b/OpenKeychain-Test/src/test/resources/public-key-for-sample.blob
Binary files differ
diff --git a/OpenKeychain/src/test/resources/sample-altered.txt b/OpenKeychain-Test/src/test/resources/sample-altered.txt
index 458821f81..458821f81 100644
--- a/OpenKeychain/src/test/resources/sample-altered.txt
+++ b/OpenKeychain-Test/src/test/resources/sample-altered.txt
diff --git a/OpenKeychain/src/test/resources/sample.txt b/OpenKeychain-Test/src/test/resources/sample.txt
index c0065f78d..c0065f78d 100644
--- a/OpenKeychain/src/test/resources/sample.txt
+++ b/OpenKeychain-Test/src/test/resources/sample.txt
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 18a801ce0..88706f1f9 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -1,5 +1,4 @@
apply plugin: 'android'
-apply plugin: 'robolectric'
dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
@@ -16,18 +15,11 @@ dependencies {
compile project(':extern:spongycastle:pg')
compile project(':extern:spongycastle:pkix')
compile project(':extern:spongycastle:prov')
- compile project(':extern:AppMsg:library')
compile project(':extern:SuperToasts:supertoasts')
compile project(':extern:minidns')
compile project(':extern:KeybaseLib:Lib')
compile project(':extern:TokenAutoComplete:library')
- // Unit tests are run with Robolectric
- testCompile 'junit:junit:4.11'
- testCompile 'org.robolectric:robolectric:2.3'
- testCompile 'com.squareup:fest-android:1.0.8'
- testCompile 'com.google.android:android:4.1.1.4'
- // compile dependencies are automatically also included in testCompile
}
android {
@@ -86,7 +78,7 @@ android {
// NOTE: This disables Lint!
tasks.whenTaskAdded { task ->
- if (task.name.equals("lint")) {
+ if (task.name.contains("lint")) {
task.enabled = false
}
}
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index 82eb40ad3..de559fe16 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -31,7 +31,7 @@
For OI Filemanager it makes no difference, gpg files can't be associated
-->
- <!-- Specified in buid.gradle -->
+ <!-- Specified in build.gradle -->
<!--<uses-sdk-->
<!--android:minSdkVersion="9"-->
<!--android:targetSdkVersion="19" />-->
@@ -49,17 +49,17 @@
android:name="android.hardware.touchscreen"
android:required="false" />
- <permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"/>
- <uses-permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"/>
+ <permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" />
+ <uses-permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
- <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
- <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
- <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+ <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
@@ -84,20 +84,26 @@
</intent-filter>
</activity>
<activity
- android:name=".ui.WizardActivity"
+ android:name=".ui.FirstTimeActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_wizard"
- android:windowSoftInputMode="stateHidden" />
+ android:label="@string/app_name"
+ android:windowSoftInputMode="stateAlwaysHidden" />
<activity
- android:name=".ui.EditKeyActivityOld"
+ android:name=".ui.CreateKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_edit_key"
- android:windowSoftInputMode="stateHidden" />
+ android:label="@string/title_create_key">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".ui.KeyListActivity" />
+ </activity>
<activity
android:name=".ui.EditKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_edit_key"
- android:windowSoftInputMode="stateHidden" />
+ android:label="@string/title_edit_key" />
+ <activity
+ android:name=".ui.QrCodeActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:label="@string/share_qr_code_dialog_title" />
<activity
android:name=".ui.ViewKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -221,8 +227,19 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
- <data android:mimeType="*/*"/>
- <!-- Workaround to match files in pathes with dots in them, like /cdcard/my.folder/test.gpg -->
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
+ <data android:pathPattern=".*\\.asc" />
+ <data android:pathPattern=".*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
<data android:pathPattern=".*\\.gpg" />
<data android:pathPattern=".*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\.gpg" />
@@ -233,7 +250,34 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
+ <!--
+ Some apps will only respect these file associations
+ if the mimeType is not set, and other apps will only respect them if mimeType is set
+ to */*. Therefore we have two whole copies of the same thing, besides setting the mimeType.
+ -->
<intent-filter android:label="@string/intent_decrypt_file">
<action android:name="android.intent.action.VIEW" />
@@ -243,7 +287,10 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
+
<data android:mimeType="*/*" />
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
<data android:pathPattern=".*\\.asc" />
<data android:pathPattern=".*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\.asc" />
@@ -254,6 +301,39 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
+ <data android:pathPattern=".*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
</activity>
<activity
@@ -323,6 +403,9 @@
<!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->
<data android:mimeType="application/pgp-keys" />
+ <!-- also link to text/plain, AOSP mail and K-9 mail only give mimeType text/plain
+ when the key file has been manually attached -->
+ <data android:mimeType="text/plain" />
</intent-filter>
<!-- NFC: Handle NFC tags detected from outside our application -->
<intent-filter>
@@ -342,6 +425,19 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
+ <data android:pathPattern=".*\\.asc" />
+ <data android:pathPattern=".*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
<data android:pathPattern=".*\\.gpg" />
<data android:pathPattern=".*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\.gpg" />
@@ -352,8 +448,34 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
- <!-- VIEW with file endings: *.asc -->
+ <!--
+ Some apps will only respect these file associations
+ if the mimeType is not set, and other apps will only respect them if mimeType is set
+ to */*. Therefore we have two whole copies of the same thing, besides setting the mimeType.
+ -->
<intent-filter android:label="@string/intent_import_key">
<action android:name="android.intent.action.VIEW" />
@@ -363,7 +485,10 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
+
<data android:mimeType="*/*" />
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
<data android:pathPattern=".*\\.asc" />
<data android:pathPattern=".*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\.asc" />
@@ -374,7 +499,41 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
+ <data android:pathPattern=".*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
+
<!-- Keychain's own Actions -->
<!-- IMPORT_KEY with files TODO: does this work? -->
<intent-filter android:label="@string/intent_import_key">
@@ -469,31 +628,31 @@
<service android:name=".service.DummyAccountService">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator"/>
+ <action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
- android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/account_desc"/>
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/account_desc" />
</service>
<service android:name=".service.ContactSyncAdapterService">
<intent-filter>
- <action android:name="android.content.SyncAdapter"/>
+ <action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
- android:name="android.content.SyncAdapter"
- android:resource="@xml/sync_adapter_desc"/>
+ android:name="android.content.SyncAdapter"
+ android:resource="@xml/sync_adapter_desc" />
<meta-data
- android:name="android.provider.CONTACTS_STRUCTURE"
- android:resource="@xml/custom_pgp_contacts_structure"/>
+ android:name="android.provider.CONTACTS_STRUCTURE"
+ android:resource="@xml/custom_pgp_contacts_structure" />
</service>
<provider
android:name=".provider.TemporaryStorageProvider"
android:authorities="org.sufficientlysecure.keychain.tempstorage"
android:writePermission="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"
- android:exported="true"/>
+ android:exported="true" />
</application>
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 956019605..b679ef210 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -71,6 +71,8 @@ public final class Constants {
public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
public static final String KEY_SERVERS = "keyServers";
public static final String KEY_SERVERS_DEFAULT_VERSION = "keyServersDefaultVersion";
+ public static final String CONCEAL_PGP_APPLICATION = "concealPgpApplication";
+ public static final String FIRST_TIME = "firstTime";
}
public static final class Defaults {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 6f3d38ccd..5d765b663 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -139,6 +139,16 @@ public class Preferences {
editor.commit();
}
+ public boolean isFirstTime() {
+ return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true);
+ }
+
+ public void setFirstTime(boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Constants.Pref.FIRST_TIME, value);
+ editor.commit();
+ }
+
public String[] getKeyServers() {
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
Constants.Defaults.KEY_SERVERS);
@@ -187,4 +197,14 @@ public class Preferences {
.commit();
}
}
+
+ public void setConcealPgpApplication(boolean conceal) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Constants.Pref.CONCEAL_PGP_APPLICATION, conceal);
+ editor.commit();
+ }
+
+ public boolean getConcealPgpApplication() {
+ return mSharedPreferences.getBoolean(Constants.Pref.CONCEAL_PGP_APPLICATION, false);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java
new file mode 100644
index 000000000..08b8afae7
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.keyimport;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.sufficientlysecure.keychain.KeychainApplication;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * When sending large data (over 1MB) through Androids Binder IPC you get
+ * JavaBinder E !!! FAILED BINDER TRANSACTION !!!
+ * <p/>
+ * To overcome this problem, we cache large Parcelables into a file in our private cache directory
+ * instead of sending them through IPC.
+ */
+public class FileImportCache {
+
+ private Context mContext;
+
+ private static final String FILENAME = "key_import.pcl";
+ private static final String BUNDLE_DATA = "data";
+
+ public FileImportCache(Context context) {
+ this.mContext = context;
+ }
+
+ public void writeCache(ArrayList<ParcelableKeyRing> selectedEntries) throws IOException {
+ Bundle in = new Bundle();
+ in.putParcelableArrayList(BUNDLE_DATA, selectedEntries);
+ File cacheDir = mContext.getCacheDir();
+ if (cacheDir == null) {
+ // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
+ throw new IOException("cache dir is null!");
+ }
+ File tempFile = new File(mContext.getCacheDir(), FILENAME);
+
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ Parcel p = Parcel.obtain(); // creating empty parcel object
+ in.writeToParcel(p, 0); // saving bundle as parcel
+ fos.write(p.marshall()); // writing parcel to file
+ fos.flush();
+ fos.close();
+ }
+
+ public List<ParcelableKeyRing> readCache() throws IOException {
+ Parcel parcel = Parcel.obtain(); // creating empty parcel object
+ Bundle out;
+ File cacheDir = mContext.getCacheDir();
+ if (cacheDir == null) {
+ // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
+ throw new IOException("cache dir is null!");
+ }
+
+ File tempFile = new File(cacheDir, FILENAME);
+ try {
+ FileInputStream fis = new FileInputStream(tempFile);
+ byte[] array = new byte[(int) fis.getChannel().size()];
+ fis.read(array, 0, array.length);
+ fis.close();
+
+ parcel.unmarshall(array, 0, array.length);
+ parcel.setDataPosition(0);
+ out = parcel.readBundle(KeychainApplication.class.getClassLoader());
+ out.putAll(out);
+
+ return out.getParcelableArrayList(BUNDLE_DATA);
+ } finally {
+ parcel.recycle();
+ // delete temp file
+ tempFile.delete();
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index 44679ba18..43bed8397 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -22,6 +22,7 @@ import de.measite.minidns.Client;
import de.measite.minidns.Question;
import de.measite.minidns.Record;
import de.measite.minidns.record.SRV;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.TlsHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -102,8 +103,9 @@ public class HkpKeyserver extends Keyserver {
*/
public static final Pattern PUB_KEY_LINE = Pattern
.compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line
- + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines
- Pattern.CASE_INSENSITIVE);
+ + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines
+ Pattern.CASE_INSENSITIVE
+ );
/**
* uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags%
@@ -215,10 +217,18 @@ public class HkpKeyserver extends Keyserver {
throw new HttpError(response, data);
}
} catch (IOException e) {
- throw new QueryFailedException("querying server(s) for '" + mHost + "' failed");
+ throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!");
}
}
+ /**
+ * Results are sorted by creation date of key!
+ *
+ * @param query
+ * @return
+ * @throws QueryFailedException
+ * @throws QueryNeedsRepairException
+ */
@Override
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
QueryNeedsRepairException {
@@ -240,18 +250,26 @@ public class HkpKeyserver extends Keyserver {
try {
data = query(request);
} catch (HttpError e) {
- if (e.getCode() == 404) {
- return results;
- } else {
+ if (e.getData() != null) {
+ Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.US));
+
if (e.getData().toLowerCase(Locale.US).contains("no keys found")) {
+ // NOTE: This is also a 404 error for some keyservers!
return results;
} else if (e.getData().toLowerCase(Locale.US).contains("too many")) {
throw new TooManyResponsesException();
} else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) {
throw new QueryTooShortException();
+ } else if (e.getCode() == 404) {
+ // NOTE: handle this 404 at last, maybe it was a "no keys found" error
+ throw new QueryFailedException("Keyserver '" + mHost + "' not found. Error 404");
+ } else {
+ // NOTE: some keyserver do not provide a more detailed error response
+ throw new QueryTooShortOrTooManyResponsesException();
}
}
- throw new QueryFailedException("querying server(s) for '" + mHost + "' failed");
+
+ throw new QueryFailedException("Querying server(s) for '" + mHost + "' failed.");
}
final Matcher matcher = PUB_KEY_LINE.matcher(data);
@@ -291,7 +309,7 @@ public class HkpKeyserver extends Keyserver {
while (uidMatcher.find()) {
String tmp = uidMatcher.group(1).trim();
if (tmp.contains("%")) {
- if(tmp.contains("%%")) {
+ if (tmp.contains("%%")) {
// This is a fix for issue #683
// The server encodes a percent sign as %%, so it is swapped out with its
// urlencoded counterpart to prevent errors
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 0a49cb629..30e93f957 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -250,7 +250,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
mHashCode = key.hashCode();
- mPrimaryUserId = key.getPrimaryUserId();
+ mPrimaryUserId = key.getPrimaryUserIdWithFallback();
mUserIds = key.getUnorderedUserIds();
// if there was no user id flagged as primary, use the first one
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
index 842e7d922..b726529f8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
@@ -44,6 +44,13 @@ public abstract class Keyserver {
private static final long serialVersionUID = 2703768928624654514L;
}
+ /**
+ * query too short _or_ too many responses
+ */
+ public static class QueryTooShortOrTooManyResponsesException extends QueryNeedsRepairException {
+ private static final long serialVersionUID = 2703768928624654514L;
+ }
+
public static class AddKeyException extends Exception {
private static final long serialVersionUID = -507574859137295530L;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index 6f3068261..ee0dfefa4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -16,13 +16,11 @@ import java.io.OutputStream;
* getter method.
*
*/
-public abstract class WrappedKeyRing extends KeyRing {
+public abstract class CanonicalizedKeyRing extends KeyRing {
- private final boolean mHasAnySecret;
private final int mVerified;
- WrappedKeyRing(boolean hasAnySecret, int verified) {
- mHasAnySecret = hasAnySecret;
+ CanonicalizedKeyRing(int verified) {
mVerified = verified;
}
@@ -30,17 +28,17 @@ public abstract class WrappedKeyRing extends KeyRing {
return getRing().getPublicKey().getKeyID();
}
- public boolean hasAnySecret() {
- return mHasAnySecret;
- }
-
public int getVerified() {
return mVerified;
}
public String getPrimaryUserId() throws PgpGeneralException {
- return (String) getRing().getPublicKey().getUserIDs().next();
- };
+ return getPublicKey().getPrimaryUserId();
+ }
+
+ public String getPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return getPublicKey().getPrimaryUserIdWithFallback();
+ }
public boolean isRevoked() throws PgpGeneralException {
// Is the master key revoked?
@@ -52,7 +50,7 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public long getEncryptId() throws PgpGeneralException {
- for(WrappedPublicKey key : publicKeyIterator()) {
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
if(key.canEncrypt()) {
return key.getKeyId();
}
@@ -70,7 +68,7 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public long getSignId() throws PgpGeneralException {
- for(WrappedPublicKey key : publicKeyIterator()) {
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
if(key.canSign()) {
return key.getKeyId();
}
@@ -99,10 +97,18 @@ public abstract class WrappedKeyRing extends KeyRing {
abstract PGPKeyRing getRing();
- abstract public IterableIterator<WrappedPublicKey> publicKeyIterator();
+ abstract public IterableIterator<CanonicalizedPublicKey> publicKeyIterator();
- public UncachedKeyRing getUncached() {
- return new UncachedKeyRing(getRing());
+ public CanonicalizedPublicKey getPublicKey() {
+ return new CanonicalizedPublicKey(this, getRing().getPublicKey());
+ }
+
+ public CanonicalizedPublicKey getPublicKey(long id) {
+ return new CanonicalizedPublicKey(this, getRing().getPublicKey(id));
+ }
+
+ public byte[] getEncoded() throws IOException {
+ return getRing().getEncoded();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index 69a4fbdee..981caad49 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -14,12 +14,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
* stored in the database.
*
*/
-public class WrappedPublicKey extends UncachedPublicKey {
+public class CanonicalizedPublicKey extends UncachedPublicKey {
// this is the parent key ring
final KeyRing mRing;
- WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
+ CanonicalizedPublicKey(KeyRing ring, PGPPublicKey key) {
super(key);
mRing = ring;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index b2abf15a4..70288dceb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -1,42 +1,45 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.util.Iterator;
-public class WrappedPublicKeyRing extends WrappedKeyRing {
+public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
private PGPPublicKeyRing mRing;
- private final byte[] mPubKey;
- public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
- super(hasAnySecret, verified);
- mPubKey = blob;
+ CanonicalizedPublicKeyRing(PGPPublicKeyRing ring, int verified) {
+ super(verified);
+ mRing = ring;
}
- PGPPublicKeyRing getRing() {
+ public CanonicalizedPublicKeyRing(byte[] blob, int verified) {
+ super(verified);
if(mRing == null) {
- PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
- PGPKeyRing keyRing = null;
+ // get first object in block
+ PGPObjectFactory factory = new PGPObjectFactory(blob);
try {
- if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
- Log.e(Constants.TAG, "No keys given!");
+ Object obj = factory.nextObject();
+ if (! (obj instanceof PGPPublicKeyRing)) {
+ throw new RuntimeException("Error constructing CanonicalizedPublicKeyRing, should never happen!");
+ }
+ mRing = (PGPPublicKeyRing) obj;
+ if (factory.nextObject() != null) {
+ throw new RuntimeException("Encountered trailing data after keyring, should never happen!");
}
} catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ throw new RuntimeException("IO Error constructing CanonicalizedPublicKeyRing, should never happen!");
}
-
- mRing = (PGPPublicKeyRing) keyRing;
}
+ }
+
+ PGPPublicKeyRing getRing() {
return mRing;
}
@@ -44,19 +47,11 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
getRing().encode(stream);
}
- public WrappedPublicKey getSubkey() {
- return new WrappedPublicKey(this, getRing().getPublicKey());
- }
-
- public WrappedPublicKey getSubkey(long id) {
- return new WrappedPublicKey(this, getRing().getPublicKey(id));
- }
-
/** Getter that returns the subkey that should be used for signing. */
- WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
+ CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
if(key != null) {
- WrappedPublicKey cKey = new WrappedPublicKey(this, key);
+ CanonicalizedPublicKey cKey = new CanonicalizedPublicKey(this, key);
if(!cKey.canEncrypt()) {
throw new PgpGeneralException("key error");
}
@@ -65,18 +60,18 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
throw new PgpGeneralException("no encryption key available");
}
- public IterableIterator<WrappedPublicKey> publicKeyIterator() {
+ public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
@SuppressWarnings("unchecked")
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedPublicKey next() {
- return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
+ public CanonicalizedPublicKey next() {
+ return new CanonicalizedPublicKey(CanonicalizedPublicKeyRing.this, it.next());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index ef8044a9b..2eb517697 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -37,26 +37,18 @@ import java.util.List;
* properly imported secret keys only.
*
*/
-public class WrappedSecretKey extends WrappedPublicKey {
+public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
private final PGPSecretKey mSecretKey;
private PGPPrivateKey mPrivateKey = null;
- WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
+ CanonicalizedSecretKey(CanonicalizedSecretKeyRing ring, PGPSecretKey key) {
super(ring, key.getPublicKey());
mSecretKey = key;
}
- public WrappedSecretKeyRing getRing() {
- return (WrappedSecretKeyRing) mRing;
- }
-
- /** Returns the wrapped PGPSecretKeyRing.
- * This function is for compatibility only, should not be used anymore and will be removed
- */
- @Deprecated
- public PGPSecretKey getKeyExternal() {
- return mSecretKey;
+ public CanonicalizedSecretKeyRing getRing() {
+ return (CanonicalizedSecretKeyRing) mRing;
}
public boolean unlock(String passphrase) throws PgpGeneralException {
@@ -97,7 +89,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
signatureGenerator.init(signatureType, mPrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, mRing.getPrimaryUserId());
+ spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch(PGPException e) {
@@ -148,7 +140,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
* @param userIds User IDs to certify, must not be null or empty
* @return A keyring with added certifications
*/
- public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds)
+ public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
@@ -175,7 +167,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
}
// get the master subkey (which we certify for)
- PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
+ PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index c737b7c46..e48fe5020 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -1,10 +1,12 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
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.operator.PBESecretKeyDecryptor;
@@ -15,15 +17,21 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Iterator;
-public class WrappedSecretKeyRing extends WrappedKeyRing {
+public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
private PGPSecretKeyRing mRing;
- public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
+ CanonicalizedSecretKeyRing(PGPSecretKeyRing ring, int verified) {
+ super(verified);
+ mRing = ring;
+ }
+
+ public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
{
- super(isRevoked, verified);
+ super(verified);
PGPObjectFactory factory = new PGPObjectFactory(blob);
PGPKeyRing keyRing = null;
try {
@@ -41,19 +49,32 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
return mRing;
}
- public WrappedSecretKey getSubKey() {
- return new WrappedSecretKey(this, mRing.getSecretKey());
+ public CanonicalizedSecretKey getSecretKey() {
+ return new CanonicalizedSecretKey(this, mRing.getSecretKey());
+ }
+
+ public CanonicalizedSecretKey getSecretKey(long id) {
+ return new CanonicalizedSecretKey(this, mRing.getSecretKey(id));
}
- public WrappedSecretKey getSubKey(long id) {
- return new WrappedSecretKey(this, mRing.getSecretKey(id));
+ public HashSet<Long> getAvailableSubkeys() {
+ HashSet<Long> result = new HashSet<Long>();
+ // then, mark exactly the keys we have available
+ for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(getRing().getSecretKeys())) {
+ S2K s2k = sub.getS2K();
+ // Set to 1, except if the encryption type is GNU_DUMMY_S2K
+ if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
+ result.add(sub.getKeyID());
+ }
+ }
+ return result;
}
/** Getter that returns the subkey that should be used for signing. */
- WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
+ CanonicalizedSecretKey getSigningSubKey() throws PgpGeneralException {
PGPSecretKey key = mRing.getSecretKey(getSignId());
if(key != null) {
- WrappedSecretKey cKey = new WrappedSecretKey(this, key);
+ CanonicalizedSecretKey cKey = new CanonicalizedSecretKey(this, key);
if(!cKey.canSign()) {
throw new PgpGeneralException("key error");
}
@@ -88,17 +109,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
}
}
- public IterableIterator<WrappedSecretKey> secretKeyIterator() {
+ public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
- return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
+ return new IterableIterator<CanonicalizedSecretKey>(new Iterator<CanonicalizedSecretKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedSecretKey next() {
- return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
+ public CanonicalizedSecretKey next() {
+ return new CanonicalizedSecretKey(CanonicalizedSecretKeyRing.this, it.next());
}
@Override
@@ -108,17 +129,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
});
}
- public IterableIterator<WrappedPublicKey> publicKeyIterator() {
+ public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedPublicKey next() {
- return new WrappedPublicKey(WrappedSecretKeyRing.this, it.next());
+ public CanonicalizedPublicKey next() {
+ return new CanonicalizedPublicKey(CanonicalizedSecretKeyRing.this, it.next());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 47b827677..ebc49ab05 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -12,7 +12,7 @@ import java.util.regex.Pattern;
* keyring should in all cases agree on the output of all methods described
* here.
*
- * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see CanonicalizedKeyRing
* @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
*
*/
@@ -22,8 +22,10 @@ public abstract class KeyRing {
abstract public String getPrimaryUserId() throws PgpGeneralException;
- public String[] getSplitPrimaryUserId() throws PgpGeneralException {
- return splitUserId(getPrimaryUserId());
+ abstract public String getPrimaryUserIdWithFallback() throws PgpGeneralException;
+
+ public String[] getSplitPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return splitUserId(getPrimaryUserIdWithFallback());
}
abstract public boolean isRevoked() throws PgpGeneralException;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index a5ccfbd3b..7f2d971ed 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -231,7 +231,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
- WrappedSecretKey secretEncryptionKey = null;
+ CanonicalizedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@@ -243,10 +243,10 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- WrappedSecretKeyRing secretKeyRing;
+ CanonicalizedSecretKeyRing secretKeyRing;
try {
// get actual keyring object based on master key id
- secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
+ secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
);
} catch (ProviderHelper.NotFoundException e) {
@@ -258,7 +258,7 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
- secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
+ secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
@@ -365,8 +365,8 @@ public class PgpDecryptVerify {
Object dataChunk = plainFact.nextObject();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
int signatureIndex = -1;
- WrappedPublicKeyRing signingRing = null;
- WrappedPublicKey signingKey = null;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@@ -390,10 +390,10 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
try {
long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- signingKey = signingRing.getSubkey(sigKeyId);
+ signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -409,7 +409,7 @@ public class PgpDecryptVerify {
signatureResultBuilder.knownKey(true);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
- signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
@@ -566,8 +566,8 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
- WrappedPublicKeyRing signingRing = null;
- WrappedPublicKey signingKey = null;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
int signatureIndex = -1;
// go through all signatures
@@ -575,10 +575,10 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
try {
long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
- signingKey = signingRing.getSubkey(sigKeyId);
+ signingKey = signingRing.getPublicKey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@@ -596,7 +596,7 @@ public class PgpDecryptVerify {
signatureResultBuilder.knownKey(true);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
- signatureResultBuilder.userId(signingRing.getPrimaryUserId());
+ signatureResultBuilder.userId(signingRing.getPrimaryUserIdWithFallback());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 969ff1de8..0ceefc4d9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
@@ -60,7 +61,11 @@ public class PgpHelper {
}
public static String getFullVersion(Context context) {
- return "OpenKeychain v" + getVersion(context);
+ if(Preferences.getPreferences(context).getConcealPgpApplication()){
+ return "";
+ } else {
+ return "OpenKeychain v" + getVersion(context);
+ }
}
// public static final class content {
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 6fc55cfb8..846b00ef2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
-import org.sufficientlysecure.keychain.service.OperationResults.ImportResult;
+import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -93,7 +93,7 @@ public class PgpImportExport {
}
}
- public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
+ public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
@@ -123,14 +123,14 @@ public class PgpImportExport {
}
/** Imports keys from given data. If keyIds is given only those are imported */
- public ImportResult importKeyRings(List<ParcelableKeyRing> entries) {
+ public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries) {
updateProgress(R.string.progress_importing, 0, 100);
// If there aren't even any keys, do nothing here.
if (entries == null || entries.size() == 0) {
- return new ImportResult(
- ImportResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
+ return new ImportKeyResult(
+ ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
}
int newKeys = 0, oldKeys = 0, badKeys = 0;
@@ -185,26 +185,26 @@ public class PgpImportExport {
int resultType = 0;
// special return case: no new keys at all
if (badKeys == 0 && newKeys == 0 && oldKeys == 0) {
- resultType = ImportResult.RESULT_FAIL_NOTHING;
+ resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else {
if (newKeys > 0) {
- resultType |= ImportResult.RESULT_OK_NEWKEYS;
+ resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
}
if (oldKeys > 0) {
- resultType |= ImportResult.RESULT_OK_UPDATED;
+ resultType |= ImportKeyResult.RESULT_OK_UPDATED;
}
if (badKeys > 0) {
- resultType |= ImportResult.RESULT_WITH_ERRORS;
+ resultType |= ImportKeyResult.RESULT_WITH_ERRORS;
if (newKeys == 0 && oldKeys == 0) {
- resultType |= ImportResult.RESULT_ERROR;
+ resultType |= ImportKeyResult.RESULT_ERROR;
}
}
if (log.containsWarnings()) {
- resultType |= ImportResult.RESULT_WITH_WARNINGS;
+ resultType |= ImportKeyResult.RESULT_WITH_WARNINGS;
}
}
- return new ImportResult(resultType, log, newKeys, oldKeys, badKeys);
+ return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys);
}
@@ -235,7 +235,7 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
+ CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId)
);
@@ -263,8 +263,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- WrappedSecretKeyRing secretKeyRing =
- mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
+ CanonicalizedSecretKeyRing secretKeyRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
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 9a08290e4..b1bd6a77a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -46,15 +46,17 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
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;
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.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Primes;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.IOException;
import java.math.BigInteger;
@@ -65,10 +67,9 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
-import java.util.TimeZone;
+import java.util.Stack;
/**
* This class is the single place where ALL operations that actually modify a PGP public or secret
@@ -80,7 +81,7 @@ import java.util.TimeZone;
* This indicator may be null.
*/
public class PgpKeyOperation {
- private Progressable mProgress;
+ private Stack<Progressable> mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
@@ -94,21 +95,43 @@ public class PgpKeyOperation {
public PgpKeyOperation(Progressable progress) {
super();
- this.mProgress = progress;
+ if (progress != null) {
+ mProgress = new Stack<Progressable>();
+ mProgress.push(progress);
+ }
+ }
+
+ private void subProgressPush(int from, int to) {
+ if (mProgress == null) {
+ return;
+ }
+ mProgress.push(new ProgressScaler(mProgress.peek(), from, to, 100));
+ }
+ private void subProgressPop() {
+ if (mProgress == null) {
+ return;
+ }
+ if (mProgress.size() == 1) {
+ throw new RuntimeException("Tried to pop progressable without prior push! "
+ + "This is a programming error, please file a bug report.");
+ }
+ mProgress.pop();
}
- void updateProgress(int message, int current, int total) {
- if (mProgress != null) {
- mProgress.setProgress(message, current, total);
+ private void progress(int message, int current) {
+ if (mProgress == null) {
+ return;
}
+ mProgress.peek().setProgress(message, current, 100);
}
/** Creates new secret key. */
- private PGPKeyPair createKey(int algorithmChoice, int keySize) throws PgpGeneralMsgIdException {
+ private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) {
try {
if (keySize < 512) {
- throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
+ return null;
}
int algorithm;
@@ -116,6 +139,7 @@ public class PgpKeyOperation {
switch (algorithmChoice) {
case Constants.choice.algorithm.dsa: {
+ progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
algorithm = PGPPublicKey.DSA;
@@ -123,6 +147,7 @@ public class PgpKeyOperation {
}
case Constants.choice.algorithm.elgamal: {
+ progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
BigInteger g = new BigInteger("2");
@@ -135,6 +160,7 @@ public class PgpKeyOperation {
}
case Constants.choice.algorithm.rsa: {
+ progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
@@ -143,7 +169,8 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
+ return null;
}
}
@@ -157,31 +184,55 @@ public class PgpKeyOperation {
} catch(InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch(PGPException e) {
- throw new PgpGeneralMsgIdException(R.string.msg_mf_error_pgp, e);
+ Log.e(Constants.TAG, "internal pgp error", e);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
+ return null;
}
}
- public UncachedKeyRing createSecretKeyRing(SaveKeyringParcel saveParcel, OperationLog log,
- int indent) {
+ public EditKeyResult createSecretKeyRing(SaveKeyringParcel saveParcel) {
+
+ OperationLog log = new OperationLog();
+ int indent = 0;
try {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
+ log.add(LogLevel.START, LogType.MSG_CR, indent);
+ progress(R.string.progress_building_key, 0);
indent += 1;
- updateProgress(R.string.progress_building_key, 0, 100);
- if (saveParcel.addSubKeys == null || saveParcel.addSubKeys.isEmpty()) {
+ if (saveParcel.mAddSubKeys.isEmpty()) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_MASTER, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ if (saveParcel.mAddUserIds.isEmpty()) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_USER_ID, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- SubkeyAdd add = saveParcel.addSubKeys.remove(0);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize);
+ SubkeyAdd add = saveParcel.mAddSubKeys.remove(0);
+ if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
if (add.mAlgorithm == Constants.choice.algorithm.elgamal) {
- throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ subProgressPush(10, 30);
+ PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ subProgressPop();
+
+ // return null if this failed (an error will already have been logged by createKey)
+ if (keyPair == null) {
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ progress(R.string.progress_building_master_key, 40);
+
// define hashing and signing algos
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
.build().get(HashAlgorithmTags.SHA1);
@@ -195,17 +246,16 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
- return internal(sKR, masterSecretKey, saveParcel, "", log, indent);
+ subProgressPush(50, 100);
+ return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);
} catch (PGPException e) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
Log.e(Constants.TAG, "pgp error encoding key", e);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (IOException e) {
Log.e(Constants.TAG, "io error encoding key", e);
- return null;
- } catch (PgpGeneralMsgIdException e) {
- Log.e(Constants.TAG, "pgp msg id error", e);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -221,8 +271,11 @@ public class PgpKeyOperation {
* are changed by adding new certificates, which implicitly override older certificates.
*
*/
- public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
- String passphrase, OperationLog log, int indent) {
+ public EditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
+ String passphrase) {
+
+ OperationLog log = new OperationLog();
+ int indent = 0;
/*
* 1. Unlock private key
@@ -235,14 +288,15 @@ public class PgpKeyOperation {
* 6. If requested, change passphrase
*/
- log.add(LogLevel.START, LogType.MSG_MF, indent);
+ log.add(LogLevel.START, LogType.MSG_MF, indent,
+ PgpKeyHelper.convertKeyIdToHex(wsKR.getMasterKeyId()));
indent += 1;
- updateProgress(R.string.progress_building_key, 0, 100);
+ progress(R.string.progress_building_key, 0);
// Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// We work on bouncycastle object level here
@@ -254,22 +308,30 @@ public class PgpKeyOperation {
|| !Arrays.equals(saveParcel.mFingerprint,
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_FINGERPRINT, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- return internal(sKR, masterSecretKey, saveParcel, passphrase, log, indent);
+ // read masterKeyFlags, and use the same as before.
+ // since this is the master key, this contains at least CERTIFY_OTHER
+ int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
+
+ return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);
}
- private UncachedKeyRing internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ int masterKeyFlags,
SaveKeyringParcel saveParcel, String passphrase,
- OperationLog log, int indent) {
+ OperationLog log) {
+
+ int indent = 1;
- updateProgress(R.string.progress_certifying_master_key, 20, 100);
+ progress(R.string.progress_modify, 0);
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// 1. Unlock private key
+ progress(R.string.progress_modify_unlock, 10);
log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent);
PGPPrivateKey masterPrivateKey;
{
@@ -279,7 +341,7 @@ public class PgpKeyOperation {
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -289,18 +351,28 @@ public class PgpKeyOperation {
PGPPublicKey modifiedPublicKey = masterPublicKey;
// 2a. Add certificates for new user ids
- for (String userId : saveParcel.addUserIds) {
+ subProgressPush(15, 25);
+ for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ String userId = saveParcel.mAddUserIds.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent);
+ if (userId.equals("")) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
// this operation supersedes all previous binding and revocation certificates,
// so remove those to retain assertions from canonicalization for later operations
@SuppressWarnings("unchecked")
Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
if (it != null) {
for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
- // if it's not a self cert, never mind
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- continue;
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
@@ -314,27 +386,39 @@ public class PgpKeyOperation {
}
// if it's supposed to be primary, we can do that here as well
- boolean isPrimary = saveParcel.changePrimaryUserId != null
- && userId.equals(saveParcel.changePrimaryUserId);
+ boolean isPrimary = saveParcel.mChangePrimaryUserId != null
+ && userId.equals(saveParcel.mChangePrimaryUserId);
// generate and add new certificate
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
- masterPublicKey, userId, isPrimary);
- modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ masterPublicKey, userId, isPrimary, masterKeyFlags);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
+ subProgressPop();
// 2b. Add revocations for revoked user ids
- for (String userId : saveParcel.revokeUserIds) {
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent);
- // a duplicate revocatin will be removed during canonicalization, so no need to
+ subProgressPush(25, 40);
+ for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ String userId = saveParcel.mRevokeUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+
+ // a duplicate revocation will be removed during canonicalization, so no need to
// take care of that here.
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
- modifiedPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
+ subProgressPop();
// 3. If primary user id changed, generate new certificates for both old and new
- if (saveParcel.changePrimaryUserId != null) {
+ if (saveParcel.mChangePrimaryUserId != null) {
+ progress(R.string.progress_modify_primaryuid, 40);
+
+ // keep track if we actually changed one
+ boolean ok = false;
log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
+ indent += 1;
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
// noinspection unchecked
@@ -343,10 +427,11 @@ public class PgpKeyOperation {
PGPSignature currentCert = null;
// noinspection unchecked
for (PGPSignature cert : new IterableIterator<PGPSignature>(
- masterPublicKey.getSignaturesForID(userId))) {
- // if it's not a self cert, never mind
+ modifiedPublicKey.getSignaturesForID(userId))) {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- continue;
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// we know from canonicalization that if there is any revocation here, it
// is valid and not superseded by a newer certification.
@@ -367,30 +452,33 @@ public class PgpKeyOperation {
if (currentCert == null) {
// no certificate found?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// we definitely should not update certifications of revoked keys, so just leave it.
if (isRevoked) {
// revoked user ids cannot be primary!
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
continue;
}
// if this is~ the/a primary user id
- if (currentCert.hasSubpackets() && currentCert.getHashedSubPackets().isPrimaryUserID()) {
+ if (currentCert.getHashedSubPackets() != null
+ && currentCert.getHashedSubPackets().isPrimaryUserID()) {
// if it's the one we want, just leave it as is
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ ok = true;
continue;
}
// otherwise, generate new non-primary certification
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
modifiedPublicKey = PGPPublicKey.removeCertification(
modifiedPublicKey, userId, currentCert);
PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, false);
+ masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);
modifiedPublicKey = PGPPublicKey.addCertification(
modifiedPublicKey, userId, newCert);
continue;
@@ -399,20 +487,28 @@ public class PgpKeyOperation {
// if we are here, this is not currently a primary user id
// if it should be
- if (userId.equals(saveParcel.changePrimaryUserId)) {
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
// add shiny new primary user id certificate
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
modifiedPublicKey = PGPPublicKey.removeCertification(
modifiedPublicKey, userId, currentCert);
PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, true);
+ masterPrivateKey, masterPublicKey, userId, true, masterKeyFlags);
modifiedPublicKey = PGPPublicKey.addCertification(
modifiedPublicKey, userId, newCert);
+ ok = true;
}
// user id is not primary and is not supposed to be - nothing to do here.
}
+ indent -= 1;
+
+ if (!ok) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
}
// Update the secret key ring
@@ -423,40 +519,80 @@ public class PgpKeyOperation {
}
// 4a. For each subkey change, generate new subkey binding certificate
- for (SaveKeyringParcel.SubkeyChange change : saveParcel.changeSubKeys) {
+ subProgressPush(50, 60);
+ for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeychange, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
+
+ // TODO allow changes in master key? this implies generating new user id certs...
+ if (change.mKeyId == masterPublicKey.getKeyID()) {
+ Log.e(Constants.TAG, "changing the master key not supported");
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
- if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) {
+ // expiry must not be in the past
+ if (change.mExpiry != null && new Date(change.mExpiry*1000).before(new Date())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // keep old flags, or replace with new ones
+ int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
+ long expiry;
+ if (change.mExpiry == null) {
+ long valid = pKey.getValidSeconds();
+ expiry = valid == 0
+ ? 0
+ : pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
+ } else {
+ expiry = change.mExpiry;
}
- // generate and add new signature. we can be sloppy here and just leave the old one,
- // it will be removed during canonicalization
+ // drop all old signatures, they will be superseded by the new one
+ //noinspection unchecked
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
+ // special case: if there is a revocation, don't use expiry from before
+ if (change.mExpiry == null
+ && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
+ expiry = 0;
+ }
+ pKey = PGPPublicKey.removeCertification(pKey, sig);
+ }
+
+ // generate and add new signature
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
- sKey, pKey, change.mFlags, change.mExpiry, passphrase);
+ sKey, pKey, flags, expiry, passphrase);
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
+ subProgressPop();
// 4b. For each subkey revocation, generate new subkey revocation certificate
- for (long revocation : saveParcel.revokeSubKeys) {
+ subProgressPush(60, 70);
+ for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ long revocation = saveParcel.mRevokeSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_REVOKE,
indent, PgpKeyHelper.convertKeyIdToHex(revocation));
+
PGPSecretKey sKey = sKR.getSecretKey(revocation);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
indent+1, PgpKeyHelper.convertKeyIdToHex(revocation));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
@@ -466,54 +602,64 @@ public class PgpKeyOperation {
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
+ subProgressPop();
// 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_MF_SUBKEY_PAST_EXPIRY, indent +1);
- return null;
- }
-
- log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
-
- // generate a new secret key (privkey only for now)
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize);
+ subProgressPush(70, 90);
+ for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {
- // add subkey binding signature (making this a sub rather than master key)
- PGPPublicKey pKey = keyPair.getPublicKey();
- PGPSignature cert = generateSubkeyBindingSignature(
- masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
- add.mFlags, add.mExpiry);
- pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
+ progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(0);
+ log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
- PGPSecretKey sKey; {
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
+ if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- // Build key encrypter and decrypter based on passphrase
- PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ // generate a new secret key (privkey only for now)
+ subProgressPush(
+ (i-1) * (100 / saveParcel.mAddSubKeys.size()),
+ i * (100 / saveParcel.mAddSubKeys.size())
+ );
+ PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ subProgressPop();
+ if(keyPair == null) {
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
- sha1Calc, false, keyEncryptor);
- }
+ // add subkey binding signature (making this a sub rather than master key)
+ PGPPublicKey pKey = keyPair.getPublicKey();
+ PGPSignature cert = generateSubkeyBindingSignature(
+ masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
+ add.mFlags, add.mExpiry == null ? 0 : add.mExpiry);
+ pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
+
+ PGPSecretKey sKey; {
+ // 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("".toCharArray());
+
+ sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
+ sha1Calc, false, keyEncryptor);
+ }
- log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
- indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
+ indent+1, PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
- } catch (PgpGeneralMsgIdException e) {
- return null;
- }
}
+ subProgressPop();
// 6. If requested, change passphrase
- if (saveParcel.newPassphrase != null) {
+ if (saveParcel.mNewPassphrase != null) {
+ progress(R.string.progress_modify_passphrase, 90);
log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
@@ -523,7 +669,7 @@ public class PgpKeyOperation {
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- saveParcel.newPassphrase.toCharArray());
+ saveParcel.mNewPassphrase.toCharArray());
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
@@ -531,22 +677,24 @@ public class PgpKeyOperation {
// This one must only be thrown by
} catch (IOException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (PGPException e) {
+ Log.e(Constants.TAG, "encountered pgp error while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (SignatureException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ progress(R.string.progress_done, 100);
log.add(LogLevel.OK, LogType.MSG_MF_SUCCESS, indent);
- return new UncachedKeyRing(sKR);
+ return new EditKeyResult(OperationResultParcel.RESULT_OK, log, new UncachedKeyRing(sKR));
}
private static PGPSignature generateUserIdSignature(
- PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
@@ -558,6 +706,7 @@ public class PgpKeyOperation {
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
subHashedPacketsGen.setPrimaryUserID(false, primary);
+ subHashedPacketsGen.setKeyFlags(false, flags);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
@@ -599,7 +748,7 @@ public class PgpKeyOperation {
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 IOException, PGPException, SignatureException {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
@@ -611,7 +760,7 @@ public class PgpKeyOperation {
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
- PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, Long expiry)
+ PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
throws IOException, PGPException, SignatureException {
// date for signing
@@ -640,17 +789,9 @@ public class PgpKeyOperation {
hashedPacketsGen.setKeyFlags(false, flags);
}
- if (expiry != null) {
- Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- creationDate.setTime(pKey.getCreationTime());
-
- // (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 {
- hashedPacketsGen.setKeyExpirationTime(false, 0);
+ if (expiry > 0) {
+ long creationTime = pKey.getCreationTime().getTime() / 1000;
+ hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
@@ -665,4 +806,22 @@ public class PgpKeyOperation {
}
+ /** Returns all flags valid for this key.
+ *
+ * This method does not do any validity checks on the signature, so it should not be used on
+ * a non-canonicalized key!
+ *
+ */
+ private static int readKeyFlags(PGPPublicKey key) {
+ int flags = 0;
+ //noinspection unchecked
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (sig.getHashedSubPackets() == null) {
+ continue;
+ }
+ flags |= sig.getHashedSubPackets().getKeyFlags();
+ }
+ return flags;
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 4cb92c368..f0403e625 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -266,11 +266,11 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
- WrappedSecretKey signingKey = null;
+ CanonicalizedSecretKey signingKey = null;
if (enableSignature) {
- WrappedSecretKeyRing signingKeyRing;
+ CanonicalizedSecretKeyRing signingKeyRing;
try {
- signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
+ signingKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
@@ -316,9 +316,9 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
- WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
+ CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
- WrappedPublicKey key = keyRing.getEncryptionSubKey();
+ CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
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 441e2762a..502e3a70c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -1,7 +1,6 @@
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;
@@ -14,6 +13,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
@@ -22,20 +22,18 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
-import java.util.Vector;
/** Wrapper around PGPKeyRing class, to be constructed from bytes.
*
@@ -49,7 +47,7 @@ import java.util.Vector;
* treated equally for most purposes in UI code. It is up to the programmer to
* take care of the differences.
*
- * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see CanonicalizedKeyRing
* @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
* @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
*
@@ -59,18 +57,10 @@ public class UncachedKeyRing {
final PGPKeyRing mRing;
final boolean mIsSecret;
- final boolean mIsCanonicalized;
UncachedKeyRing(PGPKeyRing ring) {
mRing = ring;
mIsSecret = ring instanceof PGPSecretKeyRing;
- mIsCanonicalized = false;
- }
-
- private UncachedKeyRing(PGPKeyRing ring, boolean canonicalized) {
- mRing = ring;
- mIsSecret = ring instanceof PGPSecretKeyRing;
- mIsCanonicalized = canonicalized;
}
public long getMasterKeyId() {
@@ -81,11 +71,15 @@ public class UncachedKeyRing {
return new UncachedPublicKey(mRing.getPublicKey());
}
+ public UncachedPublicKey getPublicKey(long keyId) {
+ return new UncachedPublicKey(mRing.getPublicKey(keyId));
+ }
+
public Iterator<UncachedPublicKey> getPublicKeys() {
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
return new Iterator<UncachedPublicKey>() {
public void remove() {
- it.remove();
+ throw new UnsupportedOperationException();
}
public UncachedPublicKey next() {
return new UncachedPublicKey(it.next());
@@ -101,10 +95,6 @@ public class UncachedKeyRing {
return mIsSecret;
}
- public boolean isCanonicalized() {
- return mIsCanonicalized;
- }
-
public byte[] getEncoded() throws IOException {
return mRing.getEncoded();
}
@@ -115,43 +105,86 @@ public class UncachedKeyRing {
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
- BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(data));
- if (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // get first object in block
- Object obj;
- if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
- return new UncachedKeyRing((PGPKeyRing) obj);
- } else {
- throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
- }
- } else {
+
+ Iterator<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data));
+
+ if ( ! parsed.hasNext()) {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
+
+ UncachedKeyRing ring = parsed.next();
+
+ if (parsed.hasNext()) {
+ throw new PgpGeneralException("Expected single keyring in stream, found at least two");
+ }
+
+ return ring;
+
}
- public static List<UncachedKeyRing> fromStream(InputStream stream)
- throws PgpGeneralException, IOException {
+ public static Iterator<UncachedKeyRing> fromStream(final InputStream stream) throws IOException {
+
+ return new Iterator<UncachedKeyRing>() {
- PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+ UncachedKeyRing mNext = null;
+ PGPObjectFactory mObjectFactory = null;
- List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
+ private void cacheNext() {
+ if (mNext != null) {
+ return;
+ }
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ try {
+ while(stream.available() > 0) {
+ // if there are no objects left from the last factory, create a new one
+ if (mObjectFactory == null) {
+ mObjectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+ }
+
+ // go through all objects in this block
+ Object obj;
+ while ((obj = mObjectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ if (!(obj instanceof PGPKeyRing)) {
+ Log.i(Constants.TAG,
+ "Skipping object of bad type " + obj.getClass().getName() + " in stream");
+ // skip object
+ continue;
+ }
+ mNext = new UncachedKeyRing((PGPKeyRing) obj);
+ return;
+ }
+ // if we are past the while loop, that means the objectFactory had no next
+ mObjectFactory = null;
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException while processing stream", e);
+ }
- if (obj instanceof PGPKeyRing) {
- result.add(new UncachedKeyRing((PGPKeyRing) obj));
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
- }
- return result;
+
+ @Override
+ public boolean hasNext() {
+ cacheNext();
+ return mNext != null;
+ }
+
+ @Override
+ public UncachedKeyRing next() {
+ try {
+ cacheNext();
+ return mNext;
+ } finally {
+ mNext = null;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
}
public void encodeArmored(OutputStream out, String version) throws IOException {
@@ -161,25 +194,6 @@ public class UncachedKeyRing {
aos.close();
}
- public HashSet<Long> getAvailableSubkeys() {
- if(!isSecret()) {
- throw new RuntimeException("Tried to find available subkeys from non-secret keys. " +
- "This is a programming error and should never happen!");
- }
-
- HashSet<Long> result = new HashSet<Long>();
- // then, mark exactly the keys we have available
- for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
- ((PGPSecretKeyRing) mRing).getSecretKeys())) {
- S2K s2k = sub.getS2K();
- // Set to 1, except if the encryption type is GNU_DUMMY_S2K
- if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
- result.add(sub.getKeyID());
- }
- }
- return result;
- }
-
/** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be
* applied to public keyrings only.
*
@@ -189,7 +203,7 @@ public class UncachedKeyRing {
* - Remove all certificates flagged as "local"
* - 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:
+ * - Remove all certificates in other positions if not of known type:
* - key revocation signatures on the master key
* - subkey binding signatures for subkeys
* - certifications and certification revocations for user ids
@@ -204,7 +218,7 @@ public class UncachedKeyRing {
*
*/
@SuppressWarnings("ConstantConditions")
- public UncachedKeyRing canonicalize(OperationLog log, int indent) {
+ public CanonicalizedKeyRing canonicalize(OperationLog log, int indent) {
log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC,
indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()));
@@ -225,7 +239,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = masterKey;
PGPSignature revocation = null;
- for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getSignatures())) {
+ for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
// Disregard certifications on user ids, we will deal with those later
@@ -234,6 +248,10 @@ public class UncachedKeyRing {
|| type == PGPSignature.CASUAL_CERTIFICATION
|| type == PGPSignature.POSITIVE_CERTIFICATION
|| type == PGPSignature.CERTIFICATION_REVOCATION) {
+ // These should not be here...
+ log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ badCerts += 1;
continue;
}
WrappedSignature cert = new WrappedSignature(zert);
@@ -317,7 +335,7 @@ public class UncachedKeyRing {
if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, indent);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -325,7 +343,7 @@ public class UncachedKeyRing {
if (cert.isLocal()) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -425,7 +443,7 @@ 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,
+ log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE,
indent, userId);
}
}
@@ -505,14 +523,17 @@ public class UncachedKeyRing {
continue;
}
- if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
+ // if this certificate says it allows signing for the key
+ if (zert.getHashedSubPackets() != null &&
+ 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) {
+ boolean ok = false;
+ // it MUST have an embedded primary key binding signature
try {
PGPSignatureList list = zert.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) {
@@ -526,17 +547,19 @@ public class UncachedKeyRing {
}
}
}
- if (!ok) {
- log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
- badCerts += 1;
- continue;
- }
} catch (Exception e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, indent);
badCerts += 1;
continue;
}
+ // if it doesn't, get rid of this!
+ if (!ok) {
+ log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
+ badCerts += 1;
+ continue;
+ }
}
+
}
// if we already have a cert, and this one is not newer: skip it
@@ -549,6 +572,8 @@ public class UncachedKeyRing {
selfCert = zert;
// if this is newer than a possibly existing revocation, drop that one
if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_REVOKE_DUP, indent);
+ redundantCerts += 1;
revocation = null;
}
@@ -558,7 +583,7 @@ public class UncachedKeyRing {
// make sure the certificate checks out
try {
cert.init(masterKey);
- if (!cert.verifySignature(key)) {
+ if (!cert.verifySignature(masterKey, key)) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_REVOKE_BAD, indent);
badCerts += 1;
continue;
@@ -582,7 +607,7 @@ public class UncachedKeyRing {
// it is not properly bound? error!
if (selfCert == null) {
- ring = replacePublicKey(ring, modified);
+ ring = removeSubKey(ring, key);
log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT,
indent, PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
@@ -615,7 +640,8 @@ public class UncachedKeyRing {
log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, indent);
}
- return new UncachedKeyRing(ring, true);
+ return isSecret() ? new CanonicalizedSecretKeyRing((PGPSecretKeyRing) ring, 1)
+ : new CanonicalizedPublicKeyRing((PGPPublicKeyRing) ring, 0);
}
/** This operation merges information from a different keyring, returning a combined
@@ -650,7 +676,7 @@ public class UncachedKeyRing {
return left.length - right.length;
}
// compare byte-by-byte
- for (int i = 0; i < left.length && i < right.length; i++) {
+ for (int i = 0; i < left.length; i++) {
if (left[i] != right[i]) {
return (left[i] & 0xff) - (right[i] & 0xff);
}
@@ -678,7 +704,14 @@ public class UncachedKeyRing {
final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID());
if (resultKey == null) {
log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, indent);
- result = replacePublicKey(result, key);
+ // special case: if both rings are secret, copy over the secret key
+ if (isSecret() && other.isSecret()) {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) candidate).getSecretKey(key.getKeyID());
+ result = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) result, sKey);
+ } else {
+ // otherwise, just insert the public key
+ result = replacePublicKey(result, key);
+ }
continue;
}
@@ -686,17 +719,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = resultKey;
// Iterate certifications
- for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) {
- int type = cert.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;
- }
-
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getKeySignatures())) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
@@ -744,8 +767,12 @@ public class UncachedKeyRing {
}
- log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW,
- indent, Integer.toString(newCerts));
+ if (newCerts > 0) {
+ log.add(LogLevel.DEBUG, LogType.MSG_MG_FOUND_NEW, indent,
+ Integer.toString(newCerts));
+ } else {
+ log.add(LogLevel.DEBUG, LogType.MSG_MG_UNCHANGED, indent);
+ }
return new UncachedKeyRing(result);
@@ -756,19 +783,20 @@ public class UncachedKeyRing {
}
- public UncachedKeyRing extractPublicKeyRing() {
+ public UncachedKeyRing extractPublicKeyRing() throws IOException {
if(!isSecret()) {
throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " +
"This is a programming error and should never happen!");
}
- ArrayList<PGPPublicKey> keys = new ArrayList();
Iterator<PGPPublicKey> it = mRing.getPublicKeys();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(2048);
while (it.hasNext()) {
- keys.add(it.next());
+ stream.write(it.next().getEncoded());
}
- return new UncachedKeyRing(new PGPPublicKeyRing(keys));
+ return new UncachedKeyRing(
+ new PGPPublicKeyRing(stream.toByteArray(), new JcaKeyFingerprintCalculator()));
}
/** This method replaces a public key in a keyring.
@@ -792,4 +820,20 @@ public class UncachedKeyRing {
return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
}
+ /** This method removes a subkey in a keyring.
+ *
+ * This method essentially wraps PGP*KeyRing.remove*Key, where the keyring may be of either
+ * the secret or public subclass.
+ *
+ * @return the resulting PGPKeyRing of the same type as the input
+ */
+ private static PGPKeyRing removeSubKey(PGPKeyRing ring, PGPPublicKey key) {
+ if (ring instanceof PGPPublicKeyRing) {
+ return PGPPublicKeyRing.removePublicKey((PGPPublicKeyRing) ring, key);
+ } else {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) ring).getSecretKey(key.getKeyID());
+ return PGPSecretKeyRing.removeSecretKey((PGPSecretKeyRing) ring, sKey);
+ }
+ }
+
}
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 33db7771b..358b1c552 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -1,16 +1,14 @@
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;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
-import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -44,14 +42,19 @@ public class UncachedPublicKey {
}
public Date getExpiryTime() {
- Date creationDate = getCreationTime();
- if (mPublicKey.getValidDays() == 0) {
+ long seconds = mPublicKey.getValidSeconds();
+ if (seconds > Integer.MAX_VALUE) {
+ Log.e(Constants.TAG, "error, expiry time too large");
+ return null;
+ }
+ if (seconds == 0) {
// no expiry
return null;
}
+ Date creationDate = getCreationTime();
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
- calendar.add(Calendar.DATE, mPublicKey.getValidDays());
+ calendar.add(Calendar.SECOND, (int) seconds);
return calendar.getTime();
}
@@ -77,26 +80,76 @@ public class UncachedPublicKey {
return mPublicKey.getBitStrength();
}
+ /** Returns the primary user id, as indicated by the public key's self certificates.
+ *
+ * This is an expensive operation, since potentially a lot of certificates (and revocations)
+ * have to be checked, and even then the result is NOT guaranteed to be constant through a
+ * canonicalization operation.
+ *
+ * Returns null if there is no primary user id (as indicated by certificates)
+ *
+ */
public String getPrimaryUserId() {
+ String found = null;
+ PGPSignature foundSig = null;
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
+ PGPSignature revocation = null;
+
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
- if (sig.getHashedSubPackets() != null
- && sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
- try {
+ try {
+
+ // if this is a revocation, this is not the user id
+ if (sig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ // make sure it's actually valid
+ sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
+ if (!sig.verifyCertification(userId, mPublicKey)) {
+ continue;
+ }
+ if (found != null && found.equals(userId)) {
+ found = null;
+ }
+ revocation = sig;
+ // this revocation may still be overridden by a newer cert
+ continue;
+ }
+
+ if (sig.getHashedSubPackets() != null && sig.getHashedSubPackets().isPrimaryUserID()) {
+ if (foundSig != null && sig.getCreationTime().before(foundSig.getCreationTime())) {
+ continue;
+ }
+ // ignore if there is a newer revocation for this user id
+ if (revocation != null && sig.getCreationTime().before(revocation.getCreationTime())) {
+ continue;
+ }
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (sig.verifyCertification(userId, mPublicKey)) {
- return userId;
+ found = userId;
+ foundSig = sig;
+ // this one can't be relevant anymore at this point
+ revocation = null;
}
- } catch (Exception e) {
- // nothing bad happens, the key is just not considered the primary key id
}
- }
+ } catch (Exception e) {
+ // nothing bad happens, the key is just not considered the primary key id
+ }
}
}
- return null;
+ return found;
+ }
+
+ /**
+ * Returns primary user id if existing. If not, return first encountered user id.
+ */
+ public String getPrimaryUserIdWithFallback() {
+ String userId = getPrimaryUserId();
+ if (userId == null) {
+ userId = (String) mPublicKey.getUserIDs().next();
+ }
+ return userId;
}
public ArrayList<String> getUnorderedUserIds() {
@@ -186,6 +239,21 @@ public class UncachedPublicKey {
return mPublicKey;
}
+ public Iterator<WrappedSignature> getSignatures() {
+ final Iterator<PGPSignature> it = mPublicKey.getSignatures();
+ return new Iterator<WrappedSignature>() {
+ public void remove() {
+ it.remove();
+ }
+ public WrappedSignature next() {
+ return new WrappedSignature(it.next());
+ }
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+ };
+ }
+
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
return new Iterator<WrappedSignature>() {
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 196ac1dee..07fb4fb9e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -15,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
+import java.util.ArrayList;
import java.util.Date;
/** OpenKeychain wrapper around PGPSignature objects.
@@ -55,12 +56,41 @@ public class WrappedSignature {
return mSig.getCreationTime();
}
+ public ArrayList<WrappedSignature> getEmbeddedSignatures() {
+ ArrayList<WrappedSignature> sigs = new ArrayList<WrappedSignature>();
+ if (!mSig.hasSubpackets()) {
+ return sigs;
+ }
+ try {
+ PGPSignatureList list;
+ if (mSig.getHashedSubPackets() != null) {
+ list = mSig.getHashedSubPackets().getEmbeddedSignatures();
+ for (int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
+ }
+ if (mSig.getUnhashedSubPackets() != null) {
+ list = mSig.getUnhashedSubPackets().getEmbeddedSignatures();
+ for (int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
+ }
+ } catch (PGPException e) {
+ // no matter
+ Log.e(Constants.TAG, "exception reading embedded signatures", e);
+ } catch (IOException e) {
+ // no matter
+ Log.e(Constants.TAG, "exception reading embedded signatures", e);
+ }
+ return sigs;
+ }
+
public byte[] getEncoded() throws IOException {
return mSig.getEncoded();
}
public boolean isRevocation() {
- return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
+ return mSig.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
}
public boolean isPrimaryUserId() {
@@ -71,6 +101,9 @@ public class WrappedSignature {
if(!isRevocation()) {
throw new PgpGeneralException("Not a revocation signature.");
}
+ if (mSig.getHashedSubPackets() == null) {
+ return null;
+ }
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
SignatureSubpacketTags.REVOCATION_REASON);
// For some reason, this is missing in SignatureSubpacketInputStream:146
@@ -80,7 +113,7 @@ public class WrappedSignature {
return ((RevocationReason) p).getRevocationDescription();
}
- public void init(WrappedPublicKey key) throws PgpGeneralException {
+ public void init(CanonicalizedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
@@ -158,7 +191,7 @@ public class WrappedSignature {
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
- public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
+ public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
@@ -179,7 +212,7 @@ public class WrappedSignature {
}
public boolean isLocal() {
- if (!mSig.hasSubpackets()
+ if (mSig.getHashedSubPackets() == null
|| !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) {
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
index bc7221d13..aa0207a6a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -83,6 +83,10 @@ public class CachedPublicKeyRing extends KeyRing {
}
}
+ public String getPrimaryUserIdWithFallback() throws PgpGeneralException {
+ return getPrimaryUserId();
+ }
+
@Override
public boolean isRevoked() throws PgpGeneralException {
try {
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 81e218ccf..aa85577e0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -28,10 +28,12 @@ import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.NullProgressable;
import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
@@ -39,8 +41,6 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
@@ -180,7 +180,7 @@ public class ProviderHelper {
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
}
- private LongSparseArray<WrappedPublicKey> getTrustedMasterKeys() {
+ private LongSparseArray<CanonicalizedPublicKey> 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
@@ -190,16 +190,15 @@ public class ProviderHelper {
}, KeyRings.HAS_ANY_SECRET + " = 1", null, null);
try {
- LongSparseArray<WrappedPublicKey> result = new LongSparseArray<WrappedPublicKey>();
+ LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<CanonicalizedPublicKey>();
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
- 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());
+ new CanonicalizedPublicKeyRing(blob, verified).getPublicKey());
}
} while (cursor.moveToNext());
@@ -217,23 +216,23 @@ public class ProviderHelper {
return new CachedPublicKeyRing(this, queryUri);
}
- public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException {
- return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
+ public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long id) throws NotFoundException {
+ return (CanonicalizedPublicKeyRing) getCanonicalizedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
}
- public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException {
- return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false);
+ public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException {
+ return (CanonicalizedPublicKeyRing) getCanonicalizedKeyRing(queryUri, false);
}
- public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException {
- return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
+ public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(long id) throws NotFoundException {
+ return (CanonicalizedSecretKeyRing) getCanonicalizedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
}
- public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException {
- return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true);
+ public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException {
+ return (CanonicalizedSecretKeyRing) getCanonicalizedKeyRing(queryUri, true);
}
- private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
+ private KeyRing getCanonicalizedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{
// we pick from cache only information that is not easily available from keyrings
@@ -252,8 +251,8 @@ public class ProviderHelper {
throw new NotFoundException("Secret key not available!");
}
return secret
- ? new WrappedSecretKeyRing(blob, true, verified)
- : new WrappedPublicKeyRing(blob, hasAnySecret, verified);
+ ? new CanonicalizedSecretKeyRing(blob, true, verified)
+ : new CanonicalizedPublicKeyRing(blob, verified);
} else {
throw new NotFoundException("Key not found!");
}
@@ -271,16 +270,8 @@ public class ProviderHelper {
* and need to be saved externally to be preserved past the operation.
*/
@SuppressWarnings("unchecked")
- private int internalSavePublicKeyRing(UncachedKeyRing keyRing,
- Progressable progress, boolean selfCertsAreTrusted) {
- if (keyRing.isSecret()) {
- log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
- return SaveKeyringResult.RESULT_ERROR;
- }
- if (!keyRing.isCanonicalized()) {
- log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
- return SaveKeyringResult.RESULT_ERROR;
- }
+ private int saveCanonicalizedPublicKeyRing(CanonicalizedPublicKeyRing keyRing,
+ Progressable progress, boolean selfCertsAreTrusted) {
// start with ok result
int result = SaveKeyringResult.SAVED_PUBLIC;
@@ -318,7 +309,7 @@ public class ProviderHelper {
{ // insert subkeys
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
int rank = 0;
- for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
+ for (CanonicalizedPublicKey key : keyRing.publicKeyIterator()) {
long keyId = key.getKeyId();
log(LogLevel.DEBUG, keyId == masterKeyId ? LogType.MSG_IP_MASTER : LogType.MSG_IP_SUBKEY,
PgpKeyHelper.convertKeyIdToHex(keyId)
@@ -401,7 +392,7 @@ public class ProviderHelper {
mIndent -= 1;
// get a list of owned secret keys, for verification filtering
- LongSparseArray<WrappedPublicKey> trustedKeys = getTrustedMasterKeys();
+ LongSparseArray<CanonicalizedPublicKey> 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.
@@ -445,13 +436,12 @@ public class ProviderHelper {
// verify signatures from known private keys
if (trustedKeys.indexOfKey(certId) >= 0) {
- WrappedPublicKey trustedKey = trustedKeys.get(certId);
+ CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);
cert.init(trustedKey);
if (cert.verifySignature(masterKey, userId)) {
item.trustedCerts.add(cert);
log(LogLevel.INFO, LogType.MSG_IP_UID_CERT_GOOD,
- PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId()),
- trustedKey.getPrimaryUserId()
+ PgpKeyHelper.convertKeyIdToHexShort(trustedKey.getKeyId())
);
} else {
log(LogLevel.WARN, LogType.MSG_IP_UID_CERT_BAD);
@@ -560,28 +550,13 @@ public class ProviderHelper {
/** Saves an UncachedKeyRing of the secret variant into the db.
* This method will fail if no corresponding public keyring is in the database!
*/
- private int internalSaveSecretKeyRing(UncachedKeyRing keyRing) {
-
- if (!keyRing.isSecret()) {
- log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
- return SaveKeyringResult.RESULT_ERROR;
- }
-
- if (!keyRing.isCanonicalized()) {
- log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
- return SaveKeyringResult.RESULT_ERROR;
- }
+ private int saveCanonicalizedSecretKeyRing(CanonicalizedSecretKeyRing keyRing) {
long masterKeyId = keyRing.getMasterKeyId();
log(LogLevel.START, LogType.MSG_IS, PgpKeyHelper.convertKeyIdToHex(masterKeyId));
mIndent += 1;
- try {
- // Canonicalize this key, to assert a number of assumptions made about it.
- keyRing = keyRing.canonicalize(mLog, mIndent);
- if (keyRing == null) {
- return SaveKeyringResult.RESULT_ERROR;
- }
+ try {
// IF this is successful, it's a secret key
int result = SaveKeyringResult.SAVED_SECRET;
@@ -616,8 +591,7 @@ public class ProviderHelper {
log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS);
mIndent += 1;
Set<Long> available = keyRing.getAvailableSubkeys();
- for (UncachedPublicKey sub :
- new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
+ for (UncachedPublicKey sub : keyRing.publicKeyIterator()) {
long id = sub.getKeyId();
if (available.contains(id)) {
int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?",
@@ -668,9 +642,16 @@ public class ProviderHelper {
log(LogLevel.START, LogType.MSG_IP, PgpKeyHelper.convertKeyIdToHex(masterKeyId));
mIndent += 1;
+ if (publicRing.isSecret()) {
+ log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ }
+
+ CanonicalizedPublicKeyRing canPublicRing;
+
// If there is an old keyring, merge it
try {
- UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached();
+ UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();
// Merge data from new public ring into the old one
publicRing = oldPublicRing.merge(publicRing, mLog, mIndent);
@@ -681,8 +662,8 @@ public class ProviderHelper {
}
// Canonicalize this keyring, to assert a number of assumptions made about it.
- publicRing = publicRing.canonicalize(mLog, mIndent);
- if (publicRing == null) {
+ canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ if (canPublicRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
@@ -696,39 +677,40 @@ public class ProviderHelper {
// Not an issue, just means we are dealing with a new keyring.
// Canonicalize this keyring, to assert a number of assumptions made about it.
- publicRing = publicRing.canonicalize(mLog, mIndent);
- if (publicRing == null) {
+ canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ if (canPublicRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
}
// If there is a secret key, merge new data (if any) and save the key for later
- UncachedKeyRing secretRing;
+ CanonicalizedSecretKeyRing canSecretRing;
try {
- secretRing = getWrappedSecretKeyRing(publicRing.getMasterKeyId()).getUncached();
+ UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId()).getUncachedKeyRing();
// Merge data from new public ring into secret one
secretRing = secretRing.merge(publicRing, mLog, mIndent);
if (secretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
- secretRing = secretRing.canonicalize(mLog, mIndent);
- if (secretRing == null) {
+ // This has always been a secret key ring, this is a safe cast
+ canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
+ if (canSecretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
} catch (NotFoundException e) {
// No secret key available (this is what happens most of the time)
- secretRing = null;
+ canSecretRing = null;
}
- int result = internalSavePublicKeyRing(publicRing, progress, secretRing != null);
+ int result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, canSecretRing != null);
// Save the saved keyring (if any)
- if (secretRing != null) {
+ if (canSecretRing != null) {
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
- int secretResult = internalSaveSecretKeyRing(secretRing);
+ int secretResult = saveCanonicalizedSecretKeyRing(canSecretRing);
if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) {
result |= SaveKeyringResult.SAVED_SECRET;
}
@@ -752,12 +734,19 @@ public class ProviderHelper {
log(LogLevel.START, LogType.MSG_IS, PgpKeyHelper.convertKeyIdToHex(masterKeyId));
mIndent += 1;
+ if ( ! secretRing.isSecret()) {
+ log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ }
+
+ CanonicalizedSecretKeyRing canSecretRing;
+
// If there is an old secret key, merge it.
try {
- UncachedKeyRing oldSecretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
+ UncachedKeyRing oldSecretRing = getCanonicalizedSecretKeyRing(masterKeyId).getUncachedKeyRing();
// Merge data from new secret ring into old one
- secretRing = oldSecretRing.merge(secretRing, mLog, mIndent);
+ secretRing = secretRing.merge(oldSecretRing, mLog, mIndent);
// If this is null, there is an error in the log so we can just return
if (secretRing == null) {
@@ -765,8 +754,9 @@ public class ProviderHelper {
}
// Canonicalize this keyring, to assert a number of assumptions made about it.
- secretRing = secretRing.canonicalize(mLog, mIndent);
- if (secretRing == null) {
+ // This is a safe cast, because we made sure this is a secret ring above
+ canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
+ if (canSecretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
@@ -781,8 +771,9 @@ public class ProviderHelper {
// Not an issue, just means we are dealing with a new keyring
// Canonicalize this keyring, to assert a number of assumptions made about it.
- secretRing = secretRing.canonicalize(mLog, mIndent);
- if (secretRing == null) {
+ // This is a safe cast, because we made sure this is a secret ring above
+ canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
+ if (canSecretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
@@ -791,39 +782,34 @@ public class ProviderHelper {
// Merge new data into public keyring as well, if there is any
UncachedKeyRing publicRing;
try {
- UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncached();
+ UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();
- // Merge data from new public ring into secret one
+ // Merge data from new secret ring into public one
publicRing = oldPublicRing.merge(secretRing, mLog, mIndent);
if (publicRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
- // If nothing changed, never mind
- if (Arrays.hashCode(publicRing.getEncoded())
- == Arrays.hashCode(oldPublicRing.getEncoded())) {
- publicRing = null;
- }
-
} catch (NotFoundException e) {
log(LogLevel.DEBUG, LogType.MSG_IS_PUBRING_GENERATE);
publicRing = secretRing.extractPublicKeyRing();
}
- if (publicRing != null) {
- publicRing = publicRing.canonicalize(mLog, mIndent);
- if (publicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
- }
+ CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ if (canPublicRing == null) {
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ }
- int result = internalSavePublicKeyRing(publicRing, progress, true);
- if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
- }
+ int result;
+
+ result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true);
+ if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
- int result = internalSaveSecretKeyRing(secretRing);
+ result = saveCanonicalizedSecretKeyRing(canSecretRing);
+
return new SaveKeyringResult(result, mLog);
} catch (IOException e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 17c277026..5ed95acb3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -53,6 +53,12 @@ import java.util.Set;
public class OpenPgpService extends RemoteService {
+ static final String[] KEYRING_PROJECTION =
+ new String[]{
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ };
+
/**
* Search database for key ids based on emails.
*
@@ -70,7 +76,7 @@ public class OpenPgpService extends RemoteService {
for (String email : encryptionUserIds) {
Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
- Cursor cursor = getContentResolver().query(uri, null, null, null, null);
+ Cursor cursor = getContentResolver().query(uri, KEYRING_PROJECTION, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
@@ -419,7 +425,7 @@ public class OpenPgpService extends RemoteService {
try {
// try to find key, throws NotFoundException if not in db!
- mProviderHelper.getWrappedPublicKeyRing(masterKeyId);
+ mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 0b1d521ad..2ba792f9a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -32,10 +32,11 @@ import android.widget.Button;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
-import org.sufficientlysecure.keychain.ui.EditKeyActivityOld;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
import org.sufficientlysecure.keychain.util.AlgorithmNames;
@@ -163,11 +164,11 @@ public class AccountSettingsFragment extends Fragment implements
}
private void createKey() {
- Intent intent = new Intent(getActivity(), EditKeyActivityOld.class);
- intent.setAction(EditKeyActivityOld.ACTION_CREATE_KEY);
- intent.putExtra(EditKeyActivityOld.EXTRA_GENERATE_DEFAULT_KEYS, true);
- // set default user id to account name
- intent.putExtra(EditKeyActivityOld.EXTRA_USER_IDS, mAccSettings.getAccountName());
+ String[] userId = KeyRing.splitUserId(mAccSettings.getAccountName());
+
+ Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
+ intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId[0]);
+ intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId[1]);
startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
}
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 00f210cc1..7318833e1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -31,11 +31,15 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.FileImportCache;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
+import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -44,15 +48,13 @@ 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.WrappedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
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.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -138,9 +140,6 @@ public class KeychainIntentService extends IntentService
// delete file securely
public static final String DELETE_FILE = "deleteFile";
- // import key
- public static final String IMPORT_KEY_LIST = "import_key_list";
-
// export key
public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";
public static final String EXPORT_FILENAME = "export_filename";
@@ -175,7 +174,7 @@ public class KeychainIntentService extends IntentService
// export
public static final String RESULT_EXPORT = "exported";
- public static final String RESULT = "result";
+ public static final String RESULT_IMPORT = "result";
Messenger mMessenger;
@@ -342,39 +341,37 @@ public class KeychainIntentService extends IntentService
/* Operation */
ProviderHelper providerHelper = new ProviderHelper(this);
- PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 50, 100));
- try {
- OperationLog log = new OperationLog();
- UncachedKeyRing ring;
- if (saveParcel.mMasterKeyId != null) {
- String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
- WrappedSecretKeyRing secRing =
- providerHelper.getWrappedSecretKeyRing(saveParcel.mMasterKeyId);
-
- ring = keyOperations.modifySecretKeyRing(secRing, saveParcel,
- passphrase, log, 0);
- } else {
- ring = keyOperations.createSecretKeyRing(saveParcel, log, 0);
- }
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100));
+ EditKeyResult result;
- providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 10, 95, 100));
+ if (saveParcel.mMasterKeyId != null) {
+ String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
+ CanonicalizedSecretKeyRing secRing =
+ providerHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
- // cache new passphrase
- if (saveParcel.newPassphrase != null) {
- PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(),
- saveParcel.newPassphrase);
- }
- } catch (ProviderHelper.NotFoundException e) {
- sendErrorToHandler(e);
+ result = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
+ } else {
+ result = keyOperations.createSecretKeyRing(saveParcel);
+ }
+
+ UncachedKeyRing ring = result.getRing();
+
+ providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
+
+ // cache new passphrase
+ if (saveParcel.mNewPassphrase != null) {
+ PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(),
+ saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback());
}
setProgress(R.string.progress_done, 100, 100);
/* Output */
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
} catch (Exception e) {
sendErrorToHandler(e);
}
+
} else if (ACTION_DELETE_FILE_SECURELY.equals(action)) {
try {
/* Input */
@@ -398,13 +395,15 @@ public class KeychainIntentService extends IntentService
}
} else if (ACTION_IMPORT_KEYRING.equals(action)) {
try {
- List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
+ // get entries from cached file
+ FileImportCache cache = new FileImportCache(this);
+ List<ParcelableKeyRing> entries = cache.readCache();
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
- OperationResults.ImportResult result = pgpImportExport.importKeyRings(entries);
+ ImportKeyResult result = pgpImportExport.importKeyRings(entries);
Bundle resultData = new Bundle();
- resultData.putParcelable(RESULT, result);
+ resultData.putParcelable(RESULT_IMPORT, result);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
@@ -488,7 +487,7 @@ public class KeychainIntentService extends IntentService
HkpKeyserver server = new HkpKeyserver(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this);
- WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri);
+ CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
@@ -537,7 +536,6 @@ public class KeychainIntentService extends IntentService
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
- importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@@ -564,9 +562,9 @@ public class KeychainIntentService extends IntentService
}
ProviderHelper providerHelper = new ProviderHelper(this);
- WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId);
- WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
- WrappedSecretKey certificationKey = secretKeyRing.getSubKey();
+ CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId);
+ CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId);
+ CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey();
if(!certificationKey.unlock(signaturePassphrase)) {
throw new PgpGeneralException("Error extracting key (bad passphrase?)");
}
@@ -644,6 +642,12 @@ public class KeychainIntentService extends IntentService
}
}
+ private void sendMessageToHandler(Integer arg1, OperationResultParcel data) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(OperationResultParcel.EXTRA_RESULT, data);
+ sendMessageToHandler(arg1, null, bundle);
+ }
+
private void sendMessageToHandler(Integer arg1, Bundle data) {
sendMessageToHandler(arg1, null, data);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
index 755827482..0cdbe708e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
@@ -25,12 +25,11 @@ import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
public class KeychainIntentServiceHandler extends Handler {
@@ -102,9 +101,9 @@ public class KeychainIntentServiceHandler extends Handler {
// show error from service
if (data.containsKey(DATA_ERROR)) {
- AppMsg.makeText(mActivity,
+ Notify.showNotify(mActivity,
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
- AppMsg.STYLE_ALERT).show();
+ Notify.Style.ERROR);
}
break;
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 6bf6b655d..c27b3f6da 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
@@ -1,14 +1,27 @@
package org.sufficientlysecure.keychain.service;
+import android.app.Activity;
+import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.View;
+
+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;
+import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
/** Represent the result of an operation.
*
@@ -21,6 +34,9 @@ import java.util.ArrayList;
*
*/
public class OperationResultParcel implements Parcelable {
+
+ public static final String EXTRA_RESULT = "operation_result";
+
/** 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. */
@@ -67,9 +83,7 @@ public class OperationResultParcel implements Parcelable {
mType = type;
mParameters = parameters;
mIndent = indent;
- }
- public LogEntryParcel(LogLevel level, LogType type, Object... parameters) {
- this(level, type, 0, parameters);
+ Log.v(Constants.TAG, "log: " + this.toString());
}
public LogEntryParcel(Parcel source) {
@@ -102,6 +116,77 @@ public class OperationResultParcel implements Parcelable {
}
};
+ @Override
+ public String toString() {
+ return "LogEntryParcel{" +
+ "mLevel=" + mLevel +
+ ", mType=" + mType +
+ ", mParameters=" + Arrays.toString(mParameters) +
+ ", mIndent=" + mIndent +
+ '}';
+ }
+ }
+
+ public SuperCardToast createNotify(final Activity activity) {
+
+ int resultType = getResult();
+
+ String str;
+ int duration, color;
+
+ // Not an overall failure
+ if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) {
+
+ if (getLog().containsWarnings()) {
+ duration = 0;
+ color = Style.ORANGE;
+ } else {
+ duration = SuperToast.Duration.LONG;
+ color = Style.GREEN;
+ }
+
+ str = "operation succeeded!";
+ // str = activity.getString(R.string.import_error);
+
+ } else {
+
+ duration = 0;
+ color = Style.RED;
+
+ str = "operation failed";
+ // str = activity.getString(R.string.import_error);
+
+ }
+
+ boolean button = getLog() != null && !getLog().isEmpty();
+ SuperCardToast toast = new SuperCardToast(activity,
+ button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
+ Style.getStyle(color, SuperToast.Animations.POPUP));
+ toast.setText(str);
+ toast.setDuration(duration);
+ toast.setIndeterminate(duration == 0);
+ toast.setSwipeToDismiss(true);
+ // If we have a log and it's non-empty, show a View Log button
+ if (button) {
+ toast.setButtonIcon(R.drawable.ic_action_view_as_list,
+ activity.getResources().getString(R.string.view_log));
+ toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
+ toast.setTextColor(activity.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(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResultParcel.this);
+ activity.startActivity(intent);
+ }
+ }
+ ));
+ }
+
+ return toast;
+
}
/** This is an enum of all possible log events.
@@ -123,6 +208,8 @@ public class OperationResultParcel implements Parcelable {
*/
public static enum LogType {
+ INTERNAL_ERROR (R.string.internal_error),
+
// import public
MSG_IP(R.string.msg_ip),
MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch),
@@ -193,6 +280,7 @@ public class OperationResultParcel implements Parcelable {
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_TYPE_UID (R.string.msg_kc_revoke_bad_type_uid),
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),
@@ -224,6 +312,7 @@ 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_KC_UID_REMOVE (R.string.msg_kc_uid_remove),
// keyring consolidation
@@ -233,9 +322,17 @@ public class OperationResultParcel implements Parcelable {
MSG_MG_HETEROGENEOUS (R.string.msg_mg_heterogeneous),
MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey),
MSG_MG_FOUND_NEW (R.string.msg_mg_found_new),
+ MSG_MG_UNCHANGED (R.string.msg_mg_unchanged),
// secret key create
- MSG_CR_ERROR_NO_MASTER (R.string.msg_mr),
+ MSG_CR (R.string.msg_cr),
+ MSG_CR_ERROR_NO_MASTER (R.string.msg_cr_error_no_master),
+ MSG_CR_ERROR_NO_USER_ID (R.string.msg_cr_error_no_user_id),
+ MSG_CR_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify),
+ MSG_CR_ERROR_KEYSIZE_512 (R.string.msg_cr_error_keysize_512),
+ MSG_CR_ERROR_UNKNOWN_ALGO (R.string.msg_cr_error_unknown_algo),
+ MSG_CR_ERROR_INTERNAL_PGP (R.string.msg_cr_error_internal_pgp),
+ MSG_CR_ERROR_MASTER_ELGAMAL (R.string.msg_cr_error_master_elgamal),
// secret key modify
MSG_MF (R.string.msg_mr),
@@ -243,10 +340,13 @@ public class OperationResultParcel implements Parcelable {
MSG_MF_ERROR_FINGERPRINT (R.string.msg_mf_error_fingerprint),
MSG_MF_ERROR_KEYID (R.string.msg_mf_error_keyid),
MSG_MF_ERROR_INTEGRITY (R.string.msg_mf_error_integrity),
+ MSG_MF_ERROR_NOEXIST_PRIMARY (R.string.msg_mf_error_noexist_primary),
MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary),
MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp),
MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig),
MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase),
+ MSG_MF_PRIMARY_REPLACE_OLD (R.string.msg_mf_primary_replace_old),
+ MSG_MF_PRIMARY_NEW (R.string.msg_mf_primary_new),
MSG_MF_SUBKEY_CHANGE (R.string.msg_mf_subkey_change),
MSG_MF_SUBKEY_MISSING (R.string.msg_mf_subkey_missing),
MSG_MF_SUBKEY_NEW_ID (R.string.msg_mf_subkey_new_id),
@@ -257,6 +357,7 @@ public class OperationResultParcel implements Parcelable {
MSG_MF_UID_ADD (R.string.msg_mf_uid_add),
MSG_MF_UID_PRIMARY (R.string.msg_mf_uid_primary),
MSG_MF_UID_REVOKE (R.string.msg_mf_uid_revoke),
+ MSG_MF_UID_ERROR_EMPTY (R.string.msg_mf_uid_error_empty),
MSG_MF_UNLOCK_ERROR (R.string.msg_mf_unlock_error),
MSG_MF_UNLOCK (R.string.msg_mf_unlock),
;
@@ -288,7 +389,7 @@ public class OperationResultParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mResult);
- dest.writeTypedList(mLog);
+ dest.writeTypedList(mLog.toList());
}
public static final Creator<OperationResultParcel> CREATOR = new Creator<OperationResultParcel>() {
@@ -301,20 +402,21 @@ public class OperationResultParcel implements Parcelable {
}
};
- public static class OperationLog extends ArrayList<LogEntryParcel> {
+ public static class OperationLog implements Iterable<LogEntryParcel> {
+
+ private final List<LogEntryParcel> mParcels = new ArrayList<LogEntryParcel>();
/// Simple convenience method
public void add(LogLevel level, LogType type, int indent, Object... parameters) {
- Log.d(Constants.TAG, type.toString());
- add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters));
+ mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters));
}
public void add(LogLevel level, LogType type, int indent) {
- add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
+ mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
}
public boolean containsWarnings() {
- for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(iterator())) {
+ for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {
return true;
}
@@ -322,6 +424,22 @@ public class OperationResultParcel implements Parcelable {
return false;
}
+ public void addAll(List<LogEntryParcel> parcels) {
+ mParcels.addAll(parcels);
+ }
+
+ public List<LogEntryParcel> toList() {
+ return mParcels;
+ }
+
+ public boolean isEmpty() {
+ return mParcels.isEmpty();
+ }
+
+ @Override
+ public Iterator<LogEntryParcel> iterator() {
+ return mParcels.iterator();
+ }
}
}
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 fd3d4ed00..11829e7b8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
@@ -12,12 +12,13 @@ import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
public abstract class OperationResults {
- public static class ImportResult extends OperationResultParcel {
+ public static class ImportKeyResult extends OperationResultParcel {
public final int mNewKeys, mUpdatedKeys, mBadKeys;
@@ -47,15 +48,15 @@ public abstract class OperationResults {
return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;
}
- public ImportResult(Parcel source) {
+ public ImportKeyResult(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) {
+ public ImportKeyResult(int result, OperationLog log,
+ int newKeys, int updatedKeys, int badKeys) {
super(result, log);
mNewKeys = newKeys;
mUpdatedKeys = updatedKeys;
@@ -70,17 +71,17 @@ public abstract class OperationResults {
dest.writeInt(mBadKeys);
}
- public static Creator<ImportResult> CREATOR = new Creator<ImportResult>() {
- public ImportResult createFromParcel(final Parcel source) {
- return new ImportResult(source);
+ public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() {
+ public ImportKeyResult createFromParcel(final Parcel source) {
+ return new ImportKeyResult(source);
}
- public ImportResult[] newArray(final int size) {
- return new ImportResult[size];
+ public ImportKeyResult[] newArray(final int size) {
+ return new ImportKeyResult[size];
}
};
- public void displayNotify(final Activity activity) {
+ public SuperCardToast createNotify(final Activity activity) {
int resultType = getResult();
@@ -88,11 +89,11 @@ public abstract class OperationResults {
int duration, color;
// Not an overall failure
- if ((resultType & ImportResult.RESULT_ERROR) == 0) {
+ if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) {
String withWarnings;
// Any warnings?
- if ((resultType & ImportResult.RESULT_WITH_WARNINGS) > 0) {
+ if ((resultType & ImportKeyResult.RESULT_WITH_WARNINGS) > 0) {
duration = 0;
color = Style.ORANGE;
withWarnings = activity.getResources().getString(R.string.import_with_warnings);
@@ -106,7 +107,7 @@ public abstract class OperationResults {
if (this.isOkBoth()) {
str = activity.getResources().getQuantityString(
R.plurals.import_keys_added_and_updated_1, mNewKeys, mNewKeys);
- str += activity.getResources().getQuantityString(
+ str += " "+ activity.getResources().getQuantityString(
R.plurals.import_keys_added_and_updated_2, mUpdatedKeys, mUpdatedKeys, withWarnings);
} else if (isOkUpdated()) {
str = activity.getResources().getQuantityString(
@@ -142,7 +143,7 @@ public abstract class OperationResults {
// If we have a log and it's non-empty, show a View Log button
if (button) {
toast.setButtonIcon(R.drawable.ic_action_view_as_list,
- activity.getResources().getString(R.string.import_view_log));
+ activity.getResources().getString(R.string.view_log));
toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
toast.setTextColor(activity.getResources().getColor(R.color.black));
toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
@@ -151,18 +152,59 @@ public abstract class OperationResults {
public void onClick(View view, Parcelable token) {
Intent intent = new Intent(
activity, LogDisplayActivity.class);
- intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportResult.this);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportKeyResult.this);
activity.startActivity(intent);
}
}
));
}
- toast.show();
+ return toast;
+
+ }
+
+ }
+
+ public static class EditKeyResult extends OperationResultParcel {
+
+ private transient UncachedKeyRing mRing;
+ public final Long mRingMasterKeyId;
+
+ public EditKeyResult(int result, OperationLog log,
+ UncachedKeyRing ring) {
+ super(result, log);
+ mRing = ring;
+ mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : null;
+ }
+
+ public UncachedKeyRing getRing() {
+ return mRing;
+ }
+
+ public EditKeyResult(Parcel source) {
+ super(source);
+ mRingMasterKeyId = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mRingMasterKeyId);
}
+ public static Creator<EditKeyResult> CREATOR = new Creator<EditKeyResult>() {
+ public EditKeyResult createFromParcel(final Parcel source) {
+ return new EditKeyResult(source);
+ }
+
+ public EditKeyResult[] newArray(final int size) {
+ return new EditKeyResult[size];
+ }
+ };
+
}
+
public static class SaveKeyringResult extends OperationResultParcel {
public SaveKeyringResult(int result, OperationLog log) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 28f230f71..c4ecfdec5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.service;
import android.app.AlarmManager;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
@@ -25,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -32,11 +34,15 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+
import android.support.v4.util.LongSparseArray;
+import android.support.v4.app.NotificationCompat;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
@@ -54,6 +60,8 @@ public class PassphraseCacheService extends Service {
+ "PASSPHRASE_CACHE_ADD";
public static final String ACTION_PASSPHRASE_CACHE_GET = Constants.INTENT_PREFIX
+ "PASSPHRASE_CACHE_GET";
+ public static final String ACTION_PASSPHRASE_CACHE_CLEAR = Constants.INTENT_PREFIX
+ + "PASSPHRASE_CACHE_CLEAR";
public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX
+ "PASSPHRASE_CACHE_BROADCAST";
@@ -62,13 +70,16 @@ public class PassphraseCacheService extends Service {
public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_PASSPHRASE = "passphrase";
public static final String EXTRA_MESSENGER = "messenger";
+ public static final String EXTRA_USER_ID = "user_id";
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TTL = 15;
+ private static final int NOTIFICATION_ID = 1;
+
private BroadcastReceiver mIntentReceiver;
- private LongSparseArray<String> mPassphraseCache = new LongSparseArray<String>();
+ private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>();
Context mContext;
@@ -81,14 +92,17 @@ public class PassphraseCacheService extends Service {
* @param keyId
* @param passphrase
*/
- public static void addCachedPassphrase(Context context, long keyId, String passphrase) {
+ public static void addCachedPassphrase(Context context, long keyId, String passphrase,
+ String primaryUserId) {
Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + keyId);
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_ADD);
+
intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl());
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
intent.putExtra(EXTRA_KEY_ID, keyId);
+ intent.putExtra(EXTRA_USER_ID, primaryUserId);
context.startService(intent);
}
@@ -159,42 +173,46 @@ public class PassphraseCacheService extends Service {
// passphrase for symmetric encryption?
if (keyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
- String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
+ String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
if (cachedPassphrase == null) {
return null;
}
- addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase);
+ addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase, getString(R.string.passp_cache_notif_pwd));
return cachedPassphrase;
}
// try to get master key id which is used as an identifier for cached passphrases
try {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId);
- WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing(
+ CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
// no passphrase needed? just add empty string and return it, then
if (!key.hasPassphrase()) {
Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
- addCachedPassphrase(this, keyId, "");
+ try {
+ addCachedPassphrase(this, keyId, "", key.getPrimaryUserIdWithFallback());
+ } catch (PgpGeneralException e) {
+ Log.d(Constants.TAG, "PgpGeneralException occured");
+ }
return "";
}
// get cached passphrase
- String cachedPassphrase = mPassphraseCache.get(keyId);
+ CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) {
- Log.d(Constants.TAG, "PassphraseCacheService Passphrase not (yet) cached, returning null");
+ Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null");
// not really an error, just means the passphrase is not cached but not empty either
return null;
}
// set it again to reset the cache life cycle
- Log.d(Constants.TAG, "PassphraseCacheService Cache passphrase again when getting it!");
- addCachedPassphrase(this, keyId, cachedPassphrase);
- return cachedPassphrase;
+ Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!");
+ addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
+ return cachedPassphrase.getPassphrase();
} catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "PassphraseCacheService Passphrase for unknown key was requested!");
+ Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");
return null;
}
}
@@ -211,7 +229,7 @@ public class PassphraseCacheService extends Service {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- Log.d(Constants.TAG, "PassphraseCacheService Received broadcast...");
+ Log.d(Constants.TAG, "PassphraseCacheService: Received broadcast...");
if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
@@ -236,10 +254,8 @@ public class PassphraseCacheService extends Service {
private static PendingIntent buildIntent(Context context, long keyId) {
Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);
intent.putExtra(EXTRA_KEY_ID, keyId);
- PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent,
+ return PendingIntent.getBroadcast(context, REQUEST_ID, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
-
- return sender;
}
/**
@@ -256,14 +272,17 @@ public class PassphraseCacheService extends Service {
if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) {
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
+
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
+ String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);
Log.d(Constants.TAG,
- "PassphraseCacheService Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: "
- + keyId + ", ttl: " + ttl);
+ "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: "
+ + keyId + ", ttl: " + ttl + ", usrId: " + primaryUserID
+ );
- // add keyId and passphrase to memory
- mPassphraseCache.put(keyId, passphrase);
+ // add keyId, passphrase and primary user id to memory
+ mPassphraseCache.put(keyId, new CachedPassphrase(passphrase, primaryUserID));
if (ttl > 0) {
// register new alarm with keyId for this passphrase
@@ -271,6 +290,8 @@ public class PassphraseCacheService extends Service {
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId));
}
+
+ updateService();
} else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
@@ -284,10 +305,21 @@ public class PassphraseCacheService extends Service {
try {
messenger.send(msg);
} catch (RemoteException e) {
- Log.e(Constants.TAG, "PassphraseCacheService Sending message failed", e);
+ Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e);
}
+ } else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) {
+ AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+
+ // Stop all ttl alarms
+ for (int i = 0; i < mPassphraseCache.size(); i++) {
+ am.cancel(buildIntent(this, mPassphraseCache.keyAt(i)));
+ }
+
+ mPassphraseCache.clear();
+
+ updateService();
} else {
- Log.e(Constants.TAG, "PassphraseCacheService Intent or Intent Action not supported!");
+ Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");
}
}
@@ -306,11 +338,74 @@ public class PassphraseCacheService extends Service {
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
- // stop whole service if no cached passphrases remaining
- if (mPassphraseCache.size() == 0) {
- Log.d(Constants.TAG, "PassphraseCacheServic No passphrases remaining in memory, stopping service!");
- stopSelf();
+ updateService();
+ }
+
+ private void updateService() {
+ if (mPassphraseCache.size() > 0) {
+ startForeground(NOTIFICATION_ID, getNotification());
+ } else {
+ // stop whole service if no cached passphrases remaining
+ Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!");
+ stopForeground(true);
+ }
+ }
+
+ private Notification getNotification() {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ builder.setSmallIcon(R.drawable.ic_launcher)
+ .setContentTitle(getString(R.string.app_name))
+ .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys),
+ mPassphraseCache.size()));
+
+ NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
+
+ inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
+
+ // Moves events into the big view
+ for (int i = 0; i < mPassphraseCache.size(); i++) {
+ inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
+ }
+
+ // Moves the big view style object into the notification object.
+ builder.setStyle(inboxStyle);
+
+ // Add purging action
+ Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+ builder.addAction(
+ R.drawable.abc_ic_clear_normal,
+ getString(R.string.passp_cache_notif_clear),
+ PendingIntent.getService(
+ getApplicationContext(),
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ );
+ } else {
+ // Fallback, since expandable notifications weren't available back then
+ builder.setSmallIcon(R.drawable.ic_launcher)
+ .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys,
+ mPassphraseCache.size())))
+ .setContentText(getString(R.string.passp_cache_notif_click_to_clear));
+
+ Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+
+ builder.setContentIntent(
+ PendingIntent.getService(
+ getApplicationContext(),
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ );
}
+
+ return builder.build();
}
@Override
@@ -341,4 +436,29 @@ public class PassphraseCacheService extends Service {
private final IBinder mBinder = new PassphraseCacheBinder();
-}
+ public class CachedPassphrase {
+ private String primaryUserID;
+ private String passphrase;
+
+ public CachedPassphrase(String passphrase, String primaryUserID) {
+ setPassphrase(passphrase);
+ setPrimaryUserID(primaryUserID);
+ }
+
+ public String getPrimaryUserID() {
+ return primaryUserID;
+ }
+
+ public String getPassphrase() {
+ return passphrase;
+ }
+
+ public void setPrimaryUserID(String primaryUserID) {
+ this.primaryUserID = primaryUserID;
+ }
+
+ public void setPassphrase(String passphrase) {
+ this.passphrase = passphrase;
+ }
+ }
+} \ No newline at end of file
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 a56095767..5e90b396e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -27,23 +27,19 @@ public class SaveKeyringParcel implements Parcelable {
// the key fingerprint, for safety. MUST be null for a new key.
public byte[] mFingerprint;
- public String newPassphrase;
+ public String mNewPassphrase;
- public ArrayList<String> addUserIds;
- public ArrayList<SubkeyAdd> addSubKeys;
+ public ArrayList<String> mAddUserIds;
+ public ArrayList<SubkeyAdd> mAddSubKeys;
- public ArrayList<SubkeyChange> changeSubKeys;
- public String changePrimaryUserId;
+ public ArrayList<SubkeyChange> mChangeSubKeys;
+ public String mChangePrimaryUserId;
- public ArrayList<String> revokeUserIds;
- public ArrayList<Long> revokeSubKeys;
+ public ArrayList<String> mRevokeUserIds;
+ public ArrayList<Long> mRevokeSubKeys;
public SaveKeyringParcel() {
- addUserIds = new ArrayList<String>();
- addSubKeys = new ArrayList<SubkeyAdd>();
- changeSubKeys = new ArrayList<SubkeyChange>();
- revokeUserIds = new ArrayList<String>();
- revokeSubKeys = new ArrayList<Long>();
+ reset();
}
public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
@@ -52,6 +48,16 @@ public class SaveKeyringParcel implements Parcelable {
mFingerprint = fingerprint;
}
+ public void reset() {
+ mNewPassphrase = null;
+ mAddUserIds = new ArrayList<String>();
+ mAddSubKeys = new ArrayList<SubkeyAdd>();
+ mChangePrimaryUserId = null;
+ mChangeSubKeys = new ArrayList<SubkeyChange>();
+ mRevokeUserIds = new ArrayList<String>();
+ mRevokeSubKeys = new ArrayList<Long>();
+ }
+
// performance gain for using Parcelable here would probably be negligible,
// use Serializable instead.
public static class SubkeyAdd implements Serializable {
@@ -70,6 +76,7 @@ public class SaveKeyringParcel implements Parcelable {
public static class SubkeyChange implements Serializable {
public long mKeyId;
public Integer mFlags;
+ // this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
public Long mExpiry;
public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId;
@@ -82,16 +89,16 @@ public class SaveKeyringParcel implements Parcelable {
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray();
- newPassphrase = source.readString();
+ mNewPassphrase = source.readString();
- addUserIds = source.createStringArrayList();
- addSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable();
+ mAddUserIds = source.createStringArrayList();
+ mAddSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable();
- changeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable();
- changePrimaryUserId = source.readString();
+ mChangeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable();
+ mChangePrimaryUserId = source.readString();
- revokeUserIds = source.createStringArrayList();
- revokeSubKeys = (ArrayList<Long>) source.readSerializable();
+ mRevokeUserIds = source.createStringArrayList();
+ mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
}
@Override
@@ -102,16 +109,16 @@ public class SaveKeyringParcel implements Parcelable {
}
destination.writeByteArray(mFingerprint);
- destination.writeString(newPassphrase);
+ destination.writeString(mNewPassphrase);
- destination.writeStringList(addUserIds);
- destination.writeSerializable(addSubKeys);
+ destination.writeStringList(mAddUserIds);
+ destination.writeSerializable(mAddSubKeys);
- destination.writeSerializable(changeSubKeys);
- destination.writeString(changePrimaryUserId);
+ destination.writeSerializable(mChangeSubKeys);
+ destination.writeString(mChangePrimaryUserId);
- destination.writeStringList(revokeUserIds);
- destination.writeSerializable(revokeSubKeys);
+ destination.writeStringList(mRevokeUserIds);
+ destination.writeSerializable(mRevokeSubKeys);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java
deleted file mode 100644
index a565f0707..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.sufficientlysecure.keychain.testsupport;
-
-import android.content.Context;
-
-import org.sufficientlysecure.keychain.pgp.NullProgressable;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.OperationResults;
-
-import java.util.Collection;
-
-/**
- * Helper for tests of the Keyring import in ProviderHelper.
- */
-public class KeyringTestingHelper {
-
- private final Context context;
-
- public KeyringTestingHelper(Context robolectricContext) {
- this.context = robolectricContext;
- }
-
- public boolean addKeyring(Collection<String> blobFiles) throws Exception {
-
- ProviderHelper providerHelper = new ProviderHelper(context);
-
- byte[] data = TestDataUtil.readAllFully(blobFiles);
- UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
- long masterKeyId = ring.getMasterKeyId();
-
- // Should throw an exception; key is not yet saved
- retrieveKeyAndExpectNotFound(providerHelper, masterKeyId);
-
- OperationResults.SaveKeyringResult saveKeyringResult = providerHelper.savePublicKeyRing(ring, new NullProgressable());
-
- boolean saveSuccess = saveKeyringResult.success();
-
- // Now re-retrieve the saved key. Should not throw an exception.
- providerHelper.getWrappedPublicKeyRing(masterKeyId);
-
- // A different ID should still fail
- retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1);
-
- return saveSuccess;
- }
-
-
- private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) {
- try {
- providerHelper.getWrappedPublicKeyRing(masterKeyId);
- throw new AssertionError("Was expecting the previous call to fail!");
- } catch (ProviderHelper.NotFoundException expectedException) {
- // good
- }
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java
deleted file mode 100644
index c6d834bf9..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.sufficientlysecure.keychain.testsupport;
-
-import android.content.Context;
-import android.net.Uri;
-
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-
-/**
- * Created by art on 21/06/14.
- */
-class ProviderHelperStub extends ProviderHelper {
- public ProviderHelperStub(Context context) {
- super(context);
- }
-
- @Override
- public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException {
- byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob"));
- return new WrappedPublicKeyRing(data, false, 0);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java
deleted file mode 100644
index 9e6ede761..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.sufficientlysecure.keychain.testsupport;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collection;
-
-/**
- * Misc support functions. Would just use Guava / Apache Commons but
- * avoiding extra dependencies.
- */
-public class TestDataUtil {
- public static byte[] readFully(InputStream input) {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- appendToOutput(input, output);
- return output.toByteArray();
- }
-
- private static void appendToOutput(InputStream input, ByteArrayOutputStream output) {
- byte[] buffer = new byte[8192];
- int bytesRead;
- try {
- while ((bytesRead = input.read(buffer)) != -1) {
- output.write(buffer, 0, bytesRead);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static byte[] readAllFully(Collection<String> inputResources) {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
-
- for (String inputResource : inputResources) {
- appendToOutput(getResourceAsStream(inputResource), output);
- }
- return output.toByteArray();
- }
-
-
- public static InputStream getResourceAsStream(String resourceName) {
- return TestDataUtil.class.getResourceAsStream(resourceName);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java
deleted file mode 100644
index 1cc0f9a95..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * Test support classes.
- * This is only in main code because of gradle-Android Studio-robolectric issues. Having
- * classes in main code means IDE autocomplete, class detection, etc., all works.
- * TODO Move into test package when possible
- */
-package org.sufficientlysecure.keychain.testsupport;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index 7ac229b91..3c69fb071 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -37,12 +38,11 @@ import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@@ -52,10 +52,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.ArrayList;
@@ -64,7 +66,8 @@ import java.util.ArrayList;
*/
public class CertifyKeyActivity extends ActionBarActivity implements
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
- private View mSignButton;
+ private View mCertifyButton;
+ private ImageView mActionCertifyImage;
private CheckBox mUploadKeyCheckbox;
private Spinner mSelectKeyserverSpinner;
@@ -88,10 +91,19 @@ public class CertifyKeyActivity extends ActionBarActivity implements
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager()
.findFragmentById(R.id.sign_key_select_key_fragment);
+ mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
+ mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);
+ mCertifyButton = findViewById(R.id.certify_key_certify_button);
+ mActionCertifyImage = (ImageView) findViewById(R.id.certify_key_action_certify_image);
+ mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
+
+ // make certify image gray, like action icons
+ mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ PorterDuff.Mode.SRC_IN);
+
mSelectKeyFragment.setCallback(this);
mSelectKeyFragment.setFilterCertify(true);
- mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
.getKeyServers()
@@ -99,7 +111,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSelectKeyserverSpinner.setAdapter(adapter);
- mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);
if (!mUploadKeyCheckbox.isChecked()) {
mSelectKeyserverSpinner.setEnabled(false);
} else {
@@ -118,14 +129,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
});
- mSignButton = findViewById(R.id.sign_key_sign_button);
- mSignButton.setOnClickListener(new OnClickListener() {
+ mCertifyButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mPubKeyId != 0) {
if (mMasterKeyId == 0) {
mSelectKeyFragment.setError(getString(R.string.select_key_to_certify));
+ Notify.showNotify(CertifyKeyActivity.this, getString(R.string.select_key_to_certify),
+ Notify.Style.ERROR);
} else {
initiateSigning();
}
@@ -141,7 +153,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
Log.e(Constants.TAG, "uri: " + mDataUri);
- mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
mUserIdsAdapter = new UserIdsAdapter(this, null, 0, true);
mUserIds.setAdapter(mUserIdsAdapter);
@@ -230,7 +241,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements
startSigning();
}
}
- });
+ }
+ );
// bail out; need to wait until the user has entered the passphrase before trying again
return;
} else {
@@ -246,8 +258,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// Bail out if there is not at least one user id selected
ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();
if (userIds.isEmpty()) {
- AppMsg.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!",
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(CertifyKeyActivity.this, "No identities selected!",
+ Notify.Style.ERROR);
return;
}
@@ -274,8 +286,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- AppMsg.makeText(CertifyKeyActivity.this, R.string.key_certify_success,
- AppMsg.STYLE_INFO).show();
+ Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success,
+ Notify.Style.INFO);
// check if we need to send the key to the server or not
if (mUploadKeyCheckbox.isChecked()) {
@@ -327,8 +339,10 @@ public class CertifyKeyActivity extends ActionBarActivity implements
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- AppMsg.makeText(CertifyKeyActivity.this, R.string.key_send_success,
- AppMsg.STYLE_INFO).show();
+ Intent intent = new Intent();
+ intent.putExtra(OperationResultParcel.EXTRA_RESULT, message.getData());
+ Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success,
+ Notify.Style.INFO);
setResult(RESULT_OK);
finish();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
new file mode 100644
index 000000000..534ac5811
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBarActivity;
+
+import org.sufficientlysecure.keychain.R;
+
+public class CreateKeyActivity extends ActionBarActivity {
+
+ public static final String EXTRA_NAME = "name";
+ public static final String EXTRA_EMAIL = "email";
+
+ public static final int FRAG_ACTION_START = 0;
+ public static final int FRAG_ACTION_TO_RIGHT = 1;
+ public static final int FRAG_ACTION_TO_LEFT = 2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.create_key_activity);
+
+ // pass extras into fragment
+ CreateKeyInputFragment frag =
+ CreateKeyInputFragment.newInstance(
+ getIntent().getStringExtra(EXTRA_NAME),
+ getIntent().getStringExtra(EXTRA_EMAIL)
+ );
+ loadFragment(null, frag, FRAG_ACTION_START);
+ }
+
+ public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) {
+ // However, if we're being restored from a previous state,
+ // then we don't need to do anything and should return or else
+ // we could end up with overlapping fragments.
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ switch (action) {
+ case FRAG_ACTION_START:
+ transaction.setCustomAnimations(0, 0);
+ transaction.replace(R.id.create_key_fragment_container, fragment)
+ .commitAllowingStateLoss();
+ break;
+ case FRAG_ACTION_TO_LEFT:
+ getSupportFragmentManager().popBackStackImmediate();
+ break;
+ case FRAG_ACTION_TO_RIGHT:
+ transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left,
+ R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right);
+ transaction.addToBackStack(null);
+ transaction.replace(R.id.create_key_fragment_container, fragment)
+ .commitAllowingStateLoss();
+ break;
+
+ }
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
new file mode 100644
index 000000000..b64480087
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.util.Notify;
+
+public class CreateKeyFinalFragment extends Fragment {
+
+ CreateKeyActivity mCreateKeyActivity;
+
+ TextView mNameEdit;
+ TextView mEmailEdit;
+ CheckBox mUploadCheckbox;
+ View mBackButton;
+ View mCreateButton;
+
+ public static final String ARG_NAME = "name";
+ public static final String ARG_EMAIL = "email";
+ public static final String ARG_PASSPHRASE = "passphrase";
+
+ String mName;
+ String mEmail;
+ String mPassphrase;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateKeyFinalFragment newInstance(String name, String email, String passphrase) {
+ CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_NAME, name);
+ args.putString(ARG_EMAIL, email);
+ args.putString(ARG_PASSPHRASE, passphrase);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_key_final_fragment, container, false);
+
+ mNameEdit = (TextView) view.findViewById(R.id.name);
+ mEmailEdit = (TextView) view.findViewById(R.id.email);
+ mUploadCheckbox = (CheckBox) view.findViewById(R.id.create_key_upload);
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mCreateButton = view.findViewById(R.id.create_key_create_button);
+
+ // get args
+ mName = getArguments().getString(ARG_NAME);
+ mEmail = getArguments().getString(ARG_EMAIL);
+ mPassphrase = getArguments().getString(ARG_PASSPHRASE);
+
+ // set values
+ mNameEdit.setText(mName);
+ mEmailEdit.setText(mEmail);
+
+ mCreateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKey();
+ }
+ });
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCreateKeyActivity.loadFragment(null, null, CreateKeyActivity.FRAG_ACTION_TO_LEFT);
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void createKey() {
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+ intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
+
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ getActivity(),
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+ final OperationResults.EditKeyResult result =
+ returnData.getParcelable(OperationResultParcel.EXTRA_RESULT);
+ if (result == null) {
+ return;
+ }
+
+ if (mUploadCheckbox.isChecked()) {
+ if (result.getResult() == OperationResultParcel.RESULT_OK) {
+ // result will be displayed after upload
+ uploadKey(result);
+ } else {
+ // display result on error without finishing activity
+ result.createNotify(getActivity());
+ }
+ } else {
+ // TODO: return result
+ result.createNotify(getActivity());
+
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
+ }
+ }
+ }
+ };
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
+ String userId = mName + " <" + mEmail + ">";
+ parcel.mAddUserIds.add(userId);
+ parcel.mNewPassphrase = mPassphrase;
+
+ // get selected key entries
+ data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ saveHandler.showProgressDialog(getActivity());
+
+ getActivity().startService(intent);
+ }
+
+ private void uploadKey(final OperationResults.EditKeyResult editKeyResult) {
+ // Send all information needed to service to upload key in other thread
+ final Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_UPLOAD_KEYRING);
+
+ // set data uri as path to keyring
+ Uri blobUri = KeychainContract.KeyRingData.buildPublicKeyRingUri(
+ Long.toString(editKeyResult.mRingMasterKeyId)
+ );
+ intent.setData(blobUri);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ // upload to favorite keyserver
+ String keyserver = Preferences.getPreferences(getActivity()).getKeyServers()[0];
+ data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after uploading is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // TODO: return results
+
+ editKeyResult.createNotify(getActivity());
+
+ Notify.showNotify(getActivity(), R.string.key_send_success,
+ Notify.Style.INFO);
+
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
new file mode 100644
index 000000000..ef5a3b145
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Patterns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ContactHelper;
+
+import java.util.regex.Matcher;
+
+public class CreateKeyInputFragment extends Fragment {
+
+ CreateKeyActivity mCreateKeyActivity;
+
+ AutoCompleteTextView mNameEdit;
+ AutoCompleteTextView mEmailEdit;
+ EditText mPassphraseEdit;
+ EditText mPassphraseEditAgain;
+ View mCreateButton;
+
+ public static final String ARG_NAME = "name";
+ public static final String ARG_EMAIL = "email";
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateKeyInputFragment newInstance(String name, String email) {
+ CreateKeyInputFragment frag = new CreateKeyInputFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_NAME, name);
+ args.putString(ARG_EMAIL, email);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_key_input_fragment, container, false);
+
+ mNameEdit = (AutoCompleteTextView) view.findViewById(R.id.name);
+ mEmailEdit = (AutoCompleteTextView) view.findViewById(R.id.email);
+ mPassphraseEdit = (EditText) view.findViewById(R.id.passphrase);
+ mPassphraseEditAgain = (EditText) view.findViewById(R.id.passphrase_again);
+ mCreateButton = view.findViewById(R.id.create_key_button);
+
+ // initial values
+ String name = getArguments().getString(ARG_NAME);
+ String email = getArguments().getString(ARG_EMAIL);
+ mNameEdit.setText(name);
+ mEmailEdit.setText(email);
+
+ // focus non-empty edit fields
+ if (name != null && email != null) {
+ mPassphraseEdit.requestFocus();
+ } else if (name != null) {
+ mEmailEdit.requestFocus();
+ }
+
+ mEmailEdit.setThreshold(1); // Start working from first character
+ mEmailEdit.setAdapter(
+ new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_dropdown_item,
+ ContactHelper.getPossibleUserEmails(getActivity())
+ )
+ );
+ mEmailEdit.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ String email = editable.toString();
+ if (email.length() > 0) {
+ Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
+ if (emailMatcher.matches()) {
+ mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
+ R.drawable.uid_mail_ok, 0);
+ } else {
+ mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
+ R.drawable.uid_mail_bad, 0);
+ }
+ } else {
+ // remove drawable if email is empty
+ mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ }
+ }
+ });
+
+ mNameEdit.setThreshold(1); // Start working from first character
+ mNameEdit.setAdapter(
+ new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_dropdown_item,
+ ContactHelper.getPossibleUserNames(getActivity())
+ )
+ );
+
+ mCreateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKeyCheck();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void createKeyCheck() {
+ if (isEditTextNotEmpty(getActivity(), mNameEdit)
+ && isEditTextNotEmpty(getActivity(), mEmailEdit)
+ && isEditTextNotEmpty(getActivity(), mPassphraseEdit)
+ && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) {
+
+ CreateKeyFinalFragment frag =
+ CreateKeyFinalFragment.newInstance(
+ mNameEdit.getText().toString(),
+ mEmailEdit.getText().toString(),
+ mPassphraseEdit.getText().toString()
+ );
+
+ hideKeyboard();
+ mCreateKeyActivity.loadFragment(null, frag, CreateKeyActivity.FRAG_ACTION_TO_RIGHT);
+ }
+ }
+
+ private void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ //check if no view has focus:
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+
+ /**
+ * Checks if text of given EditText is not empty. If it is empty an error is
+ * set and the EditText gets the focus.
+ *
+ * @param context
+ * @param editText
+ * @return true if EditText is not empty
+ */
+ private static boolean isEditTextNotEmpty(Context context, EditText editText) {
+ boolean output = true;
+ if (editText.getText().toString().length() == 0) {
+ editText.setError(context.getString(R.string.create_key_empty));
+ editText.requestFocus();
+ output = false;
+ } else {
+ editText.setError(null);
+ }
+
+ return output;
+ }
+
+ private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) {
+ boolean output = true;
+ if (!editText1.getText().toString().equals(editText2.getText().toString())) {
+ editText2.setError(context.getString(R.string.create_key_passphrases_not_equal));
+ editText2.requestFocus();
+ output = false;
+ } else {
+ editText2.setError(null);
+ }
+
+ return output;
+ }
+
+}
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 dd05537ef..5b61c3f52 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -28,6 +28,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -107,7 +110,6 @@ public class DecryptFileFragment extends DecryptFragment {
private void decryptAction() {
if (mInputUri == null) {
- //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
return;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index d450bdcca..16a7b911e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -132,7 +132,7 @@ public abstract class DecryptFragment extends Fragment {
mResultText.setText(R.string.decrypt_result_decrypted_and_signature_certified);
}
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_green));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_green_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
mSignatureLayout.setVisibility(View.VISIBLE);
mLookupKey.setVisibility(View.GONE);
@@ -146,7 +146,7 @@ public abstract class DecryptFragment extends Fragment {
mResultText.setText(R.string.decrypt_result_decrypted_and_signature_uncertified);
}
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_orange));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
mSignatureLayout.setVisibility(View.VISIBLE);
mLookupKey.setVisibility(View.GONE);
@@ -160,7 +160,7 @@ public abstract class DecryptFragment extends Fragment {
mResultText.setText(R.string.decrypt_result_decrypted_unknown_pub_key);
}
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_orange));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mSignatureLayout.setVisibility(View.VISIBLE);
mLookupKey.setVisibility(View.VISIBLE);
@@ -170,7 +170,7 @@ public abstract class DecryptFragment extends Fragment {
case OpenPgpSignatureResult.SIGNATURE_ERROR: {
mResultText.setText(R.string.decrypt_result_invalid_signature);
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_red));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mSignatureLayout.setVisibility(View.GONE);
mLookupKey.setVisibility(View.GONE);
@@ -180,7 +180,7 @@ public abstract class DecryptFragment extends Fragment {
default: {
mResultText.setText(R.string.error);
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_red));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mSignatureLayout.setVisibility(View.GONE);
mLookupKey.setVisibility(View.GONE);
@@ -192,7 +192,7 @@ public abstract class DecryptFragment extends Fragment {
mLookupKey.setVisibility(View.GONE);
// successful decryption-only
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_purple));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_purple_light));
mResultText.setText(R.string.decrypt_result_decrypted);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
index 2db6c232c..cf7a0b4b8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
@@ -27,7 +27,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
-import com.devspark.appmsg.AppMsg;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.regex.Matcher;
@@ -105,12 +106,10 @@ public class DecryptMessageFragment extends DecryptFragment {
mCiphertext = matcher.group(1);
decryptStart(null);
} else {
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR);
}
} else {
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR);
}
}
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 d80425c3c..6ddaec17f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -33,7 +33,7 @@ public class EditKeyActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.edit_key_activity_new);
+ setContentView(R.layout.edit_key_activity);
Uri dataUri = getIntent().getData();
if (dataUri == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java
deleted file mode 100644
index 51457cd45..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.support.v4.app.ActivityCompat;
-import android.support.v7.app.ActionBarActivity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.devspark.appmsg.AppMsg;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.helper.ExportHelper;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
-import org.sufficientlysecure.keychain.ui.widget.Editor;
-import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
-import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
-import org.sufficientlysecure.keychain.ui.widget.SectionView;
-import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Vector;
-
-public class EditKeyActivityOld extends ActionBarActivity implements EditorListener {
-
- // Actions for internal use only:
- public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
- public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY";
-
- // possible extra keys
- public static final String EXTRA_USER_IDS = "user_ids";
- public static final String EXTRA_NO_PASSPHRASE = "no_passphrase";
- public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys";
-
- // EDIT
- private Uri mDataUri;
-
- private SectionView mUserIdsView;
- private SectionView mKeysView;
-
- private String mCurrentPassphrase = null;
- private String mNewPassphrase = null;
- private String mSavedNewPassphrase = null;
- private boolean mIsPassphraseSet;
- private boolean mNeedsSaving;
- private boolean mIsBrandNewKeyring = false;
-
- private Button mChangePassphrase;
-
- private CheckBox mNoPassphrase;
-
- Vector<String> mUserIds;
- Vector<UncachedSecretKey> mKeys;
- Vector<Integer> mKeysUsages;
- boolean mMasterCanSign = true;
-
- ExportHelper mExportHelper;
-
- public boolean needsSaving() {
- mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving();
- mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving();
- mNeedsSaving |= hasPassphraseChanged();
- mNeedsSaving |= mIsBrandNewKeyring;
- return mNeedsSaving;
- }
-
-
- public void somethingChanged() {
- ActivityCompat.invalidateOptionsMenu(this);
- }
-
- public void onDeleted(Editor e, boolean wasNewItem) {
- somethingChanged();
- }
-
- public void onEdited() {
- somethingChanged();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mExportHelper = new ExportHelper(this);
-
- // Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setTwoButtonView(getSupportActionBar(),
- R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Save
- saveClicked();
- }
- }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Cancel
- cancelClicked();
- }
- }
- );
-
- mUserIds = new Vector<String>();
- mKeys = new Vector<UncachedSecretKey>();
- mKeysUsages = new Vector<Integer>();
-
- // Catch Intents opened from other apps
- Intent intent = getIntent();
- String action = intent.getAction();
- if (ACTION_CREATE_KEY.equals(action)) {
- handleActionCreateKey(intent);
- } else if (ACTION_EDIT_KEY.equals(action)) {
- handleActionEditKey(intent);
- }
- }
-
- /**
- * Handle intent action to create new key
- *
- * @param intent
- */
- private void handleActionCreateKey(Intent intent) {
- Bundle extras = intent.getExtras();
-
- mCurrentPassphrase = "";
- mIsBrandNewKeyring = true;
-
- if (extras != null) {
- // if userId is given, prefill the fields
- if (extras.containsKey(EXTRA_USER_IDS)) {
- Log.d(Constants.TAG, "UserIds are given!");
- mUserIds.add(extras.getString(EXTRA_USER_IDS));
- }
-
- // if no passphrase is given
- if (extras.containsKey(EXTRA_NO_PASSPHRASE)) {
- boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE);
- if (noPassphrase) {
- // check "no passphrase" checkbox and remove button
- mNoPassphrase.setChecked(true);
- mChangePassphrase.setVisibility(View.GONE);
- }
- }
-
- // generate key
- if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) {
- /*
- boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS);
- if (generateDefaultKeys) {
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE,
- mCurrentPassphrase);
-
- serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after generating is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
- this, getResources().getQuantityString(R.plurals.progress_generating, 1),
- ProgressDialog.STYLE_HORIZONTAL, true,
-
- new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- // Stop key generation on cancel
- stopService(serviceIntent);
- EditKeyActivity.this.setResult(Activity.RESULT_CANCELED);
- EditKeyActivity.this.finish();
- }
- }) {
-
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get new key from data bundle returned from service
- Bundle data = message.getDataAsStringList();
-
- ArrayList<UncachedSecretKey> newKeys =
- PgpConversionHelper.BytesToPGPSecretKeyList(data
- .getByteArray(KeychainIntentService.RESULT_NEW_KEY));
-
- ArrayList<Integer> keyUsageFlags = data.getIntegerArrayList(
- KeychainIntentService.RESULT_KEY_USAGES);
-
- if (newKeys.size() == keyUsageFlags.size()) {
- for (int i = 0; i < newKeys.size(); ++i) {
- mKeys.add(newKeys.get(i));
- mKeysUsages.add(keyUsageFlags.get(i));
- }
- }
-
- buildLayout(true);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- serviceIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(serviceIntent);
- }
- */
- }
- } else {
- buildLayout(false);
- }
- }
-
- /**
- * Handle intent action to edit existing key
- *
- * @param intent
- */
- private void handleActionEditKey(Intent intent) {
- mDataUri = intent.getData();
- if (mDataUri == null) {
- Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
- finish();
- } else {
- Log.d(Constants.TAG, "uri: " + mDataUri);
-
- try {
- Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
- WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri);
-
- mMasterCanSign = keyRing.getSubKey().canCertify();
- for (WrappedSecretKey key : keyRing.secretKeyIterator()) {
- // Turn into uncached instance
- mKeys.add(key.getUncached());
- mKeysUsages.add(key.getKeyUsage()); // get usage when view is created
- }
-
- boolean isSet = false;
- for (String userId : keyRing.getSubKey().getUserIds()) {
- Log.d(Constants.TAG, "Added userId " + userId);
- if (!isSet) {
- isSet = true;
- String[] parts = KeyRing.splitUserId(userId);
- if (parts[0] != null) {
- setTitle(parts[0]);
- }
- }
- mUserIds.add(userId);
- }
-
- buildLayout(false);
-
- mCurrentPassphrase = "";
- mIsPassphraseSet = keyRing.hasPassphrase();
- if (!mIsPassphraseSet) {
- // check "no passphrase" checkbox and remove button
- mNoPassphrase.setChecked(true);
- mChangePassphrase.setVisibility(View.GONE);
- }
-
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "Keyring not found: " + e.getMessage(), e);
- Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_SHORT).show();
- finish();
- }
-
- }
- }
-
- /**
- * Shows the dialog to set a new passphrase
- */
- private void showSetPassphraseDialog() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
-
- // set new returned passphrase!
- mNewPassphrase = data
- .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
-
- updatePassphraseButtonText();
- somethingChanged();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- // set title based on isPassphraseSet()
- int title;
- if (isPassphraseSet()) {
- title = R.string.title_change_passphrase;
- } else {
- title = R.string.title_set_passphrase;
- }
-
- SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance(
- messenger, null, title);
-
- setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog");
- }
-
- /**
- * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user
- * id and key.
- *
- * @param newKeys
- */
- private void buildLayout(boolean newKeys) {
- setContentView(R.layout.edit_key_activity);
-
- // find views
- mChangePassphrase = (Button) findViewById(R.id.edit_key_btn_change_passphrase);
- mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
- // Build layout based on given userIds and keys
-
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container);
- if (mIsPassphraseSet) {
- mChangePassphrase.setText(getString(R.string.btn_change_passphrase));
- }
- mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- mUserIdsView.setType(SectionView.TYPE_USER_ID);
- mUserIdsView.setCanBeEdited(mMasterCanSign);
- mUserIdsView.setUserIds(mUserIds);
- mUserIdsView.setEditorListener(this);
- container.addView(mUserIdsView);
- mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- mKeysView.setType(SectionView.TYPE_KEY);
- mKeysView.setCanBeEdited(mMasterCanSign);
- mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
- mKeysView.setEditorListener(this);
- container.addView(mKeysView);
-
- updatePassphraseButtonText();
-
- mChangePassphrase.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- showSetPassphraseDialog();
- }
- });
-
- // disable passphrase when no passphrase checkbox is checked!
- mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- // remove passphrase
- mSavedNewPassphrase = mNewPassphrase;
- mNewPassphrase = "";
- mChangePassphrase.setVisibility(View.GONE);
- } else {
- mNewPassphrase = mSavedNewPassphrase;
- mChangePassphrase.setVisibility(View.VISIBLE);
- }
- somethingChanged();
- }
- });
- }
-
- private long getMasterKeyId() {
- if (mKeysView.getEditors().getChildCount() == 0) {
- return 0;
- }
- return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId();
- }
-
- public boolean isPassphraseSet() {
- if (mNoPassphrase.isChecked()) {
- return true;
- } else if ((mIsPassphraseSet)
- || (mNewPassphrase != null && !mNewPassphrase.equals(""))) {
- return true;
- } else {
- return false;
- }
- }
-
- public boolean hasPassphraseChanged() {
- if (mNoPassphrase != null) {
- if (mNoPassphrase.isChecked()) {
- return mIsPassphraseSet;
- } else {
- return (mNewPassphrase != null && !mNewPassphrase.equals(""));
- }
- } else {
- return false;
- }
- }
-
- private void saveClicked() {
- final long masterKeyId = getMasterKeyId();
- if (needsSaving()) { //make sure, as some versions don't support invalidateOptionsMenu
- try {
- if (!isPassphraseSet()) {
- throw new PgpGeneralException(this.getString(R.string.set_a_passphrase));
- }
-
- String passphrase;
- if (mIsPassphraseSet) {
- passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
- } else {
- passphrase = "";
- }
- if (passphrase == null) {
- PassphraseDialogFragment.show(this, masterKeyId,
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(
- EditKeyActivityOld.this, masterKeyId);
- checkEmptyIDsWanted();
- }
- }
- });
- } else {
- mCurrentPassphrase = passphrase;
- checkEmptyIDsWanted();
- }
- } catch (PgpGeneralException e) {
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- } else {
- AppMsg.makeText(this, R.string.error_change_something_first, AppMsg.STYLE_ALERT).show();
- }
- }
-
- private void checkEmptyIDsWanted() {
- try {
- ArrayList<String> userIDs = getUserIds(mUserIdsView);
- List<Boolean> newIDs = mUserIdsView.getNewIDFlags();
- ArrayList<String> originalIDs = mUserIdsView.getOriginalIDs();
- int curID = 0;
- for (String userID : userIDs) {
- if (userID.equals("") && (!userID.equals(originalIDs.get(curID)) || newIDs.get(curID))) {
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
- EditKeyActivityOld.this);
-
- alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
- alert.setTitle(R.string.warning);
- alert.setMessage(EditKeyActivityOld.this.getString(R.string.ask_empty_id_ok));
-
- alert.setPositiveButton(EditKeyActivityOld.this.getString(android.R.string.yes),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- finallySaveClicked();
- }
- }
- );
- alert.setNegativeButton(this.getString(android.R.string.no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- }
- }
- );
- alert.setCancelable(false);
- alert.show();
- return;
- }
- curID++;
- }
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show();
- }
- finallySaveClicked();
- }
-
- private boolean[] toPrimitiveArray(final List<Boolean> booleanList) {
- final boolean[] primitives = new boolean[booleanList.size()];
- int index = 0;
- for (Boolean object : booleanList) {
- primitives[index++] = object;
- }
- return primitives;
- }
-
- private void finallySaveClicked() {
- /*
- try {
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
-
- OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel();
- saveParams.userIds = getUserIds(mUserIdsView);
- saveParams.originalIDs = mUserIdsView.getOriginalIDs();
- saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
- saveParams.newIDs = toPrimitiveArray(mUserIdsView.getNewIDFlags());
- saveParams.primaryIDChanged = mUserIdsView.primaryChanged();
- saveParams.moddedKeys = toPrimitiveArray(mKeysView.getNeedsSavingArray());
- saveParams.deletedKeys = mKeysView.getDeletedKeys();
- saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView);
- saveParams.keysUsages = getKeysUsages(mKeysView);
- saveParams.newPassphrase = mNewPassphrase;
- saveParams.oldPassphrase = mCurrentPassphrase;
- saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray());
- saveParams.keys = getKeys(mKeysView);
- saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
- data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after saving is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Intent data = new Intent();
-
- // return uri pointing to new created key
- Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId());
- data.setData(uri);
-
- setResult(RESULT_OK, data);
- finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- */
- }
-
- private void cancelClicked() {
- if (needsSaving()) { //ask if we want to save
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
- EditKeyActivityOld.this);
-
- alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
- alert.setTitle(R.string.warning);
- alert.setMessage(EditKeyActivityOld.this.getString(R.string.ask_save_changed_key));
-
- alert.setPositiveButton(EditKeyActivityOld.this.getString(android.R.string.yes),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- saveClicked();
- }
- });
- alert.setNegativeButton(this.getString(android.R.string.no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- setResult(RESULT_CANCELED);
- finish();
- }
- });
- alert.setCancelable(false);
- alert.show();
- } else {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
-
- /**
- * Returns user ids from the SectionView
- *
- * @param userIdsView
- * @return
- */
- private ArrayList<String> getUserIds(SectionView userIdsView) throws PgpGeneralException {
- ArrayList<String> userIds = new ArrayList<String>();
-
- ViewGroup userIdEditors = userIdsView.getEditors();
-
- boolean gotMainUserId = false;
- for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
- String userId;
- userId = editor.getValue();
-
- if (editor.isMainUserId()) {
- userIds.add(0, userId);
- gotMainUserId = true;
- } else {
- userIds.add(userId);
- }
- }
-
- if (userIds.size() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_a_user_id));
- }
-
- if (!gotMainUserId) {
- throw new PgpGeneralException(getString(R.string.error_main_user_id_must_not_be_empty));
- }
-
- return userIds;
- }
-
- /**
- * Returns keys from the SectionView
- *
- * @param keysView
- * @return
- */
- private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
- ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
-
- ViewGroup keyEditors = keysView.getEditors();
-
- if (keyEditors.getChildCount() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_master_key));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keys.add(editor.getValue());
- }
-
- return keys;
- }
-
- /**
- * Returns usage selections of keys from the SectionView
- *
- * @param keysView
- * @return
- */
- private ArrayList<Integer> getKeysUsages(SectionView keysView) throws PgpGeneralException {
- ArrayList<Integer> keysUsages = new ArrayList<Integer>();
-
- ViewGroup keyEditors = keysView.getEditors();
-
- if (keyEditors.getChildCount() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_master_key));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keysUsages.add(editor.getUsage());
- }
-
- return keysUsages;
- }
-
- private ArrayList<Calendar> getKeysExpiryDates(SectionView keysView) throws PgpGeneralException {
- ArrayList<Calendar> keysExpiryDates = new ArrayList<Calendar>();
-
- ViewGroup keyEditors = keysView.getEditors();
-
- if (keyEditors.getChildCount() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_master_key));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keysExpiryDates.add(editor.getExpiryDate());
- }
-
- return keysExpiryDates;
- }
-
- private void updatePassphraseButtonText() {
- mChangePassphrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
- : getString(R.string.btn_set_passphrase));
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index c76dc0164..9083d1567 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -42,24 +42,28 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResults;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.ChangeExpiryDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
+import java.util.Date;
public class EditKeyFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
@@ -164,8 +168,8 @@ public class EditKeyFragment extends LoaderFragment implements
try {
Uri secretUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
- WrappedSecretKeyRing keyRing =
- new ProviderHelper(getActivity()).getWrappedSecretKeyRing(secretUri);
+ CanonicalizedSecretKeyRing keyRing =
+ new ProviderHelper(getActivity()).getCanonicalizedSecretKeyRing(secretUri);
mSaveKeyringParcel = new SaveKeyringParcel(keyRing.getMasterKeyId(),
keyRing.getUncachedKeyRing().getFingerprint());
@@ -214,10 +218,18 @@ public class EditKeyFragment extends LoaderFragment implements
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mUserIdsAddedData);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
- mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
+ mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mSubkeysList.setAdapter(mSubkeysAdapter);
- mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.addSubKeys);
+ mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ long keyId = mSubkeysAdapter.getKeyId(position);
+ editSubkey(keyId);
+ }
+ });
+
+ mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
// Prepare the loaders. Either re-connect with an existing ones,
@@ -287,7 +299,7 @@ public class EditKeyFragment extends LoaderFragment implements
Bundle data = message.getData();
// cache new returned passphrase!
- mSaveKeyringParcel.newPassphrase = data
+ mSaveKeyringParcel.mNewPassphrase = data
.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
}
}
@@ -309,19 +321,19 @@ public class EditKeyFragment extends LoaderFragment implements
switch (message.what) {
case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID:
// toggle
- if (mSaveKeyringParcel.changePrimaryUserId != null
- && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) {
- mSaveKeyringParcel.changePrimaryUserId = null;
+ if (mSaveKeyringParcel.mChangePrimaryUserId != null
+ && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) {
+ mSaveKeyringParcel.mChangePrimaryUserId = null;
} else {
- mSaveKeyringParcel.changePrimaryUserId = userId;
+ mSaveKeyringParcel.mChangePrimaryUserId = userId;
}
break;
case EditUserIdDialogFragment.MESSAGE_REVOKE:
// toggle
- if (mSaveKeyringParcel.revokeUserIds.contains(userId)) {
- mSaveKeyringParcel.revokeUserIds.remove(userId);
+ if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) {
+ mSaveKeyringParcel.mRevokeUserIds.remove(userId);
} else {
- mSaveKeyringParcel.revokeUserIds.add(userId);
+ mSaveKeyringParcel.mRevokeUserIds.add(userId);
}
break;
}
@@ -342,6 +354,72 @@ public class EditKeyFragment extends LoaderFragment implements
});
}
+ private void editSubkey(final long keyId) {
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case EditSubkeyDialogFragment.MESSAGE_CHANGE_EXPIRY:
+ editSubkeyExpiry(keyId);
+ break;
+ case EditSubkeyDialogFragment.MESSAGE_REVOKE:
+ // toggle
+ if (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) {
+ mSaveKeyringParcel.mRevokeSubKeys.remove(keyId);
+ } else {
+ mSaveKeyringParcel.mRevokeSubKeys.add(keyId);
+ }
+ break;
+ }
+ getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ final Messenger messenger = new Messenger(returnHandler);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+ EditSubkeyDialogFragment dialogFragment =
+ EditSubkeyDialogFragment.newInstance(messenger);
+
+ dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyDialog");
+ }
+ });
+ }
+
+ private void editSubkeyExpiry(final long keyId) {
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case ChangeExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE:
+ // toggle
+// if (mSaveKeyringParcel.changePrimaryUserId != null
+// && mSaveKeyringParcel.changePrimaryUserId.equals(userId)) {
+// mSaveKeyringParcel.changePrimaryUserId = null;
+// } else {
+// mSaveKeyringParcel.changePrimaryUserId = userId;
+// }
+ break;
+ }
+ getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ final Messenger messenger = new Messenger(returnHandler);
+
+ DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
+ public void run() {
+ ChangeExpiryDialogFragment dialogFragment =
+ ChangeExpiryDialogFragment.newInstance(messenger, new Date(), new Date());
+
+ dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog");
+ }
+ });
+ }
+
private void addUserId() {
mUserIdsAddedAdapter.add(new UserIdsAddedAdapter.UserIdModel());
}
@@ -373,10 +451,11 @@ public class EditKeyFragment extends LoaderFragment implements
}
private void save(String passphrase) {
- Log.d(Constants.TAG, "add userids to parcel: " + mUserIdsAddedAdapter.getDataAsStringList());
- Log.d(Constants.TAG, "mSaveKeyringParcel.newPassphrase: " + mSaveKeyringParcel.newPassphrase);
+ mSaveKeyringParcel.mAddUserIds = mUserIdsAddedAdapter.getDataAsStringList();
- mSaveKeyringParcel.addUserIds = mUserIdsAddedAdapter.getDataAsStringList();
+ Log.d(Constants.TAG, "mSaveKeyringParcel.mAddUserIds: " + mSaveKeyringParcel.mAddUserIds);
+ Log.d(Constants.TAG, "mSaveKeyringParcel.mNewPassphrase: " + mSaveKeyringParcel.mNewPassphrase);
+ Log.d(Constants.TAG, "mSaveKeyringParcel.mRevokeUserIds: " + mSaveKeyringParcel.mRevokeUserIds);
// Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
@@ -388,26 +467,30 @@ public class EditKeyFragment extends LoaderFragment implements
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- getActivity().finish();
- // TODO below
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
- final OperationResults.SaveKeyringResult result =
- returnData.getParcelable(KeychainIntentService.RESULT);
+ final OperationResults.EditKeyResult result =
+ returnData.getParcelable(EditKeyResult.EXTRA_RESULT);
if (result == null) {
return;
}
- // if good -> finish, return result to showkey and display there!
// if bad -> display here!
+ if (!result.success()) {
+ result.createNotify(getActivity()).show();
+ return;
+ }
-// result.displayNotify(ImportKeysActivity.this);
+ // if good -> finish, return result to showkey and display there!
+ Intent intent = new Intent();
+ intent.putExtra(EditKeyResult.EXTRA_RESULT, result);
+ getActivity().setResult(EditKeyActivity.RESULT_OK, intent);
+ getActivity().finish();
-// getActivity().finish();
}
}
};
@@ -432,4 +515,4 @@ public class EditKeyFragment extends LoaderFragment implements
// start service with intent
getActivity().startService(intent);
}
-} \ No newline at end of file
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
new file mode 100644
index 000000000..5f3f170a1
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.View;
+import android.view.Window;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class FirstTimeActivity extends ActionBarActivity {
+
+ View mCreateKey;
+ View mImportKey;
+ View mSkipSetup;
+
+ public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ setContentView(R.layout.first_time_activity);
+
+ mCreateKey = findViewById(R.id.first_time_create_key);
+ mImportKey = findViewById(R.id.first_time_import_key);
+ mSkipSetup = findViewById(R.id.first_time_cancel);
+
+ mSkipSetup.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finishSetup();
+ }
+ });
+
+ mImportKey.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(FirstTimeActivity.this, ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
+ }
+ });
+
+ mCreateKey.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(FirstTimeActivity.this, CreateKeyActivity.class);
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
+ }
+ });
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) {
+ if (resultCode == RESULT_OK) {
+ finishSetup();
+ }
+ } else {
+ Log.e(Constants.TAG, "No valid request code!");
+ }
+ }
+
+ private void finishSetup() {
+ Preferences prefs = Preferences.getPreferences(this);
+ prefs.setFirstTime(false);
+ Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class);
+ startActivity(intent);
+ finish();
+ }
+}
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 3ff3b56bf..524fe2ef2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -40,17 +40,19 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.FileImportCache;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
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.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
@@ -354,9 +356,8 @@ public class ImportKeysActivity extends ActionBarActivity {
ImportKeysServerFragment f = (ImportKeysServerFragment)
getActiveFragment(mViewPager, TAB_KEYSERVER);
- // TODO: Currently it simply uses keyserver nr 0
- String keyserver = Preferences.getPreferences(ImportKeysActivity.this)
- .getKeyServers()[0];
+ // ask favorite keyserver
+ String keyserver = Preferences.getPreferences(ImportKeysActivity.this).getKeyServers()[0];
// set fields of ImportKeysServerFragment
f.setQueryAndKeyserver(query, keyserver);
@@ -427,15 +428,15 @@ public class ImportKeysActivity extends ActionBarActivity {
if (returnData == null) {
return;
}
- final ImportResult result =
- returnData.getParcelable(KeychainIntentService.RESULT);
+ final ImportKeyResult result =
+ returnData.getParcelable(KeychainIntentService.RESULT_IMPORT);
if (result == null) {
return;
}
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())) {
Intent intent = new Intent();
- intent.putExtra(EXTRA_RESULT, result);
+ intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
ImportKeysActivity.this.setResult(RESULT_OK, intent);
ImportKeysActivity.this.finish();
return;
@@ -451,7 +452,7 @@ public class ImportKeysActivity extends ActionBarActivity {
return;
}
- result.displayNotify(ImportKeysActivity.this);
+ result.createNotify(ImportKeysActivity.this).show();
}
}
};
@@ -470,19 +471,29 @@ public class ImportKeysActivity extends ActionBarActivity {
// get DATA from selected key entries
ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ // instead of given the entries by Intent extra, cache them into a file
+ // to prevent Java Binder problems on heavy imports
+ // read FileImportCache for more info.
+ try {
+ FileImportCache cache = new FileImportCache(this);
+ cache.writeCache(selectedEntries);
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // show progress dialog
- saveHandler.showProgressDialog(this);
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
- // start service with intent
- startService(intent);
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Problem writing cache file", e);
+ Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
+ }
} else if (ls instanceof ImportKeysListFragment.KeyserverLoaderState) {
ImportKeysListFragment.KeyserverLoaderState sls = (ImportKeysListFragment.KeyserverLoaderState) ls;
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 9a39b6cc3..fde0f5f23 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -35,7 +35,6 @@ import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListKeybaseLoader;
@@ -288,13 +287,13 @@ public class ImportKeysListFragment extends ListFragment implements
if (error == null) {
// No error
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
- } else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
- Notify.showNotify(getActivity(), R.string.error_import_file_no_content, Notify.Style.ERROR);
- } else if (error instanceof ImportKeysListLoader.NonPgpPart) {
+ } else if (error instanceof ImportKeysListLoader.NoValidKeysException) {
+ Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR);
+ } else if (error instanceof ImportKeysListLoader.NonPgpPartException) {
Notify.showNotify(getActivity(),
- ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources().
+ ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources().
getQuantityString(R.plurals.error_import_non_pgp_part,
- ((ImportKeysListLoader.NonPgpPart) error).getCount()),
+ ((ImportKeysListLoader.NonPgpPartException) error).getCount()),
Notify.Style.OK
);
} else {
@@ -308,9 +307,11 @@ public class ImportKeysListFragment extends ListFragment implements
if (error == null) {
// No error
} else if (error instanceof Keyserver.QueryTooShortException) {
- Notify.showNotify(getActivity(), R.string.error_keyserver_insufficient_query, Notify.Style.ERROR);
+ Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR);
} else if (error instanceof Keyserver.TooManyResponsesException) {
- Notify.showNotify(getActivity(), R.string.error_keyserver_too_many_responses, Notify.Style.ERROR);
+ Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR);
+ } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
+ Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR);
} else if (error instanceof Keyserver.QueryFailedException) {
Log.d(Constants.TAG,
"Unrecoverable keyserver query error: " + error.getLocalizedMessage());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 849576284..c74f4c96c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -17,33 +17,21 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.view.Menu;
import android.view.MenuItem;
-import com.devspark.appmsg.AppMsg;
-
-import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Constants.choice.algorithm;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
-import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.io.IOException;
-import java.util.ArrayList;
public class KeyListActivity extends DrawerActivity {
@@ -53,6 +41,14 @@ public class KeyListActivity extends DrawerActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // if this is the first time show first time activity
+ Preferences prefs = Preferences.getPreferences(this);
+ if (prefs.isFirstTime()) {
+ startActivity(new Intent(this, FirstTimeActivity.class));
+ finish();
+ return;
+ }
+
mExportHelper = new ExportHelper(this);
setContentView(R.layout.key_list_activity);
@@ -66,9 +62,10 @@ public class KeyListActivity extends DrawerActivity {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.key_list, menu);
- if(Constants.DEBUG) {
+ if (Constants.DEBUG) {
menu.findItem(R.id.menu_key_list_debug_read).setVisible(true);
menu.findItem(R.id.menu_key_list_debug_write).setVisible(true);
+ menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true);
}
return true;
@@ -85,10 +82,6 @@ public class KeyListActivity extends DrawerActivity {
createKey();
return true;
- case R.id.menu_key_list_create_expert:
- createKeyExpert();
- return true;
-
case R.id.menu_key_list_export:
mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
return true;
@@ -96,24 +89,32 @@ public class KeyListActivity extends DrawerActivity {
case R.id.menu_key_list_debug_read:
try {
KeychainDatabase.debugRead(this);
- AppMsg.makeText(this, "Restored from backup", AppMsg.STYLE_CONFIRM).show();
+ Notify.showNotify(this, "Restored Notify.Style backup", Notify.Style.INFO);
getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
- } catch(IOException e) {
+ } catch (IOException e) {
Log.e(Constants.TAG, "IO Error", e);
- AppMsg.makeText(this, "IO Error: " + e.getMessage(), AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
}
return true;
case R.id.menu_key_list_debug_write:
try {
KeychainDatabase.debugWrite(this);
- AppMsg.makeText(this, "Backup successful", AppMsg.STYLE_CONFIRM).show();
+ Notify.showNotify(this, "Backup Notify.Style", Notify.Style.INFO);
} catch(IOException e) {
Log.e(Constants.TAG, "IO Error", e);
- AppMsg.makeText(this, "IO Error: " + e.getMessage(), AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
}
return true;
+ case R.id.menu_key_list_debug_first_time:
+ Preferences prefs = Preferences.getPreferences(this);
+ prefs.setFirstTime(true);
+ Intent intent = new Intent(this, FirstTimeActivity.class);
+ startActivity(intent);
+ finish();
+ return true;
+
default:
return super.onOptionsItemSelected(item);
}
@@ -125,50 +126,8 @@ public class KeyListActivity extends DrawerActivity {
}
private void createKey() {
- Intent intent = new Intent(this, WizardActivity.class);
-// intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
-// intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
-// intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
- startActivityForResult(intent, 0);
+ Intent intent = new Intent(this, CreateKeyActivity.class);
+ startActivity(intent);
}
- private void createKeyExpert() {
- Intent intent = new Intent(this, KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
-
- // Message is received after importing is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
- Bundle data = message.getData();
- // OtherHelper.logDebugBundle(data, "message reply");
- }
- };
-
- // fill values for this action
- Bundle data = new Bundle();
-
- SaveKeyringParcel parcel = new SaveKeyringParcel();
- parcel.addSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
- parcel.addSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
- parcel.addUserIds.add("swagerinho");
- parcel.newPassphrase = "swag";
-
- // get selected key entries
- data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(this);
-
- startService(intent);
- }
-} \ No newline at end of file
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index e2ca50a4c..b8efa6d86 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -46,14 +47,11 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Button;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
@@ -62,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.util.Highlighter;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
import java.util.HashMap;
@@ -86,6 +85,7 @@ public class KeyListFragment extends LoaderFragment
private Button mButtonEmptyCreate;
private Button mButtonEmptyImport;
+ public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
/**
* Load custom layout with StickyListView from library
@@ -104,11 +104,8 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onClick(View v) {
- Intent intent = new Intent(getActivity(), EditKeyActivityOld.class);
- intent.setAction(EditKeyActivityOld.ACTION_CREATE_KEY);
- intent.putExtra(EditKeyActivityOld.EXTRA_GENERATE_DEFAULT_KEYS, true);
- intent.putExtra(EditKeyActivityOld.EXTRA_USER_IDS, ""); // show user id view
- startActivityForResult(intent, 0);
+ Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import);
@@ -117,8 +114,8 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivityForResult(intent, 0);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
@@ -339,8 +336,8 @@ public class KeyListFragment extends LoaderFragment
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys
if(hasSecret && masterKeyIds.length > 1) {
- AppMsg.makeText(getActivity(), R.string.secret_cannot_multiple,
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.secret_cannot_multiple,
+ Notify.Style.ERROR);
return;
}
@@ -437,9 +434,7 @@ public class KeyListFragment extends LoaderFragment
private class ItemViewHolder {
TextView mMainUserId;
TextView mMainUserIdRest;
- FrameLayout mStatusLayout;
- TextView mRevoked;
- ImageView mVerified;
+ ImageView mStatus;
}
@Override
@@ -448,9 +443,7 @@ public class KeyListFragment extends LoaderFragment
ItemViewHolder holder = new ItemViewHolder();
holder.mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
- holder.mRevoked = (TextView) view.findViewById(R.id.revoked);
- holder.mVerified = (ImageView) view.findViewById(R.id.verified);
+ holder.mStatus = (ImageView) view.findViewById(R.id.status_image);
view.setTag(holder);
return view;
}
@@ -486,25 +479,36 @@ public class KeyListFragment extends LoaderFragment
if (cursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) {
// this is a secret key
- h.mStatusLayout.setVisibility(View.VISIBLE);
- h.mRevoked.setVisibility(View.GONE);
- h.mVerified.setVisibility(View.GONE);
+ h.mStatus.setVisibility(View.GONE);
} else {
- // this is a public key - show if it's revoked
+ // this is a public key - show if it's revoked, expired, or verified
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
boolean isExpired = !cursor.isNull(INDEX_EXPIRY)
&& new Date(cursor.getLong(INDEX_EXPIRY)*1000).before(new Date());
- if(isRevoked || isExpired) {
- h.mStatusLayout.setVisibility(View.VISIBLE);
- h.mRevoked.setVisibility(View.VISIBLE);
- h.mVerified.setVisibility(View.GONE);
- h.mRevoked.setText(isRevoked ? R.string.revoked : R.string.expired);
+ boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
+
+ // Note: order is important!
+ if (isRevoked) {
+ h.mStatus.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
+ h.mStatus.setColorFilter(getResources().getColor(R.color.android_red_light),
+ PorterDuff.Mode.SRC_ATOP);
+ h.mStatus.setVisibility(View.VISIBLE);
+ } else if (isExpired) {
+ h.mStatus.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_expired_cutout));
+ h.mStatus.setColorFilter(getResources().getColor(R.color.android_orange_light),
+ PorterDuff.Mode.SRC_ATOP);
+ h.mStatus.setVisibility(View.VISIBLE);
+ } else if (isVerified) {
+ h.mStatus.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_verified_cutout));
+ h.mStatus.setColorFilter(getResources().getColor(R.color.android_green_light),
+ PorterDuff.Mode.SRC_ATOP);
+ h.mStatus.setVisibility(View.VISIBLE);
} else {
- boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
- h.mStatusLayout.setVisibility(isVerified ? View.VISIBLE : View.GONE);
- h.mRevoked.setVisibility(View.GONE);
- h.mVerified.setVisibility(isVerified ? View.VISIBLE : View.GONE);
+ h.mStatus.setVisibility(View.GONE);
}
}
}
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 67317de6e..43de6774b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
public class LogDisplayFragment extends ListFragment implements OnTouchListener {
@@ -135,7 +136,7 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
private LayoutInflater mInflater;
private int dipFactor;
- public LogAdapter(Context context, ArrayList<LogEntryParcel> log, LogLevel level) {
+ public LogAdapter(Context context, OperationResultParcel.OperationLog log, LogLevel level) {
super(context, R.layout.log_display_item);
mInflater = LayoutInflater.from(getContext());
dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
@@ -177,9 +178,11 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
if (entry.mParameters != null && entry.mParameters.length > 0
&& entry.mParameters[0] instanceof Integer) {
ih.mText.setText(getResources().getQuantityString(entry.mType.getMsgId(),
- (Integer) entry.mParameters[0], entry.mParameters));
+ (Integer) entry.mParameters[0],
+ entry.mParameters));
} else {
- ih.mText.setText(getResources().getString(entry.mType.getMsgId(), entry.mParameters));
+ ih.mText.setText(getResources().getString(entry.mType.getMsgId(),
+ entry.mParameters));
}
ih.mText.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index 448d29156..dcacdbc9d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -121,6 +121,9 @@ public class PreferencesActivity extends PreferenceActivity {
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
+ initializeConcealPgpApplication(
+ (CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));
+
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Load the legacy preferences headers
addPreferencesFromResource(R.xml.preference_headers_legacy);
@@ -264,6 +267,9 @@ public class PreferencesActivity extends PreferenceActivity {
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
+
+ initializeConcealPgpApplication(
+ (CheckBoxPreference) findPreference(Constants.Pref.CONCEAL_PGP_APPLICATION));
}
}
@@ -396,4 +402,15 @@ public class PreferencesActivity extends PreferenceActivity {
}
});
}
+
+ private static void initializeConcealPgpApplication(final CheckBoxPreference mConcealPgpApplication) {
+ mConcealPgpApplication.setChecked(sPreferences.getConcealPgpApplication());
+ mConcealPgpApplication.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mConcealPgpApplication.setChecked((Boolean) newValue);
+ sPreferences.setConcealPgpApplication((Boolean) newValue);
+ return false;
+ }
+ });
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
new file mode 100644
index 000000000..a906dfa97
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.View;
+import android.widget.ImageView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ActionBarHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
+import org.sufficientlysecure.keychain.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.QrCodeUtils;
+
+public class QrCodeActivity extends ActionBarActivity {
+
+ private ImageView mFingerprintQrCode;
+
+ private static final int QR_CODE_SIZE = 1000;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Inflate a "Done" custom action bar
+ ActionBarHelper.setOneButtonView(getSupportActionBar(),
+ R.string.btn_okay, R.drawable.ic_action_done,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // "Done"
+ finish();
+ }
+ }
+ );
+
+ setContentView(R.layout.qr_code_activity);
+
+ Uri dataUri = getIntent().getData();
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ finish();
+ return;
+ }
+
+ mFingerprintQrCode = (ImageView) findViewById(R.id.qr_code_image);
+
+ mFingerprintQrCode.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ try {
+ byte[] blob = (byte[]) providerHelper.getGenericData(
+ KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if (blob == null) {
+ Log.e(Constants.TAG, "key not found!");
+ Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
+ finish();
+ }
+
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
+ String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ mFingerprintQrCode.setImageBitmap(QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE));
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // custom activity transition to get zoom in effect
+ this.overridePendingTransition(R.anim.qr_code_zoom_enter, android.R.anim.fade_out);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // custom activity transition to get zoom out effect
+ this.overridePendingTransition(0, R.anim.qr_code_zoom_exit);
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index c7fffe263..5201b5df8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -35,7 +35,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
@@ -143,16 +143,16 @@ public class ViewCertActivity extends ActionBarActivity
try {
ProviderHelper providerHelper = new ProviderHelper(this);
- WrappedPublicKeyRing signeeRing =
- providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
- WrappedPublicKeyRing signerRing =
- providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
+ CanonicalizedPublicKeyRing signeeRing =
+ providerHelper.getCanonicalizedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
+ CanonicalizedPublicKeyRing signerRing =
+ providerHelper.getCanonicalizedPublicKeyRing(sig.getKeyId());
try {
- sig.init(signerRing.getSubkey());
- if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) {
+ sig.init(signerRing.getPublicKey());
+ if (sig.verifySignature(signeeRing.getPublicKey(), signeeUid)) {
mStatus.setText(R.string.cert_verify_ok);
- mStatus.setTextColor(getResources().getColor(R.color.result_green));
+ mStatus.setTextColor(getResources().getColor(R.color.android_green_light));
} else {
mStatus.setText(R.string.cert_verify_failed);
mStatus.setTextColor(getResources().getColor(R.color.alert));
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 22a23e6a5..69efb4cbd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -19,9 +19,9 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -43,8 +43,9 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
-
-import com.devspark.appmsg.AppMsg;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -54,11 +55,12 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.OperationResults.ImportResult;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
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;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
import java.util.HashMap;
@@ -81,9 +83,11 @@ public class ViewKeyActivity extends ActionBarActivity implements
private ViewPager mViewPager;
private SlidingTabLayout mSlidingTabLayout;
private PagerTabStripAdapter mTabsAdapter;
+
+ private LinearLayout mStatusLayout;
+ private TextView mStatusText;
+ private ImageView mStatusImage;
private View mStatusDivider;
- private View mStatusRevoked;
- private View mStatusExpired;
public static final int REQUEST_CODE_LOOKUP_KEY = 0x00007006;
@@ -115,9 +119,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
setContentView(R.layout.view_key_activity);
- mStatusDivider = findViewById(R.id.status_divider);
- mStatusRevoked = findViewById(R.id.view_key_revoked);
- mStatusExpired = findViewById(R.id.view_key_expired);
+ mStatusLayout = (LinearLayout) findViewById(R.id.view_key_status_layout);
+ mStatusText = (TextView) findViewById(R.id.view_key_status_text);
+ mStatusImage = (ImageView) findViewById(R.id.view_key_status_image);
+ mStatusDivider = findViewById(R.id.view_key_status_divider);
mViewPager = (ViewPager) findViewById(R.id.view_key_pager);
mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout);
@@ -295,7 +300,7 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
}
} catch (ProviderHelper.NotFoundException e) {
- AppMsg.makeText(this, R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
Log.e(Constants.TAG, "Key not found", e);
}
return super.onOptionsItemSelected(item);
@@ -351,22 +356,11 @@ public class ViewKeyActivity extends ActionBarActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_LOOKUP_KEY: {
- if (resultCode == Activity.RESULT_OK) {
- ImportResult result = data.getParcelableExtra(ImportKeysActivity.EXTRA_RESULT);
- if (result != null) {
- result.displayNotify(this);
- }
- }
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
- }
+ if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) {
+ OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT);
+ result.createNotify(this).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
}
}
@@ -454,8 +448,8 @@ public class ViewKeyActivity extends ActionBarActivity implements
public void handleMessage(Message msg) {
switch (msg.what) {
case NFC_SENT:
- AppMsg.makeText(ViewKeyActivity.this, R.string.nfc_successful,
- AppMsg.STYLE_INFO).show();
+ Notify.showNotify(
+ ViewKeyActivity.this, R.string.nfc_successful, Notify.Style.INFO);
break;
}
}
@@ -514,22 +508,32 @@ public class ViewKeyActivity extends ActionBarActivity implements
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);
getSupportActionBar().setSubtitle(keyIdStr);
- // If this key is revoked, it cannot be used for anything!
- if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
+ boolean isRevoked = data.getInt(INDEX_UNIFIED_IS_REVOKED) > 0;
+ boolean isExpired = !data.isNull(INDEX_UNIFIED_EXPIRY)
+ && new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000).before(new Date());
+
+ // Note: order is important
+ if (isRevoked) {
+ mStatusText.setText(R.string.view_key_revoked);
+ mStatusText.setTextColor(getResources().getColor(R.color.android_red_light));
+ mStatusImage.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
+ mStatusImage.setColorFilter(getResources().getColor(R.color.android_red_light),
+ PorterDuff.Mode.SRC_ATOP);
+ mStatusDivider.setVisibility(View.VISIBLE);
+ mStatusLayout.setVisibility(View.VISIBLE);
+ } else if (isExpired) {
+ mStatusText.setText(R.string.view_key_expired);
+ mStatusText.setTextColor(getResources().getColor(R.color.android_orange_light));
+ mStatusImage.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_expired_cutout));
+ mStatusImage.setColorFilter(getResources().getColor(R.color.android_orange_light),
+ PorterDuff.Mode.SRC_ATOP);
mStatusDivider.setVisibility(View.VISIBLE);
- mStatusRevoked.setVisibility(View.VISIBLE);
- mStatusExpired.setVisibility(View.GONE);
+ mStatusLayout.setVisibility(View.VISIBLE);
} else {
- mStatusRevoked.setVisibility(View.GONE);
-
- Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
- if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
- mStatusDivider.setVisibility(View.VISIBLE);
- mStatusExpired.setVisibility(View.VISIBLE);
- } else {
- mStatusDivider.setVisibility(View.GONE);
- mStatusExpired.setVisibility(View.GONE);
- }
+ mStatusDivider.setVisibility(View.GONE);
+ mStatusLayout.setVisibility(View.GONE);
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index f0636cf2c..a156ff5f1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
@@ -27,10 +28,9 @@ import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ListView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
@@ -52,6 +53,8 @@ public class ViewKeyMainFragment extends LoaderFragment implements
private View mActionEditDivider;
private View mActionEncrypt;
private View mActionCertify;
+ private View mActionCertifyText;
+ private ImageView mActionCertifyImage;
private View mActionCertifyDivider;
private ListView mUserIds;
@@ -76,6 +79,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements
mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider);
mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt);
mActionCertify = view.findViewById(R.id.view_key_action_certify);
+ mActionCertifyText = view.findViewById(R.id.view_key_action_certify_text);
+ mActionCertifyImage = (ImageView) view.findViewById(R.id.view_key_action_certify_image);
+ // make certify image gray, like action icons
+ mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ PorterDuff.Mode.SRC_IN);
mActionCertifyDivider = view.findViewById(R.id.view_key_action_certify_divider);
return root;
@@ -182,6 +190,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements
if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
mActionEdit.setEnabled(false);
mActionCertify.setEnabled(false);
+ mActionCertifyText.setEnabled(false);
mActionEncrypt.setEnabled(false);
} else {
mActionEdit.setEnabled(true);
@@ -189,9 +198,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements
Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
mActionCertify.setEnabled(false);
+ mActionCertifyText.setEnabled(false);
mActionEncrypt.setEnabled(false);
} else {
mActionCertify.setEnabled(true);
+ mActionCertifyText.setEnabled(true);
mActionEncrypt.setEnabled(true);
}
}
@@ -225,7 +236,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements
private void encrypt(Uri dataUri) {
// If there is no encryption key, don't bother.
if (!mHasEncrypt) {
- AppMsg.makeText(getActivity(), R.string.error_no_encrypt_subkey, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_no_encrypt_subkey, Notify.Style.ERROR);
return;
}
try {
@@ -246,15 +257,13 @@ public class ViewKeyMainFragment extends LoaderFragment implements
private void certify(Uri dataUri) {
Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class);
signIntent.setData(dataUri);
- startActivity(signIntent);
+ startActivityForResult(signIntent, 0);
}
private void editKey(Uri dataUri) {
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri));
-// editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
-// startActivityForResult(editIntent, 0);
- startActivity(editIntent);
+ startActivityForResult(editIntent, 0);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
index 52b573f47..e81d5dbf7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -20,7 +20,14 @@ package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
@@ -33,8 +40,6 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
@@ -45,8 +50,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import org.sufficientlysecure.keychain.util.QrCodeUtils;
import java.io.IOException;
@@ -152,7 +157,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
KeyRings.buildUnifiedKeyRingUri(dataUri),
Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
String fingerprint = PgpKeyHelper.convertFingerprintToHex(data);
- if(!toClipboard){
+ if (!toClipboard) {
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else {
content = fingerprint;
@@ -171,13 +176,13 @@ public class ViewKeyShareFragment extends LoaderFragment implements
} else {
message = getResources().getString(R.string.key_copied_to_clipboard);
}
- AppMsg.makeText(getActivity(), message, AppMsg.STYLE_INFO).show();
+ Notify.showNotify(getActivity(), message, Notify.Style.OK);
} else {
// Android will fail with android.os.TransactionTooLargeException if key is too big
// see http://www.lonestarprod.com/?p=34
if (content.length() >= 86389) {
- AppMsg.makeText(getActivity(), R.string.key_too_big_for_sharing,
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.key_too_big_for_sharing,
+ Notify.Style.ERROR);
return;
}
@@ -195,19 +200,20 @@ public class ViewKeyShareFragment extends LoaderFragment implements
}
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
} catch (IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);
}
}
private void showQrCodeDialog() {
- ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(mDataUri);
- dialog.show(ViewKeyShareFragment.this.getActivity().getSupportFragmentManager(), "shareQrCodeDialog");
+ Intent qrCodeIntent = new Intent(getActivity(), QrCodeActivity.class);
+ qrCodeIntent.setData(mDataUri);
+ startActivity(qrCodeIntent);
}
private void showNfcHelpDialog() {
@@ -292,10 +298,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
- String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- mFingerprintQrCode.setImageBitmap(
- QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE)
- );
+ loadQrCode(fingerprint);
break;
}
@@ -311,4 +314,35 @@ public class ViewKeyShareFragment extends LoaderFragment implements
*/
public void onLoaderReset(Loader<Cursor> loader) {
}
+
+ /**
+ * Load QR Code asynchronously and with a fade in animation
+ *
+ * @param fingerprint
+ */
+ private void loadQrCode(final String fingerprint) {
+ AsyncTask<Void, Void, Bitmap> loadTask =
+ new AsyncTask<Void, Void, Bitmap>() {
+ protected Bitmap doInBackground(Void... unused) {
+ String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ return QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE);
+ }
+
+ protected void onPostExecute(Bitmap qrCode) {
+ mFingerprintQrCode.setImageBitmap(qrCode);
+
+ // Transition drawable with a transparent drawable and the final bitmap
+ final TransitionDrawable td =
+ new TransitionDrawable(new Drawable[]{
+ new ColorDrawable(Color.TRANSPARENT),
+ new BitmapDrawable(getResources(), qrCode)
+ });
+
+ mFingerprintQrCode.setImageDrawable(td);
+ td.startTransition(200);
+ }
+ };
+
+ loadTask.execute();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java
deleted file mode 100644
index 601fc09f9..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/WizardActivity.java
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.ActionBarActivity;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Patterns;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.RadioGroup;
-import android.widget.TextView;
-
-import org.sufficientlysecure.htmltextview.HtmlTextView;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ContactHelper;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.util.regex.Matcher;
-
-
-public class WizardActivity extends ActionBarActivity {
-
- private State mCurrentState;
-
- // values for mCurrentScreen
- private enum State {
- START, CREATE_KEY, IMPORT_KEY, K9
- }
-
- public static final int REQUEST_CODE_IMPORT = 0x00007703;
-
- Button mBackButton;
- Button mNextButton;
- StartFragment mStartFragment;
- CreateKeyFragment mCreateKeyFragment;
- K9Fragment mK9Fragment;
-
- private static final String K9_PACKAGE = "com.fsck.k9";
- // private static final String K9_MARKET_INTENT_URI_BASE = "market://details?id=%s";
-// private static final Intent K9_MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse(
-// String.format(K9_MARKET_INTENT_URI_BASE, K9_PACKAGE)));
- private static final Intent K9_MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/k9mail/k-9/releases/tag/4.904"));
-
- LinearLayout mProgressLayout;
- View mProgressLine;
- ProgressBar mProgressBar;
- ImageView mProgressImage;
- TextView mProgressText;
-
- /**
- * Checks if text of given EditText is not empty. If it is empty an error is
- * set and the EditText gets the focus.
- *
- * @param context
- * @param editText
- * @return true if EditText is not empty
- */
- private static boolean isEditTextNotEmpty(Context context, EditText editText) {
- boolean output = true;
- if (editText.getText().toString().length() == 0) {
- editText.setError("empty!");
- editText.requestFocus();
- output = false;
- } else {
- editText.setError(null);
- }
-
- return output;
- }
-
- public static class StartFragment extends Fragment {
- public static StartFragment newInstance() {
- StartFragment myFragment = new StartFragment();
-
- Bundle args = new Bundle();
- myFragment.setArguments(args);
-
- return myFragment;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.wizard_start_fragment,
- container, false);
- }
- }
-
- public static class CreateKeyFragment extends Fragment {
- public static CreateKeyFragment newInstance() {
- CreateKeyFragment myFragment = new CreateKeyFragment();
-
- Bundle args = new Bundle();
- myFragment.setArguments(args);
-
- return myFragment;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.wizard_create_key_fragment,
- container, false);
-
- final AutoCompleteTextView emailView = (AutoCompleteTextView) view.findViewById(R.id.email);
- emailView.setThreshold(1); // Start working from first character
- emailView.setAdapter(
- new ArrayAdapter<String>
- (getActivity(), android.R.layout.simple_spinner_dropdown_item,
- ContactHelper.getPossibleUserEmails(getActivity())
- )
- );
- emailView.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
- }
-
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- String email = editable.toString();
- if (email.length() > 0) {
- Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
- if (emailMatcher.matches()) {
- emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_ok, 0);
- } else {
- emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_bad, 0);
- }
- } else {
- // remove drawable if email is empty
- emailView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
- }
- });
- final AutoCompleteTextView nameView = (AutoCompleteTextView) view.findViewById(R.id.name);
- nameView.setThreshold(1); // Start working from first character
- nameView.setAdapter(
- new ArrayAdapter<String>
- (getActivity(), android.R.layout.simple_spinner_dropdown_item,
- ContactHelper.getPossibleUserNames(getActivity())
- )
- );
- return view;
- }
- }
-
- public static class K9Fragment extends Fragment {
- public static K9Fragment newInstance() {
- K9Fragment myFragment = new K9Fragment();
-
- Bundle args = new Bundle();
- myFragment.setArguments(args);
-
- return myFragment;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.wizard_k9_fragment,
- container, false);
-
- HtmlTextView text = (HtmlTextView) v
- .findViewById(R.id.wizard_k9_text);
- text.setHtmlFromString("Install K9. It's good for you! Here is a screenhot how to enable OK in K9: (TODO)", true);
-
- return v;
- }
-
- }
-
- /**
- * Loads new fragment
- *
- * @param fragment
- */
- private void loadFragment(Fragment fragment) {
- FragmentManager fragmentManager = getSupportFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager
- .beginTransaction();
- fragmentTransaction.replace(R.id.wizard_container,
- fragment);
- fragmentTransaction.commit();
- }
-
- /**
- * Instantiate View and initialize fragments for this Activity
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.wizard_activity);
- mBackButton = (Button) findViewById(R.id.wizard_back);
- mNextButton = (Button) findViewById(R.id.wizard_next);
-
- // progress layout
- mProgressLayout = (LinearLayout) findViewById(R.id.wizard_progress);
- mProgressLine = findViewById(R.id.wizard_progress_line);
- mProgressBar = (ProgressBar) findViewById(R.id.wizard_progress_progressbar);
- mProgressImage = (ImageView) findViewById(R.id.wizard_progress_image);
- mProgressText = (TextView) findViewById(R.id.wizard_progress_text);
-
- changeToState(State.START);
- }
-
- private enum ProgressState {
- WORKING, ENABLED, DISABLED, ERROR
- }
-
- private void showProgress(ProgressState state, String text) {
- switch (state) {
- case WORKING:
- mProgressBar.setVisibility(View.VISIBLE);
- mProgressImage.setVisibility(View.GONE);
- break;
- case ENABLED:
- mProgressBar.setVisibility(View.GONE);
- mProgressImage.setVisibility(View.VISIBLE);
-// mProgressImage.setImageDrawable(getResources().getDrawable(
-// R.drawable.status_enabled));
- break;
- case DISABLED:
- mProgressBar.setVisibility(View.GONE);
- mProgressImage.setVisibility(View.VISIBLE);
-// mProgressImage.setImageDrawable(getResources().getDrawable(
-// R.drawable.status_disabled));
- break;
- case ERROR:
- mProgressBar.setVisibility(View.GONE);
- mProgressImage.setVisibility(View.VISIBLE);
-// mProgressImage.setImageDrawable(getResources().getDrawable(
-// R.drawable.status_fail));
- break;
-
- default:
- break;
- }
- mProgressText.setText(text);
-
- mProgressLine.setVisibility(View.VISIBLE);
- mProgressLayout.setVisibility(View.VISIBLE);
- }
-
- private void hideProgress() {
- mProgressLine.setVisibility(View.GONE);
- mProgressLayout.setVisibility(View.GONE);
- }
-
- public void nextOnClick(View view) {
- // close keyboard
- if (getCurrentFocus() != null) {
- InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- inputManager.hideSoftInputFromWindow(getCurrentFocus()
- .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
- }
-
- switch (mCurrentState) {
- case START: {
- RadioGroup radioGroup = (RadioGroup) findViewById(R.id.wizard_start_radio_group);
- int selectedId = radioGroup.getCheckedRadioButtonId();
- switch (selectedId) {
- case R.id.wizard_start_new_key: {
- changeToState(State.CREATE_KEY);
- break;
- }
- case R.id.wizard_start_import: {
- changeToState(State.IMPORT_KEY);
- break;
- }
- case R.id.wizard_start_skip: {
- finish();
- break;
- }
- }
-
- mBackButton.setText(R.string.btn_back);
- break;
- }
- case CREATE_KEY:
- EditText nameEdit = (EditText) findViewById(R.id.name);
- EditText emailEdit = (EditText) findViewById(R.id.email);
- EditText passphraseEdit = (EditText) findViewById(R.id.passphrase);
-
- if (isEditTextNotEmpty(this, nameEdit)
- && isEditTextNotEmpty(this, emailEdit)
- && isEditTextNotEmpty(this, passphraseEdit)) {
-
-// SaveKeyringParcel newKey = new SaveKeyringParcel();
-// newKey.addUserIds.add(nameEdit.getText().toString() + " <"
-// + emailEdit.getText().toString() + ">");
-
-
- AsyncTask<String, Boolean, Boolean> generateTask = new AsyncTask<String, Boolean, Boolean>() {
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
-
- showProgress(ProgressState.WORKING, "generating key...");
- }
-
- @Override
- protected Boolean doInBackground(String... params) {
- return true;
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- super.onPostExecute(result);
-
- if (result) {
- showProgress(ProgressState.ENABLED, "key generated successfully!");
-
- changeToState(State.K9);
- } else {
- showProgress(ProgressState.ERROR, "error in key gen");
- }
- }
-
- };
-
- generateTask.execute("");
- }
- break;
- case K9: {
- RadioGroup radioGroup = (RadioGroup) findViewById(R.id.wizard_k9_radio_group);
- int selectedId = radioGroup.getCheckedRadioButtonId();
- switch (selectedId) {
- case R.id.wizard_k9_install: {
- try {
- startActivity(K9_MARKET_INTENT);
- } catch (ActivityNotFoundException e) {
- Log.e(Constants.TAG, "Activity not found for: " + K9_MARKET_INTENT);
- }
- break;
- }
- case R.id.wizard_k9_skip: {
- finish();
- break;
- }
- }
-
- finish();
- break;
- }
- default:
- break;
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- switch (requestCode) {
- case REQUEST_CODE_IMPORT: {
- if (resultCode == Activity.RESULT_OK) {
- // imported now...
- changeToState(State.K9);
- } else {
- // back to start
- changeToState(State.START);
- }
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
- }
- }
- }
-
- public void backOnClick(View view) {
- switch (mCurrentState) {
- case START:
- finish();
- break;
- case CREATE_KEY:
- changeToState(State.START);
- break;
- case IMPORT_KEY:
- changeToState(State.START);
- break;
- default:
- changeToState(State.START);
- break;
- }
- }
-
- private void changeToState(State state) {
- switch (state) {
- case START: {
- mCurrentState = State.START;
- mStartFragment = StartFragment.newInstance();
- loadFragment(mStartFragment);
- mBackButton.setText(android.R.string.cancel);
- mNextButton.setText(R.string.btn_next);
- break;
- }
- case CREATE_KEY: {
- mCurrentState = State.CREATE_KEY;
- mCreateKeyFragment = CreateKeyFragment.newInstance();
- loadFragment(mCreateKeyFragment);
- break;
- }
- case IMPORT_KEY: {
- mCurrentState = State.IMPORT_KEY;
- Intent intent = new Intent(this, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
- startActivityForResult(intent, REQUEST_CODE_IMPORT);
- break;
- }
- case K9: {
- mCurrentState = State.K9;
- mBackButton.setEnabled(false); // don't go back to import/create key
- mK9Fragment = K9Fragment.newInstance();
- loadFragment(mK9Fragment);
- break;
- }
- }
- }
-
-}
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 99f959035..4971c535c 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
@@ -30,20 +30,21 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
- public static class FileHasNoContent extends Exception {
-
+ public static class NoValidKeysException extends Exception {
}
- public static class NonPgpPart extends Exception {
+ public static class NonPgpPartException extends Exception {
private int mCount;
- public NonPgpPart(int count) {
+ public NonPgpPartException(int count) {
this.mCount = count;
}
@@ -67,7 +68,6 @@ public class ImportKeysListLoader
@Override
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
-
// This has already been loaded! nvm any further, just return
if (mEntryListWrapper != null) {
return mEntryListWrapper;
@@ -119,9 +119,6 @@ public class ImportKeysListLoader
* @return
*/
private void generateListOfKeyrings(InputData inputData) {
-
- boolean isEmpty = true;
-
PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream());
@@ -130,27 +127,23 @@ public class ImportKeysListLoader
// armor blocks
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
try {
-
- // read all available blocks... (asc files can contain many blocks with BEGIN END)
- while (bufferedInput.available() > 0) {
- // TODO: deal with non-keyring objects?
- List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
- for(UncachedKeyRing key : rings) {
- ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);
- mData.add(item);
- mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(key.getEncoded()));
- isEmpty = false;
- }
+ // parse all keyrings
+ Iterator<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput);
+ while (it.hasNext()) {
+ UncachedKeyRing ring = it.next();
+ ImportKeysListEntry item = new ImportKeysListEntry(getContext(), ring);
+ mData.add(item);
+ mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(ring.getEncoded()));
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
- }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e);
- if (isEmpty) {
- Log.e(Constants.TAG, "File has no content!", new FileHasNoContent());
+ NoValidKeysException e1 = new NoValidKeysException();
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
- (mData, new FileHasNoContent());
+ (mData, e1);
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "Other Exception on parsing key file!", e);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index 02b1f31e2..e5dbebe01 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -32,14 +32,15 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import java.util.Date;
public class SubkeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
+ private SaveKeyringParcel mSaveKeyringParcel;
private boolean hasAnySecret;
-
private ColorStateList mDefaultTextColor;
public static final String[] SUBKEYS_PROJECTION = new String[]{
@@ -71,10 +72,21 @@ public class SubkeysAdapter extends CursorAdapter {
private static final int INDEX_EXPIRY = 11;
private static final int INDEX_FINGERPRINT = 12;
- public SubkeysAdapter(Context context, Cursor c, int flags) {
+ public SubkeysAdapter(Context context, Cursor c, int flags,
+ SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
+ mSaveKeyringParcel = saveKeyringParcel;
+ }
+
+ public SubkeysAdapter(Context context, Cursor c, int flags) {
+ this(context, c, flags, null);
+ }
+
+ public long getKeyId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(INDEX_KEY_ID);
}
@Override
@@ -94,79 +106,94 @@ public class SubkeysAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
- TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
- ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
- ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
-
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(INDEX_KEY_ID));
+ TextView vKeyId = (TextView) view.findViewById(R.id.keyId);
+ TextView vKeyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ TextView vKeyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
+ ImageView vMasterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
+ ImageView vCertifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
+ ImageView vEncryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
+ ImageView vSignIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ ImageView vRevokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
+ ImageView vEditImage = (ImageView) view.findViewById(R.id.edit_image);
+
+ long keyId = cursor.getLong(INDEX_KEY_ID);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
context,
cursor.getInt(INDEX_ALGORITHM),
cursor.getInt(INDEX_KEY_SIZE)
);
- keyId.setText(keyIdStr);
+ vKeyId.setText(keyIdStr);
// may be set with additional "stripped" later on
if (hasAnySecret && cursor.getInt(INDEX_HAS_SECRET) == 0) {
- keyDetails.setText(algorithmStr + ", " +
+ vKeyDetails.setText(algorithmStr + ", " +
context.getString(R.string.key_stripped));
} else {
- keyDetails.setText(algorithmStr);
+ vKeyDetails.setText(algorithmStr);
}
// Set icons according to properties
- masterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
- certifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
- encryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
- signIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
+ vMasterKeyIcon.setVisibility(cursor.getInt(INDEX_RANK) == 0 ? View.VISIBLE : View.INVISIBLE);
+ vCertifyIcon.setVisibility(cursor.getInt(INDEX_CAN_CERTIFY) != 0 ? View.VISIBLE : View.GONE);
+ vEncryptIcon.setVisibility(cursor.getInt(INDEX_CAN_ENCRYPT) != 0 ? View.VISIBLE : View.GONE);
+ vSignIcon.setVisibility(cursor.getInt(INDEX_CAN_SIGN) != 0 ? View.VISIBLE : View.GONE);
+
+ boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+
+ // for edit key
+ if (mSaveKeyringParcel != null) {
+ boolean revokeThisSubkey = (mSaveKeyringParcel.mRevokeSubKeys.contains(keyId));
- boolean valid = true;
- if (cursor.getInt(INDEX_IS_REVOKED) > 0) {
- revokedKeyIcon.setVisibility(View.VISIBLE);
+ if (revokeThisSubkey) {
+ if (!isRevoked) {
+ isRevoked = true;
+ }
+ }
- valid = false;
+ vEditImage.setVisibility(View.VISIBLE);
} else {
- keyId.setTextColor(mDefaultTextColor);
- keyDetails.setTextColor(mDefaultTextColor);
- keyExpiry.setTextColor(mDefaultTextColor);
+ vEditImage.setVisibility(View.GONE);
+ }
- revokedKeyIcon.setVisibility(View.GONE);
+ if (isRevoked) {
+ vRevokedKeyIcon.setVisibility(View.VISIBLE);
+ } else {
+ vKeyId.setTextColor(mDefaultTextColor);
+ vKeyDetails.setTextColor(mDefaultTextColor);
+ vKeyExpiry.setTextColor(mDefaultTextColor);
+
+ vRevokedKeyIcon.setVisibility(View.GONE);
}
+ boolean isExpired;
if (!cursor.isNull(INDEX_EXPIRY)) {
Date expiryDate = new Date(cursor.getLong(INDEX_EXPIRY) * 1000);
+ isExpired = expiryDate.before(new Date());
- valid = valid && expiryDate.after(new Date());
- keyExpiry.setText(
- context.getString(R.string.label_expiry) + ": " +
- DateFormat.getDateFormat(context).format(expiryDate)
- );
+ vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": "
+ + DateFormat.getDateFormat(context).format(expiryDate));
} else {
- keyExpiry.setText(
- context.getString(R.string.label_expiry) + ": " +
- context.getString(R.string.none)
- );
+ isExpired = false;
+
+ vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " + context.getString(R.string.none));
}
// if key is expired or revoked, strike through text
- if (!valid) {
- keyId.setText(OtherHelper.strikeOutText(keyId.getText()));
- keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText()));
- keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText()));
+ boolean isInvalid = isRevoked || isExpired;
+ if (isInvalid) {
+ vKeyId.setText(OtherHelper.strikeOutText(vKeyId.getText()));
+ vKeyDetails.setText(OtherHelper.strikeOutText(vKeyDetails.getText()));
+ vKeyExpiry.setText(OtherHelper.strikeOutText(vKeyExpiry.getText()));
}
- keyId.setEnabled(valid);
- keyDetails.setEnabled(valid);
- keyExpiry.setEnabled(valid);
+ vKeyId.setEnabled(!isInvalid);
+ vKeyDetails.setEnabled(!isInvalid);
+ vKeyExpiry.setEnabled(!isInvalid);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- View view = mInflater.inflate(R.layout.view_key_keys_item, null);
+ View view = mInflater.inflate(R.layout.view_key_subkey_item, null);
if (mDefaultTextColor == null) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
mDefaultTextColor = keyId.getTextColors();
@@ -177,13 +204,21 @@ public class SubkeysAdapter extends CursorAdapter {
// Disable selection of items, http://stackoverflow.com/a/4075045
@Override
public boolean areAllItemsEnabled() {
- return false;
+ if (mSaveKeyringParcel == null) {
+ return false;
+ } else {
+ return super.areAllItemsEnabled();
+ }
}
// Disable selection of items, http://stackoverflow.com/a/4075045
@Override
public boolean isEnabled(int position) {
- return false;
+ if (mSaveKeyringParcel == null) {
+ return false;
+ } else {
+ return super.isEnabled(position);
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
index d729648e5..c8b74da2e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
@@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.Typeface;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,9 +42,7 @@ import java.util.ArrayList;
public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener {
private LayoutInflater mInflater;
-
private final ArrayList<Boolean> mCheckStates;
-
private SaveKeyringParcel mSaveKeyringParcel;
public static final String[] USER_IDS_PROJECTION = new String[]{
@@ -60,7 +60,6 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
private static final int INDEX_IS_PRIMARY = 4;
private static final int INDEX_IS_REVOKED = 5;
-
public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes,
SaveKeyringParcel saveKeyringParcel) {
super(context, c, flags);
@@ -134,15 +133,18 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
// for edit key
if (mSaveKeyringParcel != null) {
- boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.changePrimaryUserId != null);
- boolean changeThisPrimaryUserId = (mSaveKeyringParcel.changePrimaryUserId != null
- && mSaveKeyringParcel.changePrimaryUserId.equals(userId));
- boolean revokeThisUserId = (mSaveKeyringParcel.revokeUserIds.contains(userId));
+ boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null);
+ boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null
+ && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId));
+ boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId));
+ // only if primary user id will be changed
+ // (this is not triggered if the user id is currently the primary one)
if (changeAnyPrimaryUserId) {
- // change all user ids, only this one should be primary
+ // change _all_ primary user ids and set new one to true
isPrimary = changeThisPrimaryUserId;
}
+
if (revokeThisUserId) {
if (!isRevoked) {
isRevoked = true;
@@ -156,7 +158,10 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
if (isRevoked) {
// set revocation icon (can this even be primary?)
- vVerified.setImageResource(R.drawable.key_certify_revoke);
+ vVerified.setImageResource(R.drawable.status_signature_revoked_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.bg_gray),
+ PorterDuff.Mode.SRC_IN);
// disable and strike through text for revoked user ids
vName.setEnabled(false);
@@ -170,22 +175,33 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
vAddress.setEnabled(true);
vComment.setEnabled(true);
- // verified: has been verified
- // isPrimary: show small star icon for primary user ids
- int verified = cursor.getInt(INDEX_VERIFIED);
- switch (verified) {
+ if (isPrimary) {
+ vName.setTypeface(null, Typeface.BOLD);
+ vAddress.setTypeface(null, Typeface.BOLD);
+ } else {
+ vName.setTypeface(null, Typeface.NORMAL);
+ vAddress.setTypeface(null, Typeface.NORMAL);
+ }
+
+ int isVerified = cursor.getInt(INDEX_VERIFIED);
+ switch (isVerified) {
case Certs.VERIFIED_SECRET:
- vVerified.setImageResource(isPrimary
- ? R.drawable.key_certify_primary_ok_depth0
- : R.drawable.key_certify_ok_depth0);
+ vVerified.setImageResource(R.drawable.status_signature_verified_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.android_green_light),
+ PorterDuff.Mode.SRC_IN);
break;
case Certs.VERIFIED_SELF:
- vVerified.setImageResource(isPrimary
- ? R.drawable.key_certify_primary_ok_self
- : R.drawable.key_certify_ok_self);
+ vVerified.setImageResource(R.drawable.status_signature_unverified_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.bg_gray),
+ PorterDuff.Mode.SRC_IN);
break;
default:
- vVerified.setImageResource(R.drawable.key_certify_error);
+ vVerified.setImageResource(R.drawable.status_signature_invalid_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.android_red_light),
+ PorterDuff.Mode.SRC_IN);
break;
}
}
@@ -233,7 +249,7 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- View view = mInflater.inflate(R.layout.view_key_userids_item, null);
+ View view = mInflater.inflate(R.layout.view_key_user_id_item, null);
// only need to do this once ever, since mShowCheckBoxes is final
view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE);
return view;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java
new file mode 100644
index 000000000..d5354a9f6
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ChangeExpiryDialogFragment.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.DatePickerDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v4.app.DialogFragment;
+import android.text.format.DateUtils;
+import android.widget.DatePicker;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+public class ChangeExpiryDialogFragment extends DialogFragment {
+ private static final String ARG_MESSENGER = "messenger";
+ private static final String ARG_CREATION_DATE = "creation_date";
+ private static final String ARG_EXPIRY_DATE = "expiry_date";
+
+ public static final int MESSAGE_NEW_EXPIRY_DATE = 1;
+ public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date";
+
+ private Messenger mMessenger;
+ private Calendar mCreationCal;
+ private Calendar mExpiryCal;
+
+ private int mDatePickerResultCount = 0;
+ private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+ // Note: Ignore results after the first one - android sends multiples.
+ if (mDatePickerResultCount++ == 0) {
+ Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ selectedCal.set(year, monthOfYear, dayOfMonth);
+ if (mExpiryCal != null) {
+ long numDays = (selectedCal.getTimeInMillis() / 86400000)
+ - (mExpiryCal.getTimeInMillis() / 86400000);
+ if (numDays > 0) {
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime());
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ }
+ } else {
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime());
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ }
+ }
+ }
+ };
+
+ public class ExpiryDatePickerDialog extends DatePickerDialog {
+
+ public ExpiryDatePickerDialog(Context context, OnDateSetListener callBack,
+ int year, int monthOfYear, int dayOfMonth) {
+ super(context, callBack, year, monthOfYear, dayOfMonth);
+ }
+
+ // set permanent title
+ public void setTitle(CharSequence title) {
+ super.setTitle(getContext().getString(R.string.expiry_date_dialog_title));
+ }
+ }
+
+ /**
+ * Creates new instance of this dialog fragment
+ */
+ public static ChangeExpiryDialogFragment newInstance(Messenger messenger,
+ Date creationDate, Date expiryDate) {
+ ChangeExpiryDialogFragment frag = new ChangeExpiryDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_MESSENGER, messenger);
+ args.putSerializable(ARG_CREATION_DATE, creationDate);
+ args.putSerializable(ARG_EXPIRY_DATE, expiryDate);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+ Date creationDate = (Date) getArguments().getSerializable(ARG_CREATION_DATE);
+ Date expiryDate = (Date) getArguments().getSerializable(ARG_EXPIRY_DATE);
+
+ mCreationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ mCreationCal.setTime(creationDate);
+ mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ mExpiryCal.setTime(expiryDate);
+
+ /*
+ * Using custom DatePickerDialog which overrides the setTitle because
+ * the DatePickerDialog title is buggy (unix warparound bug).
+ * See: https://code.google.com/p/android/issues/detail?id=49066
+ */
+ DatePickerDialog dialog = new ExpiryDatePickerDialog(getActivity(),
+ mExpiryDateSetListener, mExpiryCal.get(Calendar.YEAR), mExpiryCal.get(Calendar.MONTH),
+ mExpiryCal.get(Calendar.DAY_OF_MONTH));
+ mDatePickerResultCount = 0;
+ dialog.setCancelable(true);
+ dialog.setButton(Dialog.BUTTON_NEGATIVE,
+ getActivity().getString(R.string.btn_no_date),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // Note: Ignore results after the first one - android sends multiples.
+ if (mDatePickerResultCount++ == 0) {
+ // none expiry dates corresponds to a null message
+ Bundle data = new Bundle();
+ data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null);
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ }
+ }
+ }
+ );
+
+ // setCalendarViewShown() is supported from API 11 onwards.
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ // Hide calendarView in tablets because of the unix warparound bug.
+ dialog.getDatePicker().setCalendarViewShown(false);
+ }
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ // will crash with IllegalArgumentException if we set a min date
+ // that is not before expiry
+ if (mCreationCal != null && mCreationCal.before(mExpiryCal)) {
+ dialog.getDatePicker().setMinDate(mCreationCal.getTime().getTime()
+ + DateUtils.DAY_IN_MILLIS);
+ } else {
+ // When created date isn't available
+ dialog.getDatePicker().setMinDate(mExpiryCal.getTime().getTime()
+ + DateUtils.DAY_IN_MILLIS);
+ }
+ }
+
+ return dialog;
+ }
+
+ /**
+ * Send message back to handler which is initialized in a activity
+ *
+ * @param what Message integer you want to send
+ */
+ private void sendMessageToHandler(Integer what, Bundle data) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
new file mode 100644
index 000000000..9fef88a78
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyDialogFragment.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v4.app.DialogFragment;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class EditSubkeyDialogFragment extends DialogFragment {
+ private static final String ARG_MESSENGER = "messenger";
+
+ public static final int MESSAGE_CHANGE_EXPIRY = 1;
+ public static final int MESSAGE_REVOKE = 2;
+
+ private Messenger mMessenger;
+
+ /**
+ * Creates new instance of this dialog fragment
+ */
+ public static EditSubkeyDialogFragment newInstance(Messenger messenger) {
+ EditSubkeyDialogFragment frag = new EditSubkeyDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_MESSENGER, messenger);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+
+ CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(getActivity());
+ CharSequence[] array = getResources().getStringArray(R.array.edit_key_edit_subkey);
+
+ builder.setTitle(R.string.edit_key_edit_subkey_title);
+ builder.setItems(array, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case 0:
+ sendMessageToHandler(MESSAGE_CHANGE_EXPIRY, null);
+ break;
+ case 1:
+ sendMessageToHandler(MESSAGE_REVOKE, null);
+ break;
+ default:
+ break;
+ }
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+ }
+ });
+
+ return builder.show();
+ }
+
+ /**
+ * Send message back to handler which is initialized in a activity
+ *
+ * @param what Message integer you want to send
+ */
+ private void sendMessageToHandler(Integer what, Bundle data) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 59e4d8dee..ef2659cf8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -33,8 +33,8 @@ import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@@ -44,8 +44,8 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@@ -62,7 +62,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
private Messenger mMessenger;
private EditText mPassphraseEditText;
- private boolean mCanKB;
/**
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
@@ -102,10 +101,10 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
try {
- if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
+ if (!new ProviderHelper(context).getCanonicalizedSecretKeyRing(secretKeyId).hasPassphrase()) {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
}
- } catch(ProviderHelper.NotFoundException e) {
+ } catch (ProviderHelper.NotFoundException e) {
throw new PgpGeneralException("Error: Key not found!", e);
}
}
@@ -120,11 +119,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return frag;
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
/**
* Creates dialog
*/
@@ -138,7 +132,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
alert.setTitle(R.string.title_authentication);
- final WrappedSecretKeyRing secretRing;
+ final CanonicalizedSecretKeyRing secretRing;
String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) {
@@ -147,12 +141,12 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
} else {
try {
ProviderHelper helper = new ProviderHelper(activity);
- secretRing = helper.getWrappedSecretKeyRing(secretKeyId);
+ secretRing = helper.getCanonicalizedSecretKeyRing(secretKeyId);
// yes the inner try/catch block is necessary, otherwise the final variable
// above can't be statically verified to have been set in all cases because
// the catch clause doesn't return.
try {
- userId = secretRing.getPrimaryUserId();
+ userId = secretRing.getPrimaryUserIdWithFallback();
} catch (PgpGeneralException e) {
userId = null;
}
@@ -165,7 +159,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
});
alert.setCancelable(false);
- mCanKB = false;
return alert.create();
}
@@ -189,7 +182,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// Early breakout if we are dealing with a symmetric key
if (secretRing == null) {
- PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase);
+ PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric,
+ passphrase, getString(R.string.passp_cache_notif_pwd));
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
@@ -197,9 +191,9 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return;
}
- WrappedSecretKey unlockedSecretKey = null;
+ CanonicalizedSecretKey unlockedSecretKey = null;
- for(WrappedSecretKey clickSecretKey : secretRing.secretKeyIterator()) {
+ for (CanonicalizedSecretKey clickSecretKey : secretRing.secretKeyIterator()) {
try {
boolean unlocked = clickSecretKey.unlock(passphrase);
if (unlocked) {
@@ -228,10 +222,18 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
- PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase);
+
+ try {
+ PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase,
+ secretRing.getPrimaryUserIdWithFallback());
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "adding of a passphrase failed", e);
+ }
+
if (unlockedSecretKey.getKeyId() != masterKeyId) {
PassphraseCacheService.addCachedPassphrase(
- activity, unlockedSecretKey.getKeyId(), passphrase);
+ activity, unlockedSecretKey.getKeyId(), passphrase,
+ unlockedSecretKey.getPrimaryUserIdWithFallback());
}
// also return passphrase back to activity
@@ -249,20 +251,30 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
});
- mCanKB = true;
- return alert.show();
- }
+ // Hack to open keyboard.
+ // This is the only method that I found to work across all Android versions
+ // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ // Notes: * onCreateView can't be used because we want to add buttons to the dialog
+ // * opening in onActivityCreated does not work on Android 4.4
+ mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPassphraseEditText.post(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+ }
+ });
+ mPassphraseEditText.requestFocus();
- @Override
- public void onActivityCreated(Bundle arg0) {
- super.onActivityCreated(arg0);
- if (mCanKB) {
- // request focus and open soft keyboard
- mPassphraseEditText.requestFocus();
- getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-
- mPassphraseEditText.setOnEditorActionListener(this);
- }
+ mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
+ mPassphraseEditText.setOnEditorActionListener(this);
+
+ return alert.show();
}
@Override
@@ -273,6 +285,27 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
sendMessageToHandler(MESSAGE_CANCEL);
}
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ Log.d(Constants.TAG, "onDismiss");
+
+ // hide keyboard on dismiss
+ hideKeyboard();
+ }
+
+ private void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ //check if no view has focus:
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+
/**
* Associate the "done" button on the soft keyboard with the okay button in the view
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 93da48b75..1386ed098 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
@@ -32,6 +33,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
@@ -164,18 +166,50 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
}
});
+ // Hack to open keyboard.
+ // This is the only method that I found to work across all Android versions
+ // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ // Notes: * onCreateView can't be used because we want to add buttons to the dialog
+ // * opening in onActivityCreated does not work on Android 4.4
+ mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPassphraseEditText.post(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+ }
+ });
+ mPassphraseEditText.requestFocus();
+
+ mPassphraseAgainEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
+ mPassphraseAgainEditText.setOnEditorActionListener(this);
+
return alert.show();
}
@Override
- public void onActivityCreated(Bundle arg0) {
- super.onActivityCreated(arg0);
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
- // request focus and open soft keyboard
- mPassphraseEditText.requestFocus();
- getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ // hide keyboard on dismiss
+ hideKeyboard();
+ }
- mPassphraseAgainEditText.setOnEditorActionListener(this);
+ private void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ //check if no view has focus:
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
deleted file mode 100644
index 24608784b..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui.dialog;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.devspark.appmsg.AppMsg;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.QrCodeUtils;
-
-public class ShareQrCodeDialogFragment extends DialogFragment {
- private static final String ARG_KEY_URI = "uri";
-
- private ImageView mImage;
- private TextView mText;
-
- private static final int QR_CODE_SIZE = 1000;
-
- /**
- * Creates new instance of this dialog fragment
- */
- public static ShareQrCodeDialogFragment newInstance(Uri dataUri) {
- ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment();
- Bundle args = new Bundle();
- args.putParcelable(ARG_KEY_URI, dataUri);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Creates dialog
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Activity activity = getActivity();
-
- Uri dataUri = getArguments().getParcelable(ARG_KEY_URI);
-
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(getActivity());
- alert.setTitle(R.string.share_qr_code_dialog_title);
-
- LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.share_qr_code_dialog, null);
- alert.setView(view);
-
- mImage = (ImageView) view.findViewById(R.id.share_qr_code_dialog_image);
- mText = (TextView) view.findViewById(R.id.share_qr_code_dialog_text);
-
- ProviderHelper providerHelper = new ProviderHelper(getActivity());
- String content;
- try {
- alert.setPositiveButton(R.string.btn_okay, null);
-
- byte[] blob = (byte[]) providerHelper.getGenericData(
- KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- if (blob == null) {
- Log.e(Constants.TAG, "key not found!");
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- return null;
- }
-
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
- mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
- content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- setQrCode(content);
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- return null;
- }
-
- return alert.show();
- }
-
- private void setQrCode(String data) {
- mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(data, QR_CODE_SIZE));
- }
-
-}
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 67f81fb24..22e3f5d66 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java
@@ -29,7 +29,7 @@ import com.github.johnpersano.supertoasts.SuperToast;
*/
public class Notify {
- public static enum Style {OK, WARN, ERROR}
+ public static enum Style {OK, WARN, INFO, ERROR}
/**
* Shows a simple in-layout notification with the CharSequence given as parameter
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
index 5553ea5d2..95a259336 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
@@ -39,15 +39,21 @@ public class ProgressScaler implements Progressable {
* Set progress of ProgressDialog by sending message to handler on UI thread
*/
public void setProgress(String message, int progress, int max) {
- mWrapped.setProgress(message, mFrom + progress * (mTo - mFrom) / max, mMax);
+ if (mWrapped != null) {
+ mWrapped.setProgress(message, mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
}
public void setProgress(int resourceId, int progress, int max) {
- mWrapped.setProgress(resourceId, progress, mMax);
+ if (mWrapped != null) {
+ mWrapped.setProgress(resourceId, mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
}
public void setProgress(int progress, int max) {
- mWrapped.setProgress(progress, max);
+ if (mWrapped != null) {
+ mWrapped.setProgress(mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
}
}
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml b/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml
new file mode 100644
index 000000000..4a021c676
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="-100%"
+ android:toXDelta="0"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml b/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml
new file mode 100644
index 000000000..1329f8bef
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="100%"
+ android:toXDelta="0"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml b/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml
new file mode 100644
index 000000000..4e02af2a4
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0"
+ android:toXDelta="-100%"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml b/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml
new file mode 100644
index 000000000..7b5f63e0d
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0"
+ android:toXDelta="100%"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml b/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml
new file mode 100644
index 000000000..2b95cfba6
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator">
+ <scale
+ android:fromXScale="0.5"
+ android:toXScale="1.0"
+ android:fromYScale="0.5"
+ android:toYScale="1.0"
+ android:pivotX="50%p"
+ android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <alpha
+ android:fromAlpha="0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml b/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml
new file mode 100644
index 000000000..772375739
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale
+ android:fromXScale="1.0"
+ android:toXScale="0.5"
+ android:fromYScale="1.0"
+ android:toYScale="0.5"
+ android:pivotX="50%p"
+ android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png
new file mode 100644
index 000000000..acc198fc3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png
deleted file mode 100644
index 391d1c988..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png
deleted file mode 100644
index 76944469c..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png
deleted file mode 100644
index 815701015..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index 026869c14..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index 12d2e026e..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png
deleted file mode 100644
index c39d3a87c..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png
new file mode 100644
index 000000000..a1b090630
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png
new file mode 100644
index 000000000..e567055aa
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png
new file mode 100644
index 000000000..98e32eadc
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png
new file mode 100644
index 000000000..21e8b536a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..84ac9bec2
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png
new file mode 100644
index 000000000..9ae2a09ab
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..967e00e80
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png
new file mode 100644
index 000000000..16e1d7181
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..244dd0708
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png
new file mode 100644
index 000000000..5c3ba866d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..82cc25a4b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png
new file mode 100644
index 000000000..b8b472a5a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..e752eaeab
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png
new file mode 100644
index 000000000..d8141b47b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..08a9f464c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png
deleted file mode 100644
index 79fddf78a..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png
deleted file mode 100644
index c400a1820..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png
deleted file mode 100644
index fb1654b53..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png
deleted file mode 100644
index 5228a4b6b..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png
new file mode 100644
index 000000000..58476bfc9
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png
deleted file mode 100644
index 6def8769f..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png
deleted file mode 100644
index e16ec810a..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png
deleted file mode 100644
index 715a16487..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index c376a2897..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index 45a261b24..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png
deleted file mode 100644
index 62ee0ca42..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png
new file mode 100644
index 000000000..cfc39f0e7
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png
new file mode 100644
index 000000000..824dc2672
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png
new file mode 100644
index 000000000..9bca59ae3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png
new file mode 100644
index 000000000..81a900147
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..bc91094b5
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png
new file mode 100644
index 000000000..baa78f795
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..bc2f56e2a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png
new file mode 100644
index 000000000..7cf985274
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..2d2593194
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png
new file mode 100644
index 000000000..3d4665320
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..0fc74d07e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png
new file mode 100644
index 000000000..8348b32b3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..96a2d1413
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png
new file mode 100644
index 000000000..02e53ac8a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..9f7cf837c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png
new file mode 100644
index 000000000..022f2dd2e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png
deleted file mode 100644
index 8278ce2b6..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png
deleted file mode 100644
index e2aef1177..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png
deleted file mode 100644
index 9bb6ceffa..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index de6614246..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index ce10da099..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png
deleted file mode 100644
index 1478e726b..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png
new file mode 100644
index 000000000..7c6bb2d18
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png
new file mode 100644
index 000000000..da4a5d89a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png
new file mode 100644
index 000000000..cd02fc1e4
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png
new file mode 100644
index 000000000..f5105c1ae
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..83f6fde35
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png
new file mode 100644
index 000000000..67880d6db
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..29830f5ba
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png
new file mode 100644
index 000000000..2ed67419b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..2f7695043
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png
new file mode 100644
index 000000000..a6f1f2792
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..2ce28c7ca
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png
new file mode 100644
index 000000000..c25a84b4d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..442c55eee
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png
new file mode 100644
index 000000000..6f435a85e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..160ec7cbe
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png
new file mode 100644
index 000000000..5392deafd
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png
deleted file mode 100644
index 9416720eb..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png
deleted file mode 100644
index 501a75d63..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png
deleted file mode 100644
index 72ada9c1f..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index 1b52ef04d..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index baa1c00d2..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png
deleted file mode 100644
index 217f4e914..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png
new file mode 100644
index 000000000..5a9664d59
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png
new file mode 100644
index 000000000..608f065af
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png
new file mode 100644
index 000000000..ee34dd396
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png
new file mode 100644
index 000000000..f475c9d84
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..33a3efed1
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png
new file mode 100644
index 000000000..f21c2cf52
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..bc39d3496
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png
new file mode 100644
index 000000000..be1a1d9dc
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..58929661f
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png
new file mode 100644
index 000000000..841cfa958
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..3020357a4
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png
new file mode 100644
index 000000000..525d1cf6b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..3829bb3a0
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png
new file mode 100644
index 000000000..54eee5ba0
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..3548ee2b6
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable/first_time_1.png b/OpenKeychain/src/main/res/drawable/first_time_1.png
new file mode 100644
index 000000000..1f340df5c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable/first_time_1.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml
index 3557c1f00..b2a9c11f5 100644
--- a/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml
+++ b/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml
@@ -1,20 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
+ <include layout="@layout/notify_area" />
+
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/api_account_settings_fragment"
- android:name="org.sufficientlysecure.keychain.remote.ui.AccountSettingsFragment"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical">
+
+ <fragment
+ android:id="@+id/api_account_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.remote.ui.AccountSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
- </LinearLayout>
-</ScrollView>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
index 49c4ccbfe..1c09820a9 100644
--- a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
+++ b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
@@ -1,33 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
+ <include layout="@layout/notify_area" />
+
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/api_app_settings_fragment"
- android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:layout="@layout/api_app_settings_fragment" />
+ android:padding="16dp"
+ android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/api_settings_accounts"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <fragment
+ android:id="@+id/api_app_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/api_app_settings_fragment" />
- <FrameLayout
- android:id="@+id/api_accounts_list_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/api_settings_accounts"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <FrameLayout
+ android:id="@+id/api_accounts_list_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
- </LinearLayout>
-</ScrollView>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/certify_key_activity.xml b/OpenKeychain/src/main/res/layout/certify_key_activity.xml
index bb43fa805..34d4dbd57 100644
--- a/OpenKeychain/src/main/res/layout/certify_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/certify_key_activity.xml
@@ -1,171 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="wrap_content"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
+ <include layout="@layout/notify_area" />
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_certification_key" />
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/sign_key_select_key_fragment"
- android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- tools:layout="@layout/select_secret_key_layout_fragment" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_key_to_certify" />
-
- <TableLayout
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_marginLeft="8dp"
- android:shrinkColumns="1">
-
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_key_id" />
-
- <TextView
- android:id="@+id/key_id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text=""
- android:typeface="monospace" />
- </TableRow>
-
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_main_user_id" />
-
- <TextView
- android:id="@+id/main_user_id"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </TableRow>
-
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_certification_key" />
+
+ <fragment
+ android:id="@+id/sign_key_select_key_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ tools:layout="@layout/select_secret_key_layout_fragment" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_key_to_certify" />
+
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_marginLeft="8dp"
+ android:shrinkColumns="1">
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_key_id" />
+
+ <TextView
+ android:id="@+id/key_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text=""
+ android:typeface="monospace" />
+ </TableRow>
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_main_user_id" />
+
+ <TextView
+ android:id="@+id/main_user_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </TableRow>
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_fingerprint" />
+
+ <TextView
+ android:id="@+id/view_key_fingerprint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:typeface="monospace" />
+
+ </TableRow>
+
+ </TableLayout>
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_uids_to_certify" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/view_key_user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:descendantFocusability="blocksDescendants" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_upload_key" />
+
+ <CheckBox
+ android:id="@+id/sign_key_upload_checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:checked="false"
+ android:text="@string/label_send_key" />
+
+ <Spinner
+ android:id="@+id/upload_key_keyserver"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:enabled="false" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_actions"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:id="@+id/certify_key_certify_button"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ android:paddingRight="4dp"
+ android:layout_marginBottom="8dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
<TextView
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:text="@string/key_view_action_certify"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+
+ <ImageView
+ android:id="@+id/certify_key_action_certify_image"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_fingerprint" />
-
- <TextView
- android:id="@+id/view_key_fingerprint"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:typeface="monospace" />
-
- </TableRow>
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/status_signature_verified_cutout"
+ android:layout_gravity="center_vertical" />
- </TableLayout>
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_uids_to_certify" />
+ </LinearLayout>
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/view_key_user_ids"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:descendantFocusability="blocksDescendants" />
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_upload_key" />
+ </LinearLayout>
- <CheckBox
- android:id="@+id/sign_key_upload_checkbox"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:checked="false"
- android:text="@string/label_send_key" />
+ </ScrollView>
- <Spinner
- android:id="@+id/upload_key_keyserver"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:enabled="false" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_actions"
- android:layout_weight="1" />
-
- <TextView
- android:id="@+id/sign_key_sign_button"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_marginBottom="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/key_view_action_certify"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_good"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:clickable="true"
- style="@style/SelectableItem" />
-
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml b/OpenKeychain/src/main/res/layout/create_key_activity.xml
index f96b993c5..0bd053c49 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_activity.xml
@@ -4,8 +4,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <include layout="@layout/notify_area" />
+
<FrameLayout
- android:id="@+id/edit_key_fragment_container"
+ android:id="@+id/create_key_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
diff --git a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
new file mode 100644
index 000000000..64b3621a4
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/create_key_buttons_divider">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:text="@string/create_key_final_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:text="@string/label_name"
+ android:textColor="@color/tertiary_text_light"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:text="Max Mustermann"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:text="@string/label_email"
+ android:textColor="@color/tertiary_text_light"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/email"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="max@musterman.com"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <CheckBox
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/create_key_upload"
+ android:id="@+id/create_key_upload" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
+ android:text="@string/create_key_final_robot_text"
+ android:textColor="@color/android_green_dark"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:drawableLeft="@drawable/create_key_robot"
+ android:drawablePadding="8dp" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:id="@+id/create_key_buttons">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_back"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_vertical" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/create_key_create_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/title_create_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/create_key_buttons_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_alignTop="@+id/create_key_buttons"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
new file mode 100644
index 000000000..30ccc05ab
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="false"
+ android:layout_above="@+id/create_key_button_divider">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:text="@string/create_key_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <AutoCompleteTextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:imeOptions="actionNext"
+ android:inputType="textPersonName"
+ android:hint="@string/label_name"
+ android:ems="10"
+ android:id="@+id/name" />
+
+ <AutoCompleteTextView
+ android:id="@+id/email"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:imeOptions="actionNext"
+ android:hint="@string/label_email"
+ android:ems="10"
+ android:inputType="textEmailAddress" />
+
+ <EditText
+ android:id="@+id/passphrase"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:inputType="textPassword"
+ android:hint="@string/label_passphrase"
+ android:ems="10"
+ android:layout_gravity="center_horizontal" />
+
+ <EditText
+ android:id="@+id/passphrase_again"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:inputType="textPassword"
+ android:hint="@string/label_passphrase_again"
+ android:ems="10"
+ android:layout_gravity="center_horizontal" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <View
+ android:id="@+id/create_key_button_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:background="?android:attr/listDivider"
+ android:layout_alignTop="@+id/create_key_buttons"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:id="@+id/create_key_buttons">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text=""
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/create_key_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/title_create_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
index 05877656b..fcad91df3 100644
--- a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
+++ b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
@@ -8,7 +8,7 @@
android:paddingRight="16dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
- android:background="@color/result_purple">
+ android:background="@color/android_purple_light">
<View
android:layout_width="match_parent"
diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity.xml b/OpenKeychain/src/main/res/layout/edit_key_activity.xml
index b6c5a1c9a..7e71ccf53 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/edit_key_activity.xml
@@ -1,46 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true"
- android:orientation="vertical" >
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="16dp"
- android:paddingRight="16dp" >
+ <include layout="@layout/notify_area"/>
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="4dp"
- android:text="@string/label_passphrase" />
+ <FrameLayout
+ android:id="@+id/edit_key_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
- <CheckBox
- android:id="@+id/edit_key_no_passphrase"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_no_passphrase" />
-
- <Button
- android:id="@+id/edit_key_btn_change_passphrase"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:text="@string/btn_set_passphrase"
- android:drawableLeft="@drawable/ic_action_edit"
- android:background="@drawable/button_edgy" />
-
- <LinearLayout
- android:id="@+id/edit_key_container"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
- </LinearLayout>
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml b/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml
index 856bef36a..a4258b998 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml
+++ b/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml
@@ -9,7 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="10dp"
- android:background="@color/result_green" />
+ android:background="@color/android_green_light" />
<TableLayout
android:layout_width="0dp"
diff --git a/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml b/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml
index e69452db1..ef0e2626e 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml
+++ b/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml
@@ -10,7 +10,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="10dp"
- android:background="@color/result_green" />
+ android:background="@color/android_green_light" />
<LinearLayout
android:orientation="vertical"
diff --git a/OpenKeychain/src/main/res/layout/first_time_activity.xml b/OpenKeychain/src/main/res/layout/first_time_activity.xml
new file mode 100644
index 000000000..fad1a3b26
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/first_time_activity.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp">
+
+ <LinearLayout
+ android:id="@+id/first_time_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:layout_alignParentBottom="true"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/first_time_import_key"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/first_time_import_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_download"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/first_time_create_key"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/first_time_create_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/first_time_cancel"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/first_time_skip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_horizontal" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_above="@+id/first_time_buttons">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/app_name"
+ android:drawableLeft="@drawable/ic_launcher"
+ android:drawablePadding="16dp"
+ android:gravity="center"
+ android:layout_gravity="center_horizontal" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:adjustViewBounds="true"
+ android:src="@drawable/first_time_1"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="1" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="64dp"
+ android:layout_marginRight="64dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/first_time_text1"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="16dp" />
+
+ </LinearLayout>
+
+
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/key_list_activity.xml b/OpenKeychain/src/main/res/layout/key_list_activity.xml
index c4c2c35cb..297fc526e 100644
--- a/OpenKeychain/src/main/res/layout/key_list_activity.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_activity.xml
@@ -2,9 +2,18 @@
<android.support.v4.widget.FixedDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent">
- <include layout="@layout/key_list_content"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/notify_area" />
+
+ <include layout="@layout/key_list_content" />
+
+ </LinearLayout>
<include layout="@layout/drawer_list" />
diff --git a/OpenKeychain/src/main/res/layout/key_list_item.xml b/OpenKeychain/src/main/res/layout/key_list_item.xml
index 99e4c0268..8d600464c 100644
--- a/OpenKeychain/src/main/res/layout/key_list_item.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_item.xml
@@ -38,30 +38,12 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
- <FrameLayout
- android:id="@+id/status_layout"
+ <ImageView
android:layout_width="wrap_content"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/revoked"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="@string/revoked"
- android:textColor="#e00"
- android:layout_gravity="center"
- android:padding="12dp" />
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/verified"
- android:layout_gravity="center"
- android:src="@drawable/key_certify_ok_depth0"
- android:padding="16dp" />
- </FrameLayout>
+ android:layout_height="wrap_content"
+ android:id="@+id/status_image"
+ android:layout_gravity="center"
+ android:src="@drawable/status_signature_revoked_cutout"
+ android:padding="16dp" />
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/notify_area.xml b/OpenKeychain/src/main/res/layout/notify_area.xml
index d1ba265a5..43df1cde8 100644
--- a/OpenKeychain/src/main/res/layout/notify_area.xml
+++ b/OpenKeychain/src/main/res/layout/notify_area.xml
@@ -1,12 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/emphasis"
android:orientation="vertical" />
</merge> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/qr_code_activity.xml b/OpenKeychain/src/main/res/layout/qr_code_activity.xml
new file mode 100644
index 000000000..57c869db6
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/qr_code_activity.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/qr_code_image"
+ android:padding="32dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ style="@style/SelectableItem" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml b/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml
deleted file mode 100644
index 0b58ae72f..000000000
--- a/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/share_qr_code_dialog_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:textAppearance="@android:style/TextAppearance.Medium" />
-
- <ImageView
- android:id="@+id/share_qr_code_dialog_image"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/upload_key_activity.xml b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
index 5a6f732d5..736617ba5 100644
--- a/OpenKeychain/src/main/res/layout/upload_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
@@ -1,55 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
+ <include layout="@layout/notify_area" />
+
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_key_server" />
+ android:layout_height="match_parent">
- <Spinner
- android:id="@+id/upload_key_keyserver"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_actions"
- android:layout_weight="1" />
-
- <TextView
- android:id="@+id/upload_key_action_upload"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_marginBottom="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/btn_export_to_server"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_upload"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:clickable="true"
- style="@style/SelectableItem" />
-
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_key_server" />
+
+ <Spinner
+ android:id="@+id/upload_key_keyserver"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_actions"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/upload_key_action_upload"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/btn_export_to_server"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_upload"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem" />
+
+ </LinearLayout>
+
+ </ScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml
index 481b1ddf5..b15a73c0e 100644
--- a/OpenKeychain/src/main/res/layout/view_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml
@@ -4,42 +4,38 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <LinearLayout
- android:id="@+id/card_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
+ <include layout="@layout/notify_area"/>
- <TextView
- android:layout_width="match_parent"
+ <LinearLayout
+ android:id="@+id/view_key_status_layout"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="This key is expired!"
- android:id="@+id/view_key_expired"
- android:textColor="@color/alert"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center_vertical|center_horizontal"
+ android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
- android:visibility="gone" />
+ android:orientation="horizontal">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="This key has been revoked!"
- android:id="@+id/view_key_revoked"
- android:textColor="@color/alert"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center_vertical|center_horizontal"
- android:visibility="gone"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp" />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/view_key_status_image" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/view_key_status_text"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="8dp"/>
+
+ </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:visibility="gone"
- android:id="@+id/status_divider" />
+ android:id="@+id/view_key_status_divider" />
<org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout
android:id="@+id/view_key_sliding_tab_layout"
diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
index d93420a99..b11538ba9 100644
--- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
@@ -35,20 +35,34 @@
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
- <TextView
+ <LinearLayout
android:id="@+id/view_key_action_certify"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/key_view_action_certify"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_good"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
+ android:layout_height="?android:attr/listPreferredItemHeight"
android:clickable="true"
- style="@style/SelectableItem" />
+ android:paddingRight="4dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/view_key_action_certify_text"
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:text="@string/key_view_action_certify"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+
+ <ImageView
+ android:id="@+id/view_key_action_certify_image"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/status_signature_verified_cutout"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
<TextView
style="@style/SectionHeader"
diff --git a/OpenKeychain/src/main/res/layout/view_key_keys_item.xml b/OpenKeychain/src/main/res/layout/view_key_subkey_item.xml
index 13feaf2cc..0c0a5d7e6 100644
--- a/OpenKeychain/src/main/res/layout/view_key_keys_item.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_subkey_item.xml
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
- android:paddingRight="3dip"
android:singleLine="true">
<ImageView
@@ -13,28 +12,44 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/key_small"
- android:layout_marginLeft="8dp" />
+ android:padding="8dp"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/edit_image"
+ android:src="@drawable/ic_action_edit"
+ android:padding="8dp"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
<LinearLayout
android:orientation="vertical"
- android:layout_width="fill_parent"
+ android:layout_toRightOf="@id/ic_masterKey"
+ android:layout_toLeftOf="@id/edit_image"
+ android:layout_centerVertical="true"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp">
+ android:layout_marginRight="8dp"
+ android:id="@+id/linearLayout">
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="2dip"
android:paddingTop="2dip">
<TextView
android:id="@+id/keyId"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/label_key_id"
+ android:text="0x00000000"
android:textAppearance="?android:attr/textAppearanceMedium"
android:typeface="monospace"
android:layout_weight="1" />
@@ -75,8 +90,8 @@
<LinearLayout
android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<TextView
android:id="@+id/keyDetails"
@@ -94,8 +109,9 @@
android:text="Expiry: 4/7/2016"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_gravity="right" />
+
</LinearLayout>
</LinearLayout>
-</LinearLayout>
+</RelativeLayout>
diff --git a/OpenKeychain/src/main/res/layout/view_key_userids_item.xml b/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml
index 8f036e600..157903000 100644
--- a/OpenKeychain/src/main/res/layout/view_key_userids_item.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml
@@ -24,7 +24,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/certified"
- android:src="@drawable/key_certify_ok_self"
android:layout_gravity="center_horizontal" />
</LinearLayout>
@@ -63,11 +62,10 @@
</LinearLayout>
-
<ImageView
+ android:id="@+id/edit_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:id="@+id/edit_image"
android:src="@drawable/ic_action_edit"
android:padding="8dp"
android:layout_gravity="center_horizontal" />
diff --git a/OpenKeychain/src/main/res/layout/wizard_activity.xml b/OpenKeychain/src/main/res/layout/wizard_activity.xml
deleted file mode 100644
index 299d07a76..000000000
--- a/OpenKeychain/src/main/res/layout/wizard_activity.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id="@+id/wizard_buttons"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/wizard_back"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:onClick="backOnClick"
- android:text="cancel"
- style="@style/SelectableItem" />
-
- <View
- android:layout_width="1dip"
- android:layout_height="match_parent"
- android:layout_marginBottom="4dip"
- android:layout_marginTop="4dip"
- android:background="?android:attr/listDivider" />
-
- <Button
- android:id="@+id/wizard_next"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:onClick="nextOnClick"
- android:text="next"
- style="@style/SelectableItem" />
- </LinearLayout>
-
- <View
- android:id="@+id/wizard_progress_line"
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_above="@+id/wizard_buttons"
- android:layout_marginLeft="4dip"
- android:layout_marginRight="4dip"
- android:background="?android:attr/listDivider"
- android:visibility="gone" />
-
- <LinearLayout
- android:id="@+id/wizard_progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/wizard_progress_line"
- android:visibility="gone">
-
- <ProgressBar
- android:id="@+id/wizard_progress_progressbar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <ImageView
- android:id="@+id/wizard_progress_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/icon_light_refresh" />
-
- <TextView
- android:id="@+id/wizard_progress_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="asd"
- android:textAppearance="?android:attr/textAppearanceMedium" />
- </LinearLayout>
-
- <View
- android:id="@+id/wizard_line2"
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_above="@+id/wizard_progress"
- android:layout_marginLeft="4dip"
- android:layout_marginRight="4dip"
- android:background="?android:attr/listDivider" />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@+id/wizard_line2">
-
- <LinearLayout
- android:id="@+id/wizard_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="16dp" />
- </ScrollView>
-
-</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml b/OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml
deleted file mode 100644
index 258ea7223..000000000
--- a/OpenKeychain/src/main/res/layout/wizard_create_key_fragment.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:text="Enter Full Name, Email and Passphrase!"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <AutoCompleteTextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="textPersonName"
- android:hint="Name"
- android:ems="10"
- android:id="@+id/name" />
-
- <AutoCompleteTextView
- android:id="@+id/email"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="bla@example.com"
- android:layout_weight="1"
- android:ems="10"
- android:inputType="textEmailAddress" />
-
- <EditText
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="textPassword"
- android:hint="passphrase"
- android:ems="10"
- android:id="@+id/passphrase"
- android:layout_gravity="center_horizontal" />
-
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml b/OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml
deleted file mode 100644
index 342adc37e..000000000
--- a/OpenKeychain/src/main/res/layout/wizard_k9_fragment.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <org.sufficientlysecure.htmltextview.HtmlTextView
- android:id="@+id/wizard_k9_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:text="Text..."
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <RadioGroup
- android:id="@+id/wizard_k9_radio_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <RadioButton
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:checked="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- style="@style/SelectableItem"
- android:text="install K9"
- android:id="@+id/wizard_k9_install" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <RadioButton
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceMedium"
- style="@style/SelectableItem"
- android:text="skip install"
- android:id="@+id/wizard_k9_skip" />
- </RadioGroup>
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/wizard_start_fragment.xml b/OpenKeychain/src/main/res/layout/wizard_start_fragment.xml
deleted file mode 100644
index 9e1403f74..000000000
--- a/OpenKeychain/src/main/res/layout/wizard_start_fragment.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:text="Welcome to OpenKeychain"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:text="What you wanna do today?"
- android:layout_weight="1" />
-
- <RadioGroup
- android:id="@+id/wizard_start_radio_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <RadioButton
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:checked="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- style="@style/SelectableItem"
- android:text="new key"
- android:id="@+id/wizard_start_new_key" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <RadioButton
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceMedium"
- style="@style/SelectableItem"
- android:text="import existing key"
- android:id="@+id/wizard_start_import" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider" />
-
- <RadioButton
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceMedium"
- style="@style/SelectableItem"
- android:text="skip wizard"
- android:id="@+id/wizard_start_skip" />
- </RadioGroup>
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml
index ebb7314b8..e865df182 100644
--- a/OpenKeychain/src/main/res/menu/key_list.xml
+++ b/OpenKeychain/src/main/res/menu/key_list.xml
@@ -27,11 +27,6 @@
android:title="@string/menu_create_key" />
<item
- android:id="@+id/menu_key_list_create_expert"
- app:showAsAction="never"
- android:title="@string/menu_create_key_expert" />
-
- <item
android:id="@+id/menu_key_list_debug_read"
app:showAsAction="never"
android:title="Debug / DB restore"
@@ -43,4 +38,10 @@
android:title="Debug / DB backup"
android:visible="false" />
+ <item
+ android:id="@+id/menu_key_list_debug_first_time"
+ app:showAsAction="never"
+ android:title="Debug / Show first time screen"
+ android:visible="false" />
+
</menu>
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index de6962dc9..9d9868457 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -247,8 +247,8 @@
<string name="error_only_files_are_supported">Binäre Daten ohne ohne Datei im Dateisystem werden nicht unterstützt.</string>
<string name="error_jelly_bean_needed">Android 4.1 wird benötigt um Androids NFC Beam nutzen zu können!</string>
<string name="error_nfc_needed">NFC steht auf diesem Gerät nicht zur Verfügung!</string>
- <string name="error_keyserver_insufficient_query">zu kurze Schlüsselanfrage</string>
- <string name="error_keyserver_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte verfeinern sie sie Anfrage.</string>
+ <string name="error_query_too_short">zu kurze Schlüsselanfrage</string>
+ <string name="error_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte verfeinern sie sie Anfrage.</string>
<string name="error_generic_report_bug">Ein allgemeiner Fehler trat auf, bitte schreiben Sie einen neuen Bugreport für OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">Ein Teil der geladenen Datei ist ein gültiges OpenPGP Objekt aber kein OpenPGP Schlüssel</item>
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index 50afd5c57..719ddf414 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -250,10 +250,10 @@
<string name="error_jelly_bean_needed">¡Necesita Android 4.1 para usar la característica NFC Beam (haz NFC) de Android!</string>
<string name="error_nfc_needed">¡NFC no está disponible en tu dispositivo!</string>
<string name="error_nothing_import">¡No se encontraron claves!</string>
- <string name="error_keyserver_insufficient_query">Petición de búsqueda de clave demasiado corta</string>
+ <string name="error_query_too_short">Petición de búsqueda de clave demasiado corta</string>
<string name="error_searching_keys">Error irrecuperable buscando claves en el servidor</string>
- <string name="error_keyserver_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string>
- <string name="error_import_file_no_content">El Fichero/Portapapeles está vacío</string>
+ <string name="error_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string>
+ <string name="error_import_no_valid_keys">El Fichero/Portapapeles está vacío</string>
<string name="error_generic_report_bug">Ha ocurrido un error genérico, por favor, informa de este bug a OpenKeychain</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">parte del archivo cargado es un objeto OpenPGP válido pero no una clave OpenPGP</item>
@@ -370,7 +370,7 @@
<item quantity="one">Clave%2$s importada con éxito.</item>
<item quantity="other">%1$d claves%2$s importadas con éxito.</item>
</plurals>
- <string name="import_view_log">Ver registro (log)</string>
+ <string name="view_log">Ver registro (log)</string>
<string name="import_error_nothing">No hay nada que importar.</string>
<string name="import_error">¡Error importando claves!</string>
<string name="import_with_warnings">, con advertencias</string>
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index f3627e9eb..0c5c5de67 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -250,10 +250,10 @@
<string name="error_jelly_bean_needed">Il vous faut Android 4.1 pour utiliser la fonction Beam NFC d\'Android !</string>
<string name="error_nfc_needed">La NFC n\'est pas disponible sur votre appareil !</string>
<string name="error_nothing_import">Aucune clef trouvée !</string>
- <string name="error_keyserver_insufficient_query">La requête de recherche de clef est trop courte</string>
+ <string name="error_query_too_short">La requête de recherche de clef est trop courte</string>
<string name="error_searching_keys">Erreur irrécupérable lors de la recherche de clef sur le serveur</string>
- <string name="error_keyserver_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string>
- <string name="error_import_file_no_content">Le fichier/le presse-papiers est vide</string>
+ <string name="error_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string>
+ <string name="error_import_no_valid_keys">Le fichier/le presse-papiers est vide</string>
<string name="error_generic_report_bug">Une erreur générique est survenue, veuillez créer un nouveau rapport de bogue pour OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">une partie du fichier chargé est un objet OpenPGP valide mais pas une clef OpenPGP</item>
@@ -358,7 +358,7 @@
<item quantity="one">Clef importée avec succès</item>
<item quantity="other">%1$d clefs importées avec succès</item>
</plurals>
- <string name="import_view_log">Consulter le journal</string>
+ <string name="view_log">Consulter le journal</string>
<string name="import_error_nothing">Rien à importer.</string>
<string name="import_error">Erreur lors de l\'importation des clefs !</string>
<string name="import_with_warnings">, avec des avertissements</string>
diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml
index c8ec2614d..074efb78b 100644
--- a/OpenKeychain/src/main/res/values-it/strings.xml
+++ b/OpenKeychain/src/main/res/values-it/strings.xml
@@ -250,10 +250,10 @@
<string name="error_jelly_bean_needed">Devi avere Android 4.1 per usare Android NFC Beam!</string>
<string name="error_nfc_needed">NFC non disponibile nel tuo dispositivo!</string>
<string name="error_nothing_import">Nessuna chiave trovata!</string>
- <string name="error_keyserver_insufficient_query">Chiave della query di ricerca troppo corta</string>
+ <string name="error_query_too_short">Chiave della query di ricerca troppo corta</string>
<string name="error_searching_keys">Errore irreversibile nella ricerca di chiavi sul server</string>
- <string name="error_keyserver_too_many_responses">Chiave della query di ricerca ha generato troppi candidati; Si prega di perfezionare la ricerca</string>
- <string name="error_import_file_no_content">File/Appunti vuoti</string>
+ <string name="error_too_many_responses">Chiave della query di ricerca ha generato troppi candidati; Si prega di perfezionare la ricerca</string>
+ <string name="error_import_no_valid_keys">File/Appunti vuoti</string>
<string name="error_generic_report_bug">Si è verificato un errore generico, si prega di creare una nuova segnalazione di errore per OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">parte del file caricato e\' un oggetto OpenPGP valido, ma non una chave OpenPGP</item>
@@ -354,7 +354,7 @@
<string name="import_clipboard_button">Ottieni chiave dagli appunti</string>
<string name="import_keybase_button">Ottieni chiave da Keybase.io</string>
<!--Import result toast-->
- <string name="import_view_log">Mostra registro</string>
+ <string name="view_log">Mostra registro</string>
<string name="import_error_nothing">Niente da importare</string>
<string name="import_error">Errore di importazione chiavi!</string>
<string name="import_with_warnings">, con avvisi</string>
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index 13463df25..070ee7f5a 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -246,10 +246,10 @@
<string name="error_jelly_bean_needed">Android NFC Beam機能を使ã†ã«ã¯Android 4.1 ãŒå¿…è¦ã§ã™!</string>
<string name="error_nfc_needed">ã‚ãªãŸã®ãƒ‡ãƒã‚¤ã‚¹ã«ã¯NFCãŒå­˜åœ¨ã—ã¾ã›ã‚“!</string>
<string name="error_nothing_import">éµãŒè¦‹å½“ã‚Šã¾ã›ã‚“!</string>
- <string name="error_keyserver_insufficient_query">éµæ¤œç´¢ã®ã‚¯ã‚¨ãƒªãŒçŸ­ã‹ã™ãŽã¾ã™</string>
+ <string name="error_query_too_short">éµæ¤œç´¢ã®ã‚¯ã‚¨ãƒªãŒçŸ­ã‹ã™ãŽã¾ã™</string>
<string name="error_searching_keys">サーãƒã§ã®éµã®æ¤œç´¢ãŒå›žå¾©ä¸å¯èƒ½ãªã‚¨ãƒ©ãƒ¼ã«ãªã‚Šã¾ã—ãŸ</string>
- <string name="error_keyserver_too_many_responses">éµæ¤œç´¢ã®ã‚¯ã‚¨ãƒªãŒæ²¢å±±ã®å€™è£œã‚’è¿”ã—ã¾ã—ãŸ; クエリを精密化ã—ã¦ãã ã•ã„</string>
- <string name="error_import_file_no_content">ファイル/クリップボードãŒç©ºã§ã™</string>
+ <string name="error_too_many_responses">éµæ¤œç´¢ã®ã‚¯ã‚¨ãƒªãŒæ²¢å±±ã®å€™è£œã‚’è¿”ã—ã¾ã—ãŸ; クエリを精密化ã—ã¦ãã ã•ã„</string>
+ <string name="error_import_no_valid_keys">ファイル/クリップボードãŒç©ºã§ã™</string>
<string name="error_generic_report_bug">一般エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€ã“ã®æ–°ã—ã„ãƒã‚°ã®æƒ…報をOpenKeychainプロジェクトã«é€ã£ã¦ãã ã•ã„</string>
<plurals name="error_import_non_pgp_part">
<item quantity="other">読ã¿è¾¼ã‚“ã ãƒ•ã‚¡ã‚¤ãƒ«ã®OpenPGPオブジェクト部分ã¯æ­£ã—ã„ã§ã™ãŒã€OpenPGPã®éµã§ã¯ã‚ã‚Šã¾ã›ã‚“</item>
@@ -358,7 +358,7 @@
<plurals name="import_keys_updated">
<item quantity="other">%1$d ã®éµ%2$sã®ã‚¢ãƒƒãƒ—デートã«æˆåŠŸã€‚</item>
</plurals>
- <string name="import_view_log">ログを見る</string>
+ <string name="view_log">ログを見る</string>
<string name="import_error_nothing">インãƒãƒ¼ãƒˆã™ã‚‹ã‚‚ã®ãŒã‚ã‚Šã¾ã›ã‚“。</string>
<string name="import_error">éµã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆã®ã‚¨ãƒ©ãƒ¼!</string>
<string name="import_with_warnings">ã€ã¨ãƒ¯ãƒ¼ãƒ‹ãƒ³ã‚°</string>
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index 539a431b5..1e697c8db 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -254,10 +254,10 @@
<string name="error_jelly_bean_needed">Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ NFC Beam требуетÑÑ Android 4.1+ !</string>
<string name="error_nfc_needed">Ваше уÑтройÑтво не поддерживает NFC!</string>
<string name="error_nothing_import">Ключи не найдены!</string>
- <string name="error_keyserver_insufficient_query">Ð—Ð°Ð¿Ñ€Ð¾Ñ Ñлишком короткий</string>
+ <string name="error_query_too_short">Ð—Ð°Ð¿Ñ€Ð¾Ñ Ñлишком короткий</string>
<string name="error_searching_keys">Ошибка поиÑка ключей на Ñервере</string>
- <string name="error_keyserver_too_many_responses">ПоиÑк ключа вернул Ñлишком много вариантов; ПожалуйÑта, уточните запроÑ</string>
- <string name="error_import_file_no_content">Файл/Буфер пуÑÑ‚</string>
+ <string name="error_too_many_responses">ПоиÑк ключа вернул Ñлишком много вариантов; ПожалуйÑта, уточните запроÑ</string>
+ <string name="error_import_no_valid_keys">Файл/Буфер пуÑÑ‚</string>
<string name="error_generic_report_bug">Ð’Ñ‹Ñвлена ошибка. ПожалуйÑта, Ñообщите о ней разработчику.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">чаÑÑ‚ÑŒ загруженного файла Ñодержит данные OpenPGP, но Ñто не ключ</item>
@@ -371,7 +371,7 @@
<item quantity="few">и обновлено %1$d ключей%2$s.</item>
<item quantity="other">и обновлено %1$d ключей%2$s.</item>
</plurals>
- <string name="import_view_log">Смотреть журнал</string>
+ <string name="view_log">Смотреть журнал</string>
<string name="import_error_nothing">Ðет данных Ð´Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð°.</string>
<string name="import_error">Ошибка импорта ключей!</string>
<string name="import_with_warnings">, Ñ Ð¿Ñ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñми</string>
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index 61fe6ed86..b9a0c4cb9 100644
--- a/OpenKeychain/src/main/res/values-sl/strings.xml
+++ b/OpenKeychain/src/main/res/values-sl/strings.xml
@@ -254,9 +254,9 @@
<string name="error_only_files_are_supported">Neposredni binarni podatki brez dejanske datoteke v datoteÄnem sistemu niso podprti.</string>
<string name="error_jelly_bean_needed">Za uporabo storitve NFC Beam potrebujete najmanj Android 4.1!</string>
<string name="error_nfc_needed">NFC ni na voljo na vaši napravi!</string>
- <string name="error_keyserver_insufficient_query">Iskalni pojem je prekratek</string>
+ <string name="error_query_too_short">Iskalni pojem je prekratek</string>
<string name="error_searching_keys">Nepremostljiva napaka pri iskanju kljuÄev na strežniku</string>
- <string name="error_keyserver_too_many_responses">Iskanje kljuÄev je vrnilo preveÄ zadetkov; prosimo redefinirajte iskalni pojem</string>
+ <string name="error_too_many_responses">Iskanje kljuÄev je vrnilo preveÄ zadetkov; prosimo redefinirajte iskalni pojem</string>
<string name="error_generic_report_bug">Pripetila se je sploÅ¡na napaka, prosimo ustvarite poroÄilo o \'hroÅ¡Äu\'.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">Del naložene datoteke je veljavnen objekt OpenPGP a ni kljuÄ.</item>
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index 6f5cc7cf1..4e267e431 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -252,9 +252,9 @@
<string name="error_jelly_bean_needed">Вам потрібний Android 4.1 Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ— Androids NFC промінь!</string>
<string name="error_nfc_needed">NFC недоÑтупний на вашому приÑтрої!</string>
<string name="error_nothing_import">Ключ не знайдено!</string>
- <string name="error_keyserver_insufficient_query">Запит пошуку ключа надто короткий</string>
+ <string name="error_query_too_short">Запит пошуку ключа надто короткий</string>
<string name="error_searching_keys">Ðевиправна помилка пошуку ключів в Ñервері</string>
- <string name="error_keyserver_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string>
+ <string name="error_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string>
<string name="error_generic_report_bug">ТрапилаÑÑ Ð·Ð°Ð³Ð°Ð»ÑŒÐ½Ð° помилка, будь лаÑка, Ñтворіть новий звіт про помилку Ð´Ð»Ñ OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">чаÑтина завантаженого файлу Ñ” вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
@@ -352,7 +352,7 @@
<string name="import_clipboard_button">Отримати ключ з буфера обміну</string>
<string name="import_keybase_button">Отримати ключ із Keybase.io</string>
<!--Import result toast-->
- <string name="import_view_log">ПереглÑнути журнал</string>
+ <string name="view_log">ПереглÑнути журнал</string>
<string name="import_error_nothing">Ðема що імпортувати.</string>
<string name="import_error">Помилка імпорту ключів!</string>
<!--Intent labels-->
diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml
index c0042d215..a21f949d1 100644
--- a/OpenKeychain/src/main/res/values/colors.xml
+++ b/OpenKeychain/src/main/res/values/colors.xml
@@ -5,12 +5,19 @@
<color name="emphasis_dark">#9933cc</color>
<color name="bg_gray">#cecbce</color>
<color name="tertiary_text_light">#808080</color>
+ <color name="alert">#ffdd3333</color>
<color name="holo_gray_light">#33999999</color>
<color name="holo_gray_bright">#33CCCCCC</color>
- <color name="result_red">#ffff4444</color>
- <color name="result_orange">#ffffbb33</color>
- <color name="result_green">#ff99cc00</color>
- <color name="result_purple">#aa66cc</color>
+ <!-- http://developer.android.com/design/style/color.html -->
+ <color name="android_red_light">#ffff4444</color>
+ <color name="android_red_dark">#ffCC0000</color>
+ <color name="android_orange_light">#ffffbb33</color>
+ <color name="android_orange_dark">#ffFF8800</color>
+ <color name="android_green_light">#ff99cc00</color>
+ <color name="android_green_dark">#ff669900</color>
+ <color name="android_purple_light">#ffaa66cc</color>
+ <color name="android_purple_dark">#ff9933CC</color>
+
</resources>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index f6c065c42..101a9fa0a 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -9,7 +9,6 @@
<string name="title_authentication">Passphrase</string>
<string name="title_create_key">Create Key</string>
<string name="title_edit_key">Edit Key</string>
- <string name="title_wizard">Welcome to OpenKeychain</string>
<string name="title_preferences">Preferences</string>
<string name="title_api_registered_apps">Apps</string>
<string name="title_key_server_preference">Keyserver Preference</string>
@@ -110,9 +109,11 @@
<string name="label_file_colon">File:</string>
<string name="label_no_passphrase">No Passphrase</string>
<string name="label_passphrase">Passphrase</string>
- <string name="label_passphrase_again">Again</string>
+ <string name="label_passphrase_again">Repeat Passphrase</string>
<string name="label_algorithm">Algorithm</string>
<string name="label_ascii_armor">ASCII Armor</string>
+ <string name="label_conceal_pgp_application">Let others know that you\'re using OpenKeychain</string>
+ <string name="label_conceal_pgp_application_summary">Writes \'OpenKeychain v2.7\' to OpenPGP signatures, ciphertext, and exported keys</string>
<string name="label_select_public_keys">Recipients</string>
<string name="label_to">To</string>
<string name="label_delete_after_encryption">Delete After Encryption</string>
@@ -275,10 +276,12 @@
<string name="error_jelly_bean_needed">You need Android 4.1 to use Android\'s NFC Beam feature!</string>
<string name="error_nfc_needed">NFC is not available on your device!</string>
<string name="error_nothing_import">No keys found!</string>
- <string name="error_keyserver_insufficient_query">Key search query too short</string>
- <string name="error_searching_keys">Unrecoverable error searching for keys at server</string>
- <string name="error_keyserver_too_many_responses">Key search query returned too many candidates; Please refine query</string>
- <string name="error_import_file_no_content">File/Clipboard is empty</string>
+ <string name="error_query_too_short">Search query too short. Please refine your query!</string>
+ <string name="error_searching_keys">An error occurred when searching for keys.</string>
+ <string name="error_too_many_responses">Key search query returned too many candidates. Please refine your query!</string>
+ <string name="error_too_short_or_too_many_responses">Either no keys or too many have been found. Please improve your query!</string>
+
+ <string name="error_import_no_valid_keys">No valid keys found in File/Clipboard!</string>
<string name="error_generic_report_bug">A generic error occurred, please create a new bug report for OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">part of the loaded file is a valid OpenPGP object but not a OpenPGP key</item>
@@ -307,6 +310,20 @@
<string name="progress_building_master_key">building master ring…</string>
<string name="progress_adding_sub_keys">adding sub keys…</string>
<string name="progress_saving_key_ring">saving key…</string>
+ <string name="progress_generating_rsa">generating new RSA key…</string>
+ <string name="progress_generating_dsa">generating new DSA key…</string>
+ <string name="progress_generating_elgamal">generating new ElGamal key…</string>
+
+ <string name="progress_modify">modifying keyring…</string>
+
+ <string name="progress_modify_unlock">unlocking keyring…</string>
+ <string name="progress_modify_adduid">adding user ids…</string>
+ <string name="progress_modify_revokeuid">revoking user ids…</string>
+ <string name="progress_modify_primaryuid">changing primary user id…</string>
+ <string name="progress_modify_subkeychange">modifying subkeys…</string>
+ <string name="progress_modify_subkeyrevoke">revoking subkeys…</string>
+ <string name="progress_modify_subkeyadd">adding subkeys…</string>
+ <string name="progress_modify_passphrase">changing passphrase…</string>
<plurals name="progress_exporting_key">
<item quantity="one">exporting key…</item>
@@ -397,8 +414,8 @@
<item quantity="other">Successfully imported %1$d keys</item>
</plurals>
<plurals name="import_keys_added_and_updated_2">
- <item quantity="one"> and updated key%2$s.</item>
- <item quantity="other"> and updated %1$d keys%2$s.</item>
+ <item quantity="one">and updated key%2$s.</item>
+ <item quantity="other">and updated %1$d keys%2$s.</item>
</plurals>
<plurals name="import_keys_added">
<item quantity="one">Successfully imported key%2$s.</item>
@@ -408,7 +425,7 @@
<item quantity="one">Successfully updated key%2$s.</item>
<item quantity="other">Successfully updated %1$d keys%2$s.</item>
</plurals>
- <string name="import_view_log">View Log</string>
+ <string name="view_log">View Log</string>
<string name="import_error_nothing">Nothing to import.</string>
<string name="import_error">Error importing keys!</string>
<string name="import_with_warnings">, with warnings</string>
@@ -486,6 +503,23 @@
<item>Change to Primary Identity</item>
<item>Revoke Identity</item>
</string-array>
+ <string name="edit_key_edit_subkey_title">Select an action!</string>
+ <string-array name="edit_key_edit_subkey">
+ <item>Change Expiry</item>
+ <item>Revoke Subkey</item>
+ </string-array>
+
+ <!-- Create key -->
+ <string name="create_key_text">Enter Full Name, Email and a Passphrase.</string>
+ <string name="create_key_upload">Upload key to keyserver</string>
+ <string name="create_key_empty">This field is required</string>
+ <string name="create_key_passphrases_not_equal">Passphrases do not match</string>
+ <string name="create_key_final_text">You entered the following credentials:</string>
+ <string name="create_key_final_robot_text">Creating a key may take a while, have a cup of coffee in the meantime…\n(3 subkeys, RSA, 4096 bit)</string>
+
+ <!-- View key -->
+ <string name="view_key_revoked">This key has been revoked!</string>
+ <string name="view_key_expired">This key is expired!</string>
<!-- Navigation Drawer -->
<string name="nav_keys">Keys</string>
@@ -511,7 +545,7 @@
<string name="cert_casual">casual</string>
<string name="cert_positive">positive</string>
<string name="cert_revoke">revoked</string>
- <string name="cert_verify_ok">ok</string>
+ <string name="cert_verify_ok">OK</string>
<string name="cert_verify_failed">failed!</string>
<string name="cert_verify_error">error!</string>
<string name="cert_verify_unavailable">key unavailable</string>
@@ -556,7 +590,7 @@
<string name="msg_ip_reinsert_secret">Re-inserting secret key</string>
<string name="msg_ip_uid_cert_bad">Encountered bad certificate!</string>
<string name="msg_ip_uid_cert_error">Error processing certificate!</string>
- <string name="msg_ip_uid_cert_good">User id is certified by %1$s (%2$s)</string>
+ <string name="msg_ip_uid_cert_good">User id is certified by %1$s</string>
<plurals name="msg_ip_uid_certs_unknown">
<item quantity="one">Ignoring one certificate issued by an unknown public key</item>
<item quantity="other">Ignoring %s certificates issued by unknown public keys</item>
@@ -570,6 +604,7 @@
<string name="msg_ip_uid_processing">Processing user id %s</string>
<string name="msg_ip_uid_revoked">User id is revoked</string>
<string name="msg_is_bad_type_public">Tried to import public keyring as secret. This is a bug, please file a report!</string>
+ <string name="msg_is_bad_type_uncanon">Tried to import a keyring without canonicalization. This is a bug, please file a report!</string>
<!-- Import Secret log entries -->
<string name="msg_is">Importing secret key %s</string>
@@ -592,6 +627,7 @@
<string name="msg_kc_revoke_bad_local">Removing keyring revocation certificate with "local" flag</string>
<string name="msg_kc_revoke_bad_time">Removing keyring revocation certificate with future timestamp</string>
<string name="msg_kc_revoke_bad_type">Removing master key certificate of unknown type (%s)</string>
+ <string name="msg_kc_revoke_bad_type_uid">Removing user id certification in bad position</string>
<string name="msg_kc_revoke_bad">Removing bad keyring revocation certificate</string>
<string name="msg_kc_revoke_dup">Removing redundant keyring revocation certificate</string>
<string name="msg_kc_sub">Processing subkey %s</string>
@@ -629,6 +665,7 @@
<string name="msg_kc_uid_revoke_dup">Removing redundant revocation certificate for user id "%s"</string>
<string name="msg_kc_uid_revoke_old">Removing outdated revocation certificate for user id "%s"</string>
<string name="msg_kc_uid_no_cert">No valid self-certificate found for user id %s, removing from ring</string>
+ <string name="msg_kc_uid_remove">Removing invalid user id %s</string>
<!-- Keyring merging log entries -->
<string name="msg_mg_public">Merging into public keyring %s</string>
@@ -637,6 +674,17 @@
<string name="msg_mg_heterogeneous">Tried to consolidate heterogeneous keyrings</string>
<string name="msg_mg_new_subkey">Adding new subkey %s</string>
<string name="msg_mg_found_new">Found %s new certificates in keyring</string>
+ <string name="msg_mg_unchanged">No new certificates</string>
+
+ <!-- createSecretKeyRing -->
+ <string name="msg_cr">Generating new master key</string>
+ <string name="msg_cr_error_no_master">No master key options specified!</string>
+ <string name="msg_cr_error_no_user_id">Keyrings must be created with at least one user id!</string>
+ <string name="msg_cr_error_no_certify">Master key must have certify flag!</string>
+ <string name="msg_cr_error_keysize_512">Key size must be greater or equal 512!</string>
+ <string name="msg_cr_error_internal_pgp">Internal PGP error!</string>
+ <string name="msg_cr_error_unknown_algo">Bad algorithm choice!</string>
+ <string name="msg_cr_error_master_elgamal">Master key must not be of type ElGamal!</string>
<!-- modifySecretKeyRing -->
<string name="msg_mr">Modifying keyring %s</string>
@@ -644,10 +692,13 @@
<string name="msg_mf_error_fingerprint">Actual key fingerprint does not match the expected one!</string>
<string name="msg_mf_error_keyid">No key ID. This is an internal error, please file a bug report!</string>
<string name="msg_mf_error_integrity">Internal error, integrity check failed!</string>
+ <string name="msg_mf_error_noexist_primary">Bad primary user id specified!</string>
<string name="msg_mf_error_revoked_primary">Revoked user ids cannot be primary!</string>
<string name="msg_mf_error_pgp">PGP internal exception!</string>
<string name="msg_mf_error_sig">Signature exception!</string>
<string name="msg_mf_passphrase">Changing passphrase</string>
+ <string name="msg_mf_primary_replace_old">Replacing certificate of previous primary user id</string>
+ <string name="msg_mf_primary_new">Generating new certificate for new primary user id</string>
<string name="msg_mf_subkey_change">Modifying subkey %s</string>
<string name="msg_mf_subkey_missing">Tried to operate on missing subkey %s!</string>
<string name="msg_mf_subkey_new">Generating new %1$s bit %2$s subkey</string>
@@ -658,10 +709,19 @@
<string name="msg_mf_uid_add">Adding user id %s</string>
<string name="msg_mf_uid_primary">Changing primary uid to %s</string>
<string name="msg_mf_uid_revoke">Revoking user id %s</string>
+ <string name="msg_mf_uid_error_empty">User ID must not be empty!</string>
<string name="msg_mf_unlock_error">Error unlocking keyring!</string>
<string name="msg_mf_unlock">Unlocking keyring</string>
+ <!-- PassphraseCache -->
+ <string name="passp_cache_notif_click_to_clear">Click to clear cached passphrases</string>
+ <string name="passp_cache_notif_n_keys">OpenKeychain has cached %d passphrases</string>
+ <string name="passp_cache_notif_keys">Cached Passphrases:</string>
+ <string name="passp_cache_notif_clear">Clear Cache</string>
+ <string name="passp_cache_notif_pwd">Password</string>
+
<!-- unsorted -->
+ <string name="internal_error">Internal error!</string>
<string name="section_certifier_id">Certifier</string>
<string name="section_cert">Certificate Details</string>
<string name="label_user_id">Identity</string>
@@ -684,4 +744,10 @@
<string name="info_no_manual_account_creation">Do not create OpenKeychain-Accounts manually.\nFor more information, see Help.</string>
<string name="contact_show_key">Show key (%s)</string>
+ <!-- First Time -->
+ <string name="first_time_text1">Take back your privacy with OpenKeychain!</string>
+ <string name="first_time_create_key">Create Key</string>
+ <string name="first_time_import_key">Import Key</string>
+ <string name="first_time_skip">Skip Setup</string>
+
</resources>
diff --git a/OpenKeychain/src/main/res/xml/adv_preferences.xml b/OpenKeychain/src/main/res/xml/adv_preferences.xml
index fa3974199..a07ae06bb 100644
--- a/OpenKeychain/src/main/res/xml/adv_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/adv_preferences.xml
@@ -38,11 +38,17 @@
android:key="defaultAsciiArmor"
android:persistent="false"
android:title="@string/label_ascii_armor" />
+
+ <CheckBoxPreference
+ android:key="concealPgpApplication"
+ android:persistent="false"
+ android:title="@string/label_conceal_pgp_application"
+ android:summary="@string/label_conceal_pgp_application_summary" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/section_advanced" >
<CheckBoxPreference
android:key="forceV3Signatures"
android:persistent="false"
- android:title="@string/label_force_v3_signature" />
+ android:title="@string/label_force_v3_signature"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/OpenKeychain/src/test/resources/extern/OpenPGP-Haskell b/OpenKeychain/src/test/resources/extern/OpenPGP-Haskell
deleted file mode 160000
-Subproject eba7e4fdce3de6622b4ec3862b405b0acd01637
diff --git a/README.md b/README.md
index 9a80eaf07..509c57e63 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,11 @@ Select everything for the newest SDK Platform (API-Level 19)
5. Execute ``./gradlew build``
6. You can install the app with ``adb install -r OpenKeychain/build/outputs/apk/OpenKeychain-debug-unaligned.apk``
+### Run Tests
+1. Use OpenJDK instead of Oracle JDK
+2. Execute ``./prepare-tests.sh``
+3. Execute ``./gradlew build``
+
### Build API Demo with Gradle
1. Follow 1-4 from above
@@ -48,13 +53,10 @@ Select everything for the newest SDK Platform (API-Level 19)
### Development with Android Studio
-I am using the newest [Android Studio](http://developer.android.com/sdk/installing/studio.html) for development. Development with Eclipse is currently not possible because I am using the new [project structure](http://developer.android.com/sdk/installing/studio-tips.html).
+We are using the newest [Android Studio](http://developer.android.com/sdk/installing/studio.html) for development. Development with Eclipse is currently not possible because we are using the new [project structure](http://developer.android.com/sdk/installing/studio-tips.html).
-1. Clone the project from github
-2. From Android Studio: File -> Import Project -> ...
- * Select the cloned top folder if you want to develop on the main project
- * Select the "OpenKeychain-API" folder if you want to develop on the API example
-3. Import project from external model -> choose Gradle
+1. Clone the project from Github
+2. From Android Studio: File -> Import Project -> Select the cloned top folder
## OpenKeychain's API
@@ -219,10 +221,6 @@ Some parts and some libraries are Apache License v2, MIT X11 License (see below)
https://github.com/Bearded-Hen/Android-Bootstrap
MIT License
-* Android AppMsg
- https://github.com/johnkil/Android-AppMsg
- Apache License v2
-
### Images
* icon.svg
modified version of kgpg_key2_kopete.svgz
diff --git a/Resources/graphics/create_key_robot.svg b/Resources/graphics/create_key_robot.svg
new file mode 100644
index 000000000..7301dc5bb
--- /dev/null
+++ b/Resources/graphics/create_key_robot.svg
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="45"
+ height="45"
+ id="svg4316"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="create_key_robot.svg">
+ <defs
+ id="defs4318" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6"
+ inkscape:cx="19.859075"
+ inkscape:cy="10.23901"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2558"
+ inkscape:window-height="1419"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4321">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1007.3622)">
+ <g
+ id="g3761">
+ <path
+ id="path5399"
+ d="m 22.166071,1009.3123 c -5.613738,0 -10.131173,4.5175 -10.131173,10.1312 l 0,18.6956 c 0,5.6138 4.517435,10.1365 10.131173,10.1365 l 0.6677,0 c 5.613732,0 10.131173,-4.5227 10.131173,-10.1365 l 0,-18.6956 c 0,-5.6137 -4.517441,-10.1312 -10.131173,-10.1312 l -0.6677,0 z m 0.336476,3.3649 c 4.044517,0 7.223786,3.1791 7.223786,7.1291 l 0,14.9628 c 0,3.95 -3.179269,7.1292 -7.223786,7.1292 -4.044516,0 -7.229038,-3.1792 -7.229038,-7.1292 l 0,-14.9628 c 0,-3.95 3.184522,-7.1291 7.229038,-7.1291 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:1.74663651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:1.74663651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5401"
+ width="33.961983"
+ height="24.945528"
+ x="5.5190063"
+ y="1025.4666"
+ rx="0.79848224"
+ ry="0.79848224" />
+ <path
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:1.74663651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 11.109664,1043.5389 c 2.495006,2.065 6.666898,3.4226 11.392966,3.4226 4.725665,0 8.89263,-1.3579 11.387713,-3.4226 z"
+ id="path5403"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscc" />
+ <path
+ sodipodi:type="arc"
+ style="color:#000000;fill:none;stroke:#669900;stroke-width:9.60078144;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path5405"
+ sodipodi:cx="307.48431"
+ sodipodi:cy="407.66223"
+ sodipodi:rx="9.388834"
+ sodipodi:ry="9.388834"
+ d="m 316.87315,407.66223 a 9.388834,9.388834 0 1 1 -18.77767,0"
+ transform="matrix(0.1819265,0,0,0.1819265,-43.957774,964.33702)"
+ sodipodi:start="0"
+ sodipodi:end="3.1415927"
+ sodipodi:open="true" />
+ <path
+ d="m 316.87315,407.66223 a 9.388834,9.388834 0 1 1 -18.77767,0 9.388834,9.388834 0 1 1 18.77767,0 z"
+ sodipodi:ry="9.388834"
+ sodipodi:rx="9.388834"
+ sodipodi:cy="407.66223"
+ sodipodi:cx="307.48431"
+ id="path5407"
+ style="color:#000000;fill:#669900;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:9.60078144000000044;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:type="arc"
+ transform="matrix(0.1819265,0,0,0.1819265,-21.704758,965.01923)" />
+ </g>
+ </g>
+</svg>
diff --git a/Resources/graphics/first_time_1.png b/Resources/graphics/first_time_1.png
new file mode 100644
index 000000000..1f340df5c
--- /dev/null
+++ b/Resources/graphics/first_time_1.png
Binary files differ
diff --git a/Resources/graphics/first_time_1.svg b/Resources/graphics/first_time_1.svg
new file mode 100644
index 000000000..1f40c5ff3
--- /dev/null
+++ b/Resources/graphics/first_time_1.svg
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg4325"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="ok_start.svg">
+ <defs
+ id="defs4327" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="405.16912"
+ inkscape:cy="246.12576"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2558"
+ inkscape:window-height="1419"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4330">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-540.36218)">
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.59999999999999998;fill:#9933cc;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path4501"
+ sodipodi:cx="227.53687"
+ sodipodi:cy="246.58241"
+ sodipodi:rx="225.51656"
+ sodipodi:ry="225.51656"
+ d="m 453.05342,246.58241 a 225.51656,225.51656 0 1 1 -451.0331106,0 225.51656,225.51656 0 1 1 451.0331106,0 z"
+ transform="translate(28.463135,549.77977)" />
+ <path
+ sodipodi:type="star"
+ style="color:#000000;fill:#ffffff;fill-opacity:0.16256157;fill-rule:nonzero;stroke:none;stroke-width:1.76498926;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path7021"
+ sodipodi:sides="25"
+ sodipodi:cx="237.45834"
+ sodipodi:cy="671.60583"
+ sodipodi:r1="60.973591"
+ sodipodi:r2="37.364418"
+ sodipodi:arg1="0.87061839"
+ sodipodi:arg2="0.99902889"
+ inkscape:flatsided="false"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="m 276.74691,718.23402 -19.06996,-15.20673 6.23968,23.51249 -14.68908,-19.47149 0.19632,24.32554 -9.38523,-22.51278 -5.85936,23.61014 -3.49167,-24.13951 -11.54688,21.41122 2.62127,-24.24948 -16.50886,17.86696 8.56952,-22.83575 -20.43354,13.20005 13.97931,-19.98717 -23.0743,7.70373 18.51073,-15.88272 -24.26522,1.72335 21.87906,-10.7803 -23.93146,-4.3653 23.87263,-5.00052 -22.094,-10.17967 24.36621,1.09346 -18.8683,-15.35441 23.32877,7.11874 -14.45703,-19.56438 20.8255,12.69672 -9.13738,-22.54504 17.01368,17.47692 -3.24358,-24.10913 12.13283,21.15898 2.85402,-24.15834 6.48963,23.51155 8.77228,-22.6896 0.43867,24.38679 14.13936,-19.79518 -5.63987,23.72973 18.61801,-15.65696 -11.36402,21.58163 21.92682,-10.53495 -16.37414,18.07749 23.85789,-4.75101 -20.35541,13.43747 24.28988,1.33147 -23.05767,7.95313 23.19564,7.33029 -24.31112,1.96905 20.64393,12.86851 -24.03703,-4.13873 16.7951,17.59816 -22.2526,-9.98648 z"
+ transform="matrix(2.3346891,0,0,2.3346891,-297.98143,-788.87687)"
+ inkscape:transform-center-x="0.48070196"
+ inkscape:transform-center-y="0.080663394" />
+ <rect
+ ry="14.623538"
+ rx="14.623538"
+ y="778.16095"
+ x="189.31395"
+ height="91.847595"
+ width="133.37183"
+ id="rect3529"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.12070131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.12070131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 254.33815,626.00818 c -27.93766,0 -50.4195,22.48185 -50.4195,50.41951 l 0,93.04182 c 0,27.93768 22.48184,50.44562 50.4195,50.44562 l 3.3229,0 c 27.93773,0 50.4195,-22.50794 50.4195,-50.44562 l 0,-93.04182 c 0,-27.93766 -22.48177,-50.41951 -50.4195,-50.41951 l -3.3229,0 z m 1.67456,16.74544 c 20.12817,0 35.95034,15.82142 35.95034,35.47939 l 0,74.46486 c 0,19.65806 -15.82217,35.47941 -35.95034,35.47941 -20.1282,0 -35.97658,-15.82135 -35.97658,-35.47941 l 0,-74.46486 c 0,-19.65797 15.84838,-35.47939 35.97658,-35.47939 z"
+ id="path3531" />
+ <rect
+ ry="3.9737797"
+ rx="3.9737797"
+ y="706.40234"
+ x="171.49129"
+ height="124.14557"
+ width="169.01746"
+ id="rect3533"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.12070131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cscc"
+ inkscape:connector-curvature="0"
+ id="path3535"
+ d="m 199.31408,796.34234 c 12.41682,10.27683 33.179,17.03328 56.69903,17.03328 23.51804,0 44.25559,-6.75787 56.67285,-17.03328 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.12070131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ transform="matrix(0.90538746,0,0,0.90538746,-76.624431,405.57604)"
+ d="m 316.87315,407.66223 a 9.388834,9.388834 0 1 1 -18.77767,0 9.388834,9.388834 0 1 1 18.77767,0 z"
+ sodipodi:ry="9.388834"
+ sodipodi:rx="9.388834"
+ sodipodi:cy="407.66223"
+ sodipodi:cx="307.48431"
+ id="path3537"
+ style="color:#000000;fill:#4b4f2f;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.55131245;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(0.90538746,0,0,0.90538746,36.007648,405.57604)"
+ sodipodi:type="arc"
+ style="color:#000000;fill:#4b4f2f;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.55131245;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3539"
+ sodipodi:cx="307.48431"
+ sodipodi:cy="407.66223"
+ sodipodi:rx="9.388834"
+ sodipodi:ry="9.388834"
+ d="m 316.87315,407.66223 a 9.388834,9.388834 0 1 1 -18.77767,0 9.388834,9.388834 0 1 1 18.77767,0 z" />
+ <g
+ id="g10754"
+ transform="matrix(0.83727181,0,0,0.83727181,829.92363,-416.95697)"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <g
+ id="g5737-8"
+ transform="translate(-898.12108,631.20806)"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5739-5"
+ width="29.344997"
+ height="96.371902"
+ x="153.20558"
+ y="918.29181"
+ rx="0.84849852"
+ ry="0.95320696" />
+ <path
+ sodipodi:nodetypes="cscc"
+ inkscape:connector-curvature="0"
+ id="path5741-6"
+ d="m 192.11794,916.83294 c -7.43536,-12.87842 -23.7891,-17.26042 -36.66754,-9.82505 -12.87845,7.43537 -17.26039,23.78913 -9.82503,36.66755 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="3"
+ rx="3"
+ y="1010.0776"
+ x="133.00574"
+ height="11.311689"
+ width="69.74469"
+ id="rect5743-2"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 145.00574,916.83294 c 7.43536,-12.87842 23.7891,-17.26042 36.66754,-9.82505 12.87845,7.43537 17.26039,23.78913 9.82503,36.66755 z"
+ id="path5745-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscc" />
+ <rect
+ ry="7.1440544"
+ rx="7.1440544"
+ y="968.78937"
+ x="145.20558"
+ height="14.288109"
+ width="45.344997"
+ id="rect5747-2"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g5749-5"
+ transform="matrix(-1,0,0,1,-472.81628,631.20806)"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <rect
+ ry="0.95320696"
+ rx="0.84849852"
+ y="918.29181"
+ x="153.20558"
+ height="96.371902"
+ width="29.344997"
+ id="rect5751-5"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 192.11794,916.83294 c -7.43536,-12.87842 -23.7891,-17.26042 -36.66754,-9.82505 -12.87845,7.43537 -17.26039,23.78913 -9.82503,36.66755 z"
+ id="path5753-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscc" />
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5755-0"
+ width="69.74469"
+ height="11.311689"
+ x="133.00574"
+ y="1010.0776"
+ rx="3"
+ ry="3" />
+ <path
+ sodipodi:nodetypes="cscc"
+ inkscape:connector-curvature="0"
+ id="path5757-1"
+ d="m 145.00574,916.83294 c 7.43536,-12.87842 23.7891,-17.26042 36.66754,-9.82505 12.87845,7.43537 17.26039,23.78913 9.82503,36.66755 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5759-7"
+ width="45.344997"
+ height="14.288109"
+ x="145.20558"
+ y="968.78937"
+ rx="7.1440544"
+ ry="7.1440544" />
+ </g>
+ </g>
+ <g
+ transform="matrix(0.83727181,0,0,0.83727181,84.650265,61.513651)"
+ id="g5697-5"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path5699-6"
+ d="m 326.98547,847.70808 57.45819,0 0,-73.83379 -10,0 0,61.83379 -47.45819,0"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5703-1"
+ width="9.4107542"
+ height="42.249756"
+ x="305.50339"
+ y="821.53503"
+ rx="4.7053771"
+ ry="4.7053771" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 390.56742,727.10519 c -6.09957,0 -11,4.90043 -11,11 l 0,10.15625 -10.15625,0 c -6.09957,0 -11,4.90043 -11,11 0,6.09957 4.90043,11 11,11 l 21.15625,0 c 6.09957,0 11,-4.90043 11,-11 l 0,-21.15625 c 0,-6.09957 -4.90043,-11 -11,-11 z"
+ id="path5705-8" />
+ <rect
+ ry="2.1935852"
+ rx="4.0765176"
+ y="771.6814"
+ x="369.11923"
+ height="12.20938"
+ width="21.740107"
+ id="rect5707-1"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="2.9156427"
+ rx="7.0489321"
+ y="829.57007"
+ x="318.22556"
+ height="26.179665"
+ width="14.097864"
+ id="rect5709-1"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <g
+ id="g5711-2"
+ transform="translate(-34.057657,-0.65450616)"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5713-1"
+ d="m 404.77705,825.97291 -9.16957,15.86559 9.16957,15.84038 18.38979,0 9.19491,-15.84038 -9.19491,-15.86559 -18.38979,0 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5715-3"
+ d="m 413.97195,836.00644 c 3.23493,0 5.85942,2.61224 5.85942,5.83206 0,3.21983 -2.62449,5.83207 -5.85942,5.83207 -3.23494,0 -5.85943,-2.61224 -5.85943,-5.83207 0,-3.21982 2.62449,-5.83206 5.85943,-5.83206 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ </g>
+ <g
+ transform="matrix(-0.83727181,0,0,-0.83727181,427.34978,1471.4837)"
+ id="g10957"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path10959"
+ d="m 326.98547,847.70808 57.45819,0 0,-73.83379 -10,0 0,61.83379 -47.45819,0"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect10961"
+ width="9.4107542"
+ height="42.249756"
+ x="305.50339"
+ y="821.53503"
+ rx="4.7053771"
+ ry="4.7053771" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 390.56742,727.10519 c -6.09957,0 -11,4.90043 -11,11 l 0,10.15625 -10.15625,0 c -6.09957,0 -11,4.90043 -11,11 0,6.09957 4.90043,11 11,11 l 21.15625,0 c 6.09957,0 11,-4.90043 11,-11 l 0,-21.15625 c 0,-6.09957 -4.90043,-11 -11,-11 z"
+ id="path10963" />
+ <rect
+ ry="2.1935852"
+ rx="4.0765176"
+ y="771.6814"
+ x="369.11923"
+ height="12.20938"
+ width="21.740107"
+ id="rect10965"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="2.9156427"
+ rx="7.0489321"
+ y="829.57007"
+ x="318.22556"
+ height="26.179665"
+ width="14.097864"
+ id="rect10967"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <g
+ id="g10969"
+ transform="translate(-34.057657,-0.65450616)"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
+ <path
+ inkscape:connector-curvature="0"
+ id="path10971"
+ d="m 404.77705,825.97291 -9.16957,15.86559 9.16957,15.84038 18.38979,0 9.19491,-15.84038 -9.19491,-15.86559 -18.38979,0 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path10973"
+ d="m 413.97195,836.00644 c 3.23493,0 5.85942,2.61224 5.85942,5.83206 0,3.21983 -2.62449,5.83207 -5.85942,5.83207 -3.23494,0 -5.85943,-2.61224 -5.85943,-5.83207 0,-3.21982 2.62449,-5.83206 5.85943,-5.83206 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.92158127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ </g>
+ <rect
+ ry="12.863822"
+ rx="12.863822"
+ y="836.96265"
+ x="214.05772"
+ height="25.727644"
+ width="83.8843"
+ id="rect5685-1"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#4b4f2f;stroke-width:4.12070131;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+</svg>
diff --git a/Resources/gnupg-infographic/README b/Resources/graphics/originals/gnupg-infographic/README
index f29c0a84a..f29c0a84a 100644
--- a/Resources/gnupg-infographic/README
+++ b/Resources/graphics/originals/gnupg-infographic/README
diff --git a/Resources/gnupg-infographic/gnupg-infographic.png b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.png
index 52b8f21ac..52b8f21ac 100644
--- a/Resources/gnupg-infographic/gnupg-infographic.png
+++ b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.png
Binary files differ
diff --git a/Resources/gnupg-infographic/gnupg-infographic.svg b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.svg
index 9a17421e2..9a17421e2 100644
--- a/Resources/gnupg-infographic/gnupg-infographic.svg
+++ b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.svg
diff --git a/Resources/graphics/ic_action_nfc/NFC.png b/Resources/graphics/originals/ic_action_nfc/NFC.png
index 96af64049..96af64049 100644
--- a/Resources/graphics/ic_action_nfc/NFC.png
+++ b/Resources/graphics/originals/ic_action_nfc/NFC.png
Binary files differ
diff --git a/Resources/graphics/ic_action_qr_code/ic_menu_qr_code.svg b/Resources/graphics/originals/ic_action_qr_code/ic_menu_qr_code.svg
index 5cbe9defc..5cbe9defc 100644
--- a/Resources/graphics/ic_action_qr_code/ic_menu_qr_code.svg
+++ b/Resources/graphics/originals/ic_action_qr_code/ic_menu_qr_code.svg
diff --git a/Resources/graphics/ic_launcher/AUTHORS b/Resources/graphics/originals/ic_launcher/AUTHORS
index dbfcfb4fc..dbfcfb4fc 100644
--- a/Resources/graphics/ic_launcher/AUTHORS
+++ b/Resources/graphics/originals/ic_launcher/AUTHORS
diff --git a/Resources/graphics/ic_launcher/COPYING b/Resources/graphics/originals/ic_launcher/COPYING
index 2faa27568..2faa27568 100644
--- a/Resources/graphics/ic_launcher/COPYING
+++ b/Resources/graphics/originals/ic_launcher/COPYING
diff --git a/Resources/graphics/ic_launcher/kgpg_key2_kopete.svgz b/Resources/graphics/originals/ic_launcher/kgpg_key2_kopete.svgz
index 2d43afb83..2d43afb83 100644
--- a/Resources/graphics/ic_launcher/kgpg_key2_kopete.svgz
+++ b/Resources/graphics/originals/ic_launcher/kgpg_key2_kopete.svgz
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/README.md b/Resources/graphics/originals/modernpgp-icons/README.md
new file mode 100644
index 000000000..c3cc37e5d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/README.md
@@ -0,0 +1,97 @@
+Icons
+=====
+
+A collection of icons representing PGP's implementation in graphical interfaces. Icons are grouped together according to Encryption, Key, and Signature.
+
+
+Projects
+========
+
+* [Mailpile](https://github.com/pagekite/Mailpile) - A fast modern webmail client for Mac, Linux, and Windows
+* [OpenKeychain](https://github.com/open-keychain/open-keychain) - An Android app for managing PGP keys
+* [GPGTools](https://github.com/GPGTools) - A well designed GUI interface for managing PGP keys on Mac OS
+
+
+Encryption
+==========
+
+### Open Lock
+
+The open lock icon SHOULD be used to represent a message or data that is going to be sent "unencrypted"
+
+![Unncrypted Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/encryption/lock-open.png)
+
+
+### Closed Lock
+
+The closed lock icon is used to represent data that WAS encrypted but HAS BEEN successfully decrypted or used for outgoing message or data that WILL BE encrypted.
+
+![Encrypted Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/encryption/lock-closed.png)
+
+
+### Error Lock
+
+The error lock icon represents data that IS encrypted and COULD NOT be decrypted.
+
+![Error Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/encryption/lock-error.png)
+
+
+Keys
+====
+
+### Fingerprint
+Use this icon to represent fingrprint of a PGP key
+
+![Fingerprint Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/keys/icon-fingerprint.png)
+
+### Key
+Use this icon to represent a PGP key or Key ID
+
+![Key Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/keys/icon-key.png)
+
+
+Signatures
+==========
+
+Here are the signature state names + description that Mailpile is currently using.
+
+
+### Invalid
+The signature was invalid or bad
+
+![Invalid Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-invalid-cutout.png)
+
+
+### Revoked
+Watch out, the signature was made with a key that has been revoked- this is not a good thing
+
+![Revoked Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-revoked-cutout.png)
+
+
+### Expired
+The signature was made with an expired key
+
+![Expired Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-expired-cutout.png)
+
+
+### Unknown
+The signature was made with an unknown key, so we can not verify it
+
+![Unknown Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-unknown-cutout.png)
+
+
+### Unverified
+The signature was good but it came from a key that is not verified yet
+
+![Unverified Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-unverified-cutout.png)
+
+
+### Verified
+The signature was good and came from a verified key, w00t!
+
+![Verified Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-verified-cutout.png)
+
+
+### Error & None
+The "error" state exists when there was a weird programatic error trying to analyze this signature. The "none" state is what some API's return when there is no signature of a message. There is no icons for either of these states.
+
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.png
new file mode 100644
index 000000000..0003ce164
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg
new file mode 100644
index 000000000..286e89297
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-closed</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-closed" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M81.502,45.132 L79.577,45.132 L79.577,29.479 C79.479,10.285 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.182,46.109 81.727,45.132 81.502,45.132 L81.502,45.132 Z M59.334,86.055 L41.061,86.055 L46.024,71.489 C43.904,70.077 42.496,67.623 42.496,64.824 C42.496,60.44 45.938,56.886 50.183,56.886 C54.428,56.886 57.87,60.443 57.87,64.824 C57.87,67.619 56.466,70.077 54.348,71.485 L59.334,86.055 L59.334,86.055 Z M34.261,45.132 L34.277,29.686 C34.277,19.737 40.348,11.783 50.183,11.783 C59.924,11.783 66.088,18.741 66.088,29.686 L66.098,45.132 L34.261,45.132 L34.261,45.132 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.png
new file mode 100644
index 000000000..693b7c6f7
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.png
new file mode 100644
index 000000000..6dea7ba27
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.png
new file mode 100644
index 000000000..7cc3b343b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.png
new file mode 100644
index 000000000..e2bd291d2
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg
new file mode 100644
index 000000000..d3c4e1d1d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-error</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-error" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M80.459,45.474 L78.533,45.474 L78.533,29.826 C78.435,10.633 65.344,0.183 49.433,0.183 C33.527,0.183 19.265,11.128 19.761,29.826 L19.745,45.454 C19.745,45.454 19.985,45.475 18.784,45.475 C17.514,45.475 9.145,46.946 9.145,54.407 L9.145,90.228 C9.145,98.187 18.565,100.171 18.784,100.171 L79.984,100.171 C80.203,100.171 89.138,99.178 89.138,90.228 L89.138,54.901 C89.139,46.452 80.684,45.474 80.459,45.474 L80.459,45.474 Z M33.234,30.033 C33.234,20.084 39.304,12.131 49.14,12.131 C58.881,12.131 65.045,19.088 65.045,30.033 L65.055,45.474 L33.218,45.474 L33.234,30.033 L33.234,30.033 Z M59.4033767,90.873 L48.4582822,79.9279055 L38.2296593,90.3644491 L31.6365,83.7568884 L42.5824946,72.8153942 L32.3439707,62.5939721 L38.7544118,56.1079235 L49.7013065,67.0503177 L60.1234487,56.7100837 L66.6365,63.2240351 L55.6896053,74.1673294 L66.0091373,84.4778605 L59.4033767,90.873 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.png
new file mode 100644
index 000000000..6a4ddf7ee
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.png
new file mode 100644
index 000000000..225a82f43
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.png
new file mode 100644
index 000000000..22c5cb14b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.png
new file mode 100644
index 000000000..30b57db5d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg
new file mode 100644
index 000000000..9beb127af
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-open</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-open" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M79.577,26.479 C79.577,10.833 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.181,46.107 81.726,45.13 81.5,45.13 L34.259,45.13 L34.275,29.684 C34.275,19.735 40.346,11.781 50.181,11.781 C59.922,11.781 66.664,18.164 66.664,29.164 L79.577,26.479 Z M59.334,86.055 L41.061,86.055 L46.024,71.49 C43.904,70.078 42.496,67.624 42.496,64.825 C42.496,60.44 45.938,56.887 50.183,56.887 C54.428,56.887 57.87,60.445 57.87,64.825 C57.87,67.62 56.466,70.078 54.348,71.485 L59.334,86.055 L59.334,86.055 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.png
new file mode 100644
index 000000000..056e1b64b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.png
new file mode 100644
index 000000000..a0a12eada
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.png
new file mode 100644
index 000000000..06a576338
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.png
new file mode 100644
index 000000000..c4b79f295
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg
new file mode 100644
index 000000000..a5ad87050
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>icon-fingerprint</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="icon-fingerprint" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M46.214961,59.4153846 C46.6578855,58.1692308 46.0890176,56.7692308 44.9413761,56.2861538 C43.80081,55.8061538 42.5074138,56.4230769 42.0644893,57.6692308 C39.3673195,65.2492308 29.7984516,79.6507692 20.939961,84.9630769 C19.8644893,85.6107692 19.4739233,87.0815385 20.0668478,88.2507692 C20.4729799,89.0492308 21.2343006,89.5 22.0182629,89.5 C22.380527,89.5 22.7498667,89.4030769 23.0923195,89.2 C33.3475082,83.0492308 43.27581,67.6830769 46.214961,59.4153846 L46.214961,59.4153846 L46.214961,59.4153846 Z M54.2159044,64.0892308 C57.1465648,54.9215385 53.5069421,48.7230769 48.6036402,46.6476923 C43.6437346,44.5446154 36.7748667,46.3707692 33.7960931,53.8676923 C31.2630742,60.24 24.7225082,71.3646154 17.0824138,75.8953846 C16.0041119,76.5353846 15.6064704,78.0046154 16.1965648,79.1784615 C16.785244,80.3476923 18.1366591,80.7830769 19.2163761,80.1415385 C28.5135459,74.6230769 35.4913761,61.7969231 37.8814704,55.7861538 C39.7564704,51.0692308 43.9677912,49.8753846 46.9904327,51.1553846 C49.8220365,52.3523077 52.1031685,55.9630769 50.0144893,62.5015385 C46.7597723,72.6784615 34.6918478,86.4707692 26.8692063,91.1061538 C25.7894893,91.7446154 25.3918478,93.2138462 25.980527,94.3876923 C26.385244,95.1938462 27.1479799,95.6492308 27.9361874,95.6492308 C28.2942063,95.6492308 28.6593006,95.5553846 28.9989233,95.3538462 C37.6819421,90.2092308 50.5890176,75.4307692 54.2159044,64.0892308 L54.2159044,64.0892308 L54.2159044,64.0892308 Z M51.6319421,38.1907692 C48.2470365,36.76 44.5762817,36.3876923 41.0328855,37.1153846 C34.4385459,38.4661538 28.8701497,43.3830769 26.1319421,50.2676923 C23.2720365,57.4676923 19.0522251,62.6830769 13.5857157,65.7692308 C12.494678,66.3861538 12.0715648,67.8461538 12.6361874,69.0307692 C13.2050553,70.2138462 14.5437346,70.6753846 15.6361874,70.0630769 C22.035244,66.4492308 26.9413761,60.4323077 30.2159044,52.1846154 C32.3654327,46.7769231 36.7168478,42.92 41.8536402,41.8692308 C44.5932629,41.3092308 47.4135459,41.5923077 50.0173195,42.6969231 C56.4503384,45.4169231 61.6593006,53.7061538 57.8965648,65.4784615 C54.3064704,76.7061538 43.664961,89.4338462 35.0059987,96.2046154 C34.0083572,96.9846154 33.780527,98.4953846 34.499395,99.58 C34.9338289,100.236923 35.6144893,100.586154 36.3078855,100.586154 C36.7578855,100.586154 37.2121308,100.436923 37.605527,100.130769 C46.8319421,92.9184615 58.2078855,79.2430769 62.099395,67.0661538 C66.8328855,52.2692308 59.9909044,41.7246154 51.6319421,38.1907692 L51.6319421,38.1907692 L51.6319421,38.1907692 Z M54.6588289,29.7338462 C49.8291119,27.6892308 44.5833572,27.1584615 39.4876025,28.1984615 C30.1833572,30.1030769 22.9635459,35.76 19.1526968,44.1323077 L19.0465648,44.3646154 C17.7347723,47.2553846 16.3776968,50.2446154 14.114961,50.9969231 C12.9390176,51.3876923 12.2767534,52.74 12.6376025,54.0184615 C12.9956214,55.2953846 14.2451497,56.0138462 15.4140176,55.6246154 C19.5163761,54.26 21.4692063,49.9615385 23.0385459,46.5061538 L23.1432629,46.2753846 C26.3173195,39.3 32.414961,34.5676923 40.3126025,32.9523077 C44.580527,32.0753846 48.9871308,32.5230769 53.0470365,34.2415385 C63.4111874,38.6246154 71.1135459,51.7861538 65.7814704,68.4584615 C63.3475082,76.0676923 58.1286402,84.7061538 51.0843006,92.78 C50.2394893,93.7507692 50.2762817,95.2815385 51.1663761,96.2 C51.5965648,96.6446154 52.1484516,96.8630769 52.6989233,96.8630769 C53.2876025,96.8630769 53.8762817,96.6107692 54.3135459,96.1092308 C61.7838289,87.5430769 67.3479799,78.2892308 69.9843006,70.0446154 C76.2418478,50.4830769 67.0111874,34.9569231 54.6588289,29.7338462 L54.6588289,29.7338462 L54.6588289,29.7338462 Z M57.689961,21.2784615 C51.3998667,18.6123077 44.5635459,17.9261538 37.9437346,19.28 C28.2319421,21.2692308 20.1121308,27.2846154 14.4644893,36.6784615 C13.7909044,37.7969231 14.0795836,39.2953846 15.1097723,40.0276923 C16.139961,40.76 17.519678,40.4476923 18.1890176,39.3276923 C23.2295836,30.9446154 30.1522251,25.8 38.7659044,24.0369231 C44.580527,22.8461538 50.564961,23.4538462 56.0767534,25.7892308 C69.8215648,31.6015385 80.6597723,49.5784615 73.6677912,71.44 C72.6531685,74.6076923 71.2748667,77.8723077 69.5725082,81.14 C68.9668478,82.3015385 69.3418478,83.7784615 70.4116591,84.4384615 C70.7597723,84.6507692 71.1347723,84.7523077 71.5069421,84.7523077 C72.2824138,84.7523077 73.0338289,84.3107692 73.4456214,83.5261538 C75.2809987,80.0046154 76.769678,76.4753846 77.8720365,73.0307692 C85.7781685,48.3030769 73.3734516,27.9107692 57.689961,21.2784615 L57.689961,21.2784615 L57.689961,21.2784615 Z M62.7135459,13.4507692 C49.6706214,7.52 34.6904327,8.71384615 22.635244,16.6369231 C21.5838289,17.3292308 21.2470365,18.8153846 21.8838289,19.96 C22.5163761,21.1015385 23.880527,21.4676923 24.9390176,20.7784615 C35.7828855,13.6507692 49.2630742,12.5784615 60.9970365,17.9153846 C72.2003384,23.0107692 79.980527,33.14 82.9026968,46.4369231 C83.1460931,47.5476923 84.0588289,48.2938462 85.064961,48.2938462 C85.2361874,48.2938462 85.410244,48.2723077 85.5843006,48.2276923 C86.7800553,47.9169231 87.5159044,46.6123077 87.2314704,45.3123077 C83.969678,30.4738462 75.2611874,19.1584615 62.7135459,13.4507692 L62.7135459,13.4507692 L62.7135459,13.4507692 Z M35.639961,6.20461538 C62.7517534,1.18307692 75.60081,15.6230769 79.8673195,22.0646154 C80.3017534,22.7215385 80.9838289,23.0692308 81.67581,23.0692308 C82.12581,23.0692308 82.5800553,22.9215385 82.9734516,22.6138462 C83.9710931,21.8338462 84.1960931,20.3215385 83.4800553,19.2369231 C78.7239233,12.06 64.4597723,-4.04 34.8942063,1.43538462 C33.6814704,1.66 32.8692063,2.90923077 33.07581,4.22615385 C33.2809987,5.54461538 34.4343006,6.43230769 35.639961,6.20461538 L35.639961,6.20461538 L35.639961,6.20461538 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.png
new file mode 100644
index 000000000..305dac198
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.png
new file mode 100644
index 000000000..eff983359
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.png
new file mode 100644
index 000000000..310b224aa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.png
new file mode 100644
index 000000000..66f4708fc
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg
new file mode 100644
index 000000000..f584037c6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>icon-key</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="icon-key" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M95.053,43.711 L46.435,43.711 C43.412,34.064 34.404,26 23.76,26 C10.638,26 0,36.638 0,49.761 C0,62.884 10.638,73.52 23.76,73.52 C34.805,73.52 44.087,64.918 46.751,54.709 L63.875,54.709 L63.877,64.651 L70.098,64.651 L70.123,54.709 L74.783,54.709 L74.786,64.641 L81.006,64.641 L81.03,54.709 L85.167,54.709 L85.167,64.641 L91.392,64.641 L91.392,54.709 L95.057,54.709 C98.581,54.709 100.001,52.734 100.001,49.209 C99.997,45.689 98.58,43.711 95.053,43.711 L95.053,43.711 Z M23.596,62.794 C16.399,62.794 10.563,56.958 10.563,49.76 C10.563,42.562 16.399,36.727 23.596,36.727 C30.794,36.727 36.629,42.562 36.629,49.76 C36.629,56.958 30.794,62.794 23.596,62.794 L23.596,62.794 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.png
new file mode 100644
index 000000000..ccd7e10a1
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.png
new file mode 100644
index 000000000..551f2ae69
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.png
new file mode 100644
index 000000000..1a4320ec6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.png
new file mode 100644
index 000000000..661da48e2
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg
new file mode 100644
index 000000000..61ac8fdd0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M5.21763502,25.9334098 C4.62201801,24.5421709 5.31408066,20.2649627 6.50270803,18.5737297 C7.69394204,16.8824967 14.8139764,11.1118682 18.0827017,9.51888655 C21.3514269,7.92852492 25.1232335,10.7136228 25.1232335,10.7136228 L31.1693326,18.5737297 C21.6564037,21.754453 12.2477403,33.889148 12.2477403,33.889148 C12.2477403,33.889148 5.81325202,27.3259588 5.21763502,25.9334098 Z M50.7969868,98.0040129 C30.5564763,98.0040129 14.1592664,81.3860653 14.1592664,60.910451 C14.1592664,41.4138456 29.0387981,25.4459175 47.9423376,23.9707712 L47.9423376,18.3341735 L41.6333009,18.3341735 L41.6333009,9.02828206 L59.2561767,9.02828206 L59.2561767,18.3341735 L53.6477076,18.3341735 L53.6477076,23.9707712 C72.5460092,25.4445909 87.4333977,41.412519 87.4333977,60.910451 C87.4333977,81.3847387 71.0296405,98.0040129 50.7969868,98.0040129 Z M51.541054,71.6933659 C57.6539179,71.6933659 62.6093732,66.7455263 62.6093732,60.6420567 C62.6093732,54.5385872 57.6539179,49.5907476 51.541054,49.5907476 C45.4281901,49.5907476 40.4727348,54.5385872 40.4727348,60.6420567 C40.4727348,66.7455263 45.4281901,71.6933659 51.541054,71.6933659 Z M96.3766201,25.9341425 C95.7811759,27.3252533 89.3433427,33.889148 89.3433427,33.889148 C89.3433427,33.889148 79.9321974,21.7542607 70.4233315,18.5751403 L76.4676764,10.7157573 C76.4676764,10.7157573 80.2396916,7.92829614 83.5087714,9.5185113 C86.7765483,11.1152759 93.8997286,16.884063 95.0880111,18.5751403 C96.2775965,20.2635977 96.9694584,24.540412 96.3766201,25.9341425 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.png
new file mode 100644
index 000000000..965888294
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.png
new file mode 100644
index 000000000..8c722274a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.png
new file mode 100644
index 000000000..6c6038df0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.png
new file mode 100644
index 000000000..75a064188
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg
new file mode 100644
index 000000000..1d280572f
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.029,-0.384 L10,-0.384 C4.477,-0.384 0,4.094 0,9.616 L0,89.594 C0,95.117 4.477,99.594 10,99.594 L90.029,99.594 C95.556,99.594 100.035,95.117 100.035,89.594 L100.035,9.616 C100.035,4.094 95.557,-0.384 90.029,-0.384 L90.029,-0.384 Z M15.002,29.256 C14.545,28.194 15.076,24.929 15.988,23.638 C16.902,22.347 22.365,17.942 24.873,16.726 C27.381,15.512 30.275,17.638 30.275,17.638 L34.914,23.638 C27.615,26.066 20.396,35.329 20.396,35.329 C20.396,35.329 15.459,30.319 15.002,29.256 L15.002,29.256 Z M49.924,83.141 C34.467,83.141 21.945,70.614 21.945,55.179 C21.945,40.482 33.308,28.445 47.744,27.333 L47.744,23.084 L42.926,23.084 L42.926,16.069 L56.384,16.069 L56.384,23.084 L52.101,23.084 L52.101,27.333 C66.533,28.444 77.902,40.481 77.902,55.179 C77.902,70.613 65.375,83.141 49.924,83.141 L49.924,83.141 Z M85.035,29.258 C84.578,30.32 79.637,35.331 79.637,35.331 C79.637,35.331 72.414,26.067 65.116,23.64 L69.755,17.64 C69.755,17.64 72.65,15.512 75.159,16.726 C77.667,17.945 83.134,22.349 84.046,23.64 C84.959,24.929 85.49,28.194 85.035,29.258 L85.035,29.258 Z M49.459,63.892 C54.1307767,63.892 57.918,60.110597 57.918,55.446 C57.918,50.781403 54.1307767,47 49.459,47 C44.7872233,47 41,50.781403 41,55.446 C41,60.110597 44.7872233,63.892 49.459,63.892 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.png
new file mode 100644
index 000000000..492ac9e80
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.png
new file mode 100644
index 000000000..1ef0ac143
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.png
new file mode 100644
index 000000000..6b82cf9ab
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.png
new file mode 100644
index 000000000..d2bfb07a0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg
new file mode 100644
index 000000000..61fd2ace0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M77.3119658,92 L50,64.6787909 L22.6865385,92 L8.00299145,77.3054987 L35.3149573,49.9977557 L8,22.6870202 L22.6850427,8.00149623 L50,35.3137279 L77.3149573,8 L92,22.6825315 L64.6850427,49.9977557 L91.9970085,77.3054987 L77.3119658,92 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.png
new file mode 100644
index 000000000..a46488d2b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.png
new file mode 100644
index 000000000..dbb3f0639
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.png
new file mode 100644
index 000000000..7a6966cbf
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.png
new file mode 100644
index 000000000..9bd4ee24a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg
new file mode 100644
index 000000000..3eb204fdd
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.169,0.282 L10.169,0.282 C4.647,0.282 0.169,4.76 0.169,10.282 L0.169,90.261 C0.169,95.782 4.647,100.261 10.169,100.261 L90.169,100.261 C95.692,100.261 100.169,95.782 100.169,90.261 L100.169,10.282 C100.169,4.76 95.692,0.282 90.169,0.282 L90.169,0.282 Z M80.248,68.521 L70.43,78.342 L52.17,60.082 L33.909,78.342 L24.092,68.521 L42.352,50.27 L24.09,32.017 L33.908,22.202 L52.17,40.456 L70.432,22.201 L80.25,32.014 L61.988,50.27 L80.248,68.521 L80.248,68.521 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.png
new file mode 100644
index 000000000..1033831aa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.png
new file mode 100644
index 000000000..78b62797a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.png
new file mode 100644
index 000000000..7de3afefc
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.png
new file mode 100644
index 000000000..a5ce2e419
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg
new file mode 100644
index 000000000..0421286fe
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M50.1457786,95.2902674 C25.2543974,95.2902674 5,75.0407401 5,50.1451337 C5,25.252107 25.2556872,5 50.1457786,5 C75.03587,5 95.2902674,25.252107 95.2902674,50.1451337 C95.2902674,75.0394503 75.0371599,95.2902674 50.1457786,95.2902674 Z M35.5297191,75.6701923 C39.8404345,78.1467253 44.8296167,79.569442 50.1464236,79.569442 C66.3793238,79.569442 79.5862102,66.3638454 79.5862102,50.1296554 C79.5862102,44.8115586 78.1622037,39.8223764 75.6843808,35.5116611 L35.5297191,75.6701923 Z M50.1464236,20.6911586 C33.9135233,20.6911586 20.7066369,33.8967551 20.7066369,50.1309452 C20.7066369,55.3523024 22.0803389,60.2563538 24.473031,64.512895 L64.5296632,24.4575526 C60.2718321,22.0635707 55.3690706,20.6911586 50.1464236,20.6911586 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.png
new file mode 100644
index 000000000..7da8f0888
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.png
new file mode 100644
index 000000000..73e769750
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.png
new file mode 100644
index 000000000..a07fbf223
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.png
new file mode 100644
index 000000000..66f74079a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg
new file mode 100644
index 000000000..5b6f7a420
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.087,-0.389 L10.087,-0.389 C4.564,-0.389 0.087,4.089 0.087,9.611 L0.087,89.589 C0.087,95.112 4.564,99.589 10.087,99.589 L90.087,99.589 C95.61,99.589 100.087,95.112 100.087,89.589 L100.087,9.611 C100.087,4.089 95.61,-0.389 90.087,-0.389 Z M38.756,69.4 C42.098,71.32 45.966,72.423 50.088,72.423 C62.673,72.423 72.912,62.185 72.912,49.599 C72.912,45.476 71.808,41.608 69.887,38.266 L38.756,69.4 Z M50.088,26.776 C37.503,26.776 27.264,37.014 27.264,49.6 C27.264,53.648 28.329,57.45 30.184,60.75 L61.239,29.696 C57.938,27.84 54.137,26.776 50.088,26.776 Z M50.088,84.6 C30.79,84.6 15.087,68.901 15.087,49.6 C15.087,30.301 30.791,14.6 50.088,14.6 C69.385,14.6 85.088,30.301 85.088,49.6 C85.088,68.9 69.386,84.6 50.088,84.6 L50.088,84.6 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.png
new file mode 100644
index 000000000..b9f891b97
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.png
new file mode 100644
index 000000000..2c333d4f9
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.png
new file mode 100644
index 000000000..4612b19db
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.png
new file mode 100644
index 000000000..1cef14805
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg
new file mode 100644
index 000000000..402bffcaa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M11.4743662,97.2253545 C1.98936285,97.2253571 -1.69987039,86.6466353 1.98936288,81.2764443 C2.36018089,80.2888073 37.5445854,9.4248374 37.6406733,9.21698534 C41.524789,0.483122973 56.8650161,0.0416071437 60.7924391,9.21698534 C60.7572519,9.19524917 98.2991929,81.8687547 97.9337883,81.2642177 C101.323931,86.2404407 96.9260512,97.2253571 88.8978453,97.2253545 C88.8978453,97.2253545 11.4756386,97.2879401 11.4743662,97.2253545 Z M50.5378687,73.3388569 C47.2443918,73.3388569 44.2703808,76.046195 44.2703808,79.5061732 C44.2703808,82.9729198 47.1388056,85.6802579 50.5378687,85.6802579 C53.9369317,85.6802579 56.8040029,82.9729198 56.8040029,79.5061732 C56.8053565,76.046195 53.8313455,73.3388569 50.5378687,73.3388569 Z M50.3063913,28.5 C46.5729719,28.5 42.719076,30.2990258 43.0805057,32.9143334 L45.8826007,65.934287 L54.7315355,65.934287 L57.5322768,32.9143334 C57.8937065,30.2990258 54.0398106,28.5 50.3063913,28.5 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.png
new file mode 100644
index 000000000..fcf3cb4a6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.png
new file mode 100644
index 000000000..33a093b71
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.png
new file mode 100644
index 000000000..011c503f0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.png
new file mode 100644
index 000000000..0b04995b8
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg
new file mode 100644
index 000000000..f0494aa33
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.478,0 0,4.478 0,10 L0,89.977 C0,95.5 4.478,99.977 10,99.977 L90,99.977 C95.523,99.977 100,95.5 100,89.977 L100,10 C100,4.477 95.523,0 90,0 L90,0 Z M81.015,83.034 C80.993,83.04 17.896,83.083 17.895,83.034 C14.215,83.038 11.855,75.247 14.581,71.294 C14.855,70.567 40.853,18.404 40.924,18.251 C43.794,11.822 55.129,11.497 58.031,18.251 C58.005,18.235 85.745,71.73 85.475,71.285 C87.98,74.948 85.14,82.991 81.015,83.034 Z M50.046,65.331 C47.613,65.331 45.416,67.331 45.416,69.887 C45.416,72.448 47.535,74.448 50.046,74.448 C52.557,74.448 54.675,72.448 54.675,69.887 C54.676,67.331 52.479,65.331 50.046,65.331 Z M49.875,29.502 C47.117,29.502 44.27,30.961 44.537,33.082 L46.607,59.861 L53.144,59.861 L55.213,33.082 C55.48,30.961 52.633,29.502 49.875,29.502 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.png
new file mode 100644
index 000000000..bea9ecaf2
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.png
new file mode 100644
index 000000000..2baa3b700
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.png
new file mode 100644
index 000000000..54dac1caa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.png
new file mode 100644
index 000000000..51c2ed7eb
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg
new file mode 100644
index 000000000..ffa98580a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M49.8900274,96.5521596 C75.8474106,96.5521596 96.8900274,75.5095428 96.8900274,49.5521596 C96.8900274,23.5947764 75.8474106,2.5521596 49.8900274,2.5521596 C23.9326441,2.5521596 2.89002736,23.5947764 2.89002736,49.5521596 C2.89002736,75.5095428 23.9326441,96.5521596 49.8900274,96.5521596 Z M42.9188472,79.4349375 L42.9188472,67.0146143 L55.3391704,67.0146143 L55.3391704,79.4349375 L42.9188472,79.4349375 Z M68.652586,41.8646591 C67.9712562,43.583078 67.1302842,45.0524085 66.1249189,46.2716815 C65.1167028,47.4919237 64.0039592,48.5318919 62.7800362,49.3906167 C61.5570634,50.25128 60.4006082,51.1080664 59.3135213,51.9677605 C58.2254842,52.829393 57.2609796,53.8121774 56.4181072,54.9209598 C55.5733342,56.0307115 55.0449948,57.4147511 54.8273874,59.0779247 L54.8273874,62.237567 L43.8168316,62.237567 L43.8168316,58.4954263 C43.9802747,56.114064 44.4278428,54.1165111 45.1623867,52.5095519 C45.89503,50.9025928 46.7531065,49.5292145 47.7309145,48.3923248 C48.7106231,47.2564044 49.7425954,46.2716815 50.8296823,45.4400947 C51.9177195,44.6104463 52.9230848,43.7769211 53.8476788,42.9463035 C54.7722728,42.1147166 55.5201202,41.2007465 56.0912209,40.2024546 C56.6623216,39.2041628 56.9188893,37.9577517 56.8647251,36.4612832 C56.8647251,33.9112774 56.2537138,32.0261534 55.030741,30.804942 C53.8058678,29.5876075 52.1058691,28.9760325 49.9326456,28.9760325 C48.4645081,28.9760325 47.2006746,29.2677664 46.1392445,29.8492956 C45.0797149,30.4317941 44.2092851,31.2071664 43.5298558,32.177351 C42.848526,33.1475357 42.3477439,34.2844253 42.0208576,35.5860816 C41.6939713,36.8906456 41.5305282,38.2901926 41.5305282,39.7866612 L29.5431146,39.7866612 C29.5953784,36.7917856 30.0999615,34.0479368 31.0521128,31.5541455 C32.0023636,29.0593851 33.3346152,26.8970655 35.0479174,25.0691252 C36.7612195,23.2382773 38.8270647,21.8115922 41.2464032,20.7832547 C43.6666919,19.7568556 46.3730062,19.2441406 49.3624951,19.2441406 C53.2224138,19.2441406 56.4447142,19.785932 59.0274958,20.8666072 C61.6093272,21.9482516 63.6894262,23.2935226 65.2668425,24.9024202 C66.8442588,26.5103485 67.9712562,28.2423365 68.6516357,30.0993532 C69.3310651,31.9573391 69.6703046,33.6883579 69.6703046,35.2962862 C69.6722051,37.9567825 69.3320153,40.1481786 68.652586,41.8646591 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.png
new file mode 100644
index 000000000..10d328081
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.png
new file mode 100644
index 000000000..b8fe6a994
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.png
new file mode 100644
index 000000000..93e562e3b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.png
new file mode 100644
index 000000000..00989a976
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg
new file mode 100644
index 000000000..6ce6d14dd
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.477,0 0,4.478 0,10 L0,89.978 C0,95.501 4.477,99.978 10,99.978 L90,99.978 C95.523,99.978 100,95.501 100,89.978 L100,10 C100,4.478 95.523,0 90,0 L90,0 Z M56.359,80.095 L42.883,80.095 L42.883,66.877 L56.359,66.877 L56.359,80.095 L56.359,80.095 Z M70.222,40.709 C69.505,42.482 68.62,43.998 67.562,45.256 C66.501,46.515 65.33,47.588 64.042,48.474 C62.755,49.362 61.538,50.246 60.394,51.133 C59.249,52.022 58.234,53.036 57.347,54.18 C56.458,55.325 55.902,56.753 55.673,58.469 L55.673,61.729 L44.086,61.729 L44.086,57.868 C44.258,55.411 44.729,53.35 45.502,51.692 C46.273,50.034 47.176,48.617 48.205,47.444 C49.236,46.272 50.322,45.256 51.466,44.398 C52.611,43.542 53.669,42.682 54.642,41.825 C55.615,40.967 56.402,40.024 57.003,38.994 C57.604,37.964 57.874,36.678 57.817,35.134 C57.817,32.503 57.174,30.558 55.887,29.298 C54.598,28.042 52.809,27.411 50.522,27.411 C48.977,27.411 47.647,27.712 46.53,28.312 C45.415,28.913 44.499,29.713 43.784,30.714 C43.067,31.715 42.54,32.888 42.196,34.231 C41.852,35.577 41.68,37.021 41.68,38.565 L29.065,38.565 C29.12,35.475 29.651,32.644 30.653,30.071 C31.653,27.497 33.055,25.266 34.858,23.38 C36.661,21.491 38.835,20.019 41.381,18.958 C43.928,17.899 46.776,17.37 49.922,17.37 C53.984,17.37 57.375,17.929 60.093,19.044 C62.81,20.16 64.999,21.548 66.659,23.208 C68.319,24.867 69.505,26.654 70.221,28.57 C70.936,30.487 71.293,32.273 71.293,33.932 C71.295,36.677 70.937,38.938 70.222,40.709 L70.222,40.709 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.png
new file mode 100644
index 000000000..2ee36bb15
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.png
new file mode 100644
index 000000000..b1f30b334
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.png
new file mode 100644
index 000000000..56193245c
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.png
new file mode 100644
index 000000000..9a8c5efcd
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg
new file mode 100644
index 000000000..04356a977
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M50,97 C75.9573832,97 97,75.9573832 97,50 C97,24.0426168 75.9573832,3 50,3 C24.0426168,3 3,24.0426168 3,50 C3,75.9573832 24.0426168,97 50,97 Z M46.2732912,77.5085 L20,57.830916 L27.9184401,47.6349702 L43.3096859,59.5152262 L70.31112,23 L80.867825,30.7782191 L46.2732912,77.5085 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.png
new file mode 100644
index 000000000..1adfc7fb6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.png
new file mode 100644
index 000000000..227504e6b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.png
new file mode 100644
index 000000000..cf8ad067d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.png
new file mode 100644
index 000000000..e19125e3c
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg
new file mode 100644
index 000000000..197273c79
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.003,-0.017 L10.001,-0.017 C4.478,-0.017 0.001,4.46 0.001,9.983 L0.001,89.96 C0.001,95.483 4.478,99.96 10.001,99.96 L90.003,99.96 C95.526,99.96 100.003,95.483 100.003,89.96 L100.003,9.983 C100.003,4.46 95.526,-0.017 90.003,-0.017 L90.003,-0.017 Z M46.194,79.729 L17.364,58.104 L26.053,46.899 L42.942,59.955 L72.571,19.826 L84.155,28.374 L46.194,79.729 L46.194,79.729 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.png
new file mode 100644
index 000000000..9c3063010
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.png
new file mode 100644
index 000000000..5de04efe1
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.png
new file mode 100644
index 000000000..d52221f6c
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.png
Binary files differ
diff --git a/Resources/graphics/status_lock_closed.svg b/Resources/graphics/status_lock_closed.svg
new file mode 100644
index 000000000..286e89297
--- /dev/null
+++ b/Resources/graphics/status_lock_closed.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-closed</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-closed" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M81.502,45.132 L79.577,45.132 L79.577,29.479 C79.479,10.285 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.182,46.109 81.727,45.132 81.502,45.132 L81.502,45.132 Z M59.334,86.055 L41.061,86.055 L46.024,71.489 C43.904,70.077 42.496,67.623 42.496,64.824 C42.496,60.44 45.938,56.886 50.183,56.886 C54.428,56.886 57.87,60.443 57.87,64.824 C57.87,67.619 56.466,70.077 54.348,71.485 L59.334,86.055 L59.334,86.055 Z M34.261,45.132 L34.277,29.686 C34.277,19.737 40.348,11.783 50.183,11.783 C59.924,11.783 66.088,18.741 66.088,29.686 L66.098,45.132 L34.261,45.132 L34.261,45.132 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_lock_error.svg b/Resources/graphics/status_lock_error.svg
new file mode 100644
index 000000000..d3c4e1d1d
--- /dev/null
+++ b/Resources/graphics/status_lock_error.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-error</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-error" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M80.459,45.474 L78.533,45.474 L78.533,29.826 C78.435,10.633 65.344,0.183 49.433,0.183 C33.527,0.183 19.265,11.128 19.761,29.826 L19.745,45.454 C19.745,45.454 19.985,45.475 18.784,45.475 C17.514,45.475 9.145,46.946 9.145,54.407 L9.145,90.228 C9.145,98.187 18.565,100.171 18.784,100.171 L79.984,100.171 C80.203,100.171 89.138,99.178 89.138,90.228 L89.138,54.901 C89.139,46.452 80.684,45.474 80.459,45.474 L80.459,45.474 Z M33.234,30.033 C33.234,20.084 39.304,12.131 49.14,12.131 C58.881,12.131 65.045,19.088 65.045,30.033 L65.055,45.474 L33.218,45.474 L33.234,30.033 L33.234,30.033 Z M59.4033767,90.873 L48.4582822,79.9279055 L38.2296593,90.3644491 L31.6365,83.7568884 L42.5824946,72.8153942 L32.3439707,62.5939721 L38.7544118,56.1079235 L49.7013065,67.0503177 L60.1234487,56.7100837 L66.6365,63.2240351 L55.6896053,74.1673294 L66.0091373,84.4778605 L59.4033767,90.873 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_lock_open.svg b/Resources/graphics/status_lock_open.svg
new file mode 100644
index 000000000..9beb127af
--- /dev/null
+++ b/Resources/graphics/status_lock_open.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-open</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-open" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M79.577,26.479 C79.577,10.833 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.181,46.107 81.726,45.13 81.5,45.13 L34.259,45.13 L34.275,29.684 C34.275,19.735 40.346,11.781 50.181,11.781 C59.922,11.781 66.664,18.164 66.664,29.164 L79.577,26.479 Z M59.334,86.055 L41.061,86.055 L46.024,71.49 C43.904,70.078 42.496,67.624 42.496,64.825 C42.496,60.44 45.938,56.887 50.183,56.887 C54.428,56.887 57.87,60.445 57.87,64.825 C57.87,67.62 56.466,70.078 54.348,71.485 L59.334,86.055 L59.334,86.055 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_expired.svg b/Resources/graphics/status_signature_expired.svg
new file mode 100644
index 000000000..1d280572f
--- /dev/null
+++ b/Resources/graphics/status_signature_expired.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.029,-0.384 L10,-0.384 C4.477,-0.384 0,4.094 0,9.616 L0,89.594 C0,95.117 4.477,99.594 10,99.594 L90.029,99.594 C95.556,99.594 100.035,95.117 100.035,89.594 L100.035,9.616 C100.035,4.094 95.557,-0.384 90.029,-0.384 L90.029,-0.384 Z M15.002,29.256 C14.545,28.194 15.076,24.929 15.988,23.638 C16.902,22.347 22.365,17.942 24.873,16.726 C27.381,15.512 30.275,17.638 30.275,17.638 L34.914,23.638 C27.615,26.066 20.396,35.329 20.396,35.329 C20.396,35.329 15.459,30.319 15.002,29.256 L15.002,29.256 Z M49.924,83.141 C34.467,83.141 21.945,70.614 21.945,55.179 C21.945,40.482 33.308,28.445 47.744,27.333 L47.744,23.084 L42.926,23.084 L42.926,16.069 L56.384,16.069 L56.384,23.084 L52.101,23.084 L52.101,27.333 C66.533,28.444 77.902,40.481 77.902,55.179 C77.902,70.613 65.375,83.141 49.924,83.141 L49.924,83.141 Z M85.035,29.258 C84.578,30.32 79.637,35.331 79.637,35.331 C79.637,35.331 72.414,26.067 65.116,23.64 L69.755,17.64 C69.755,17.64 72.65,15.512 75.159,16.726 C77.667,17.945 83.134,22.349 84.046,23.64 C84.959,24.929 85.49,28.194 85.035,29.258 L85.035,29.258 Z M49.459,63.892 C54.1307767,63.892 57.918,60.110597 57.918,55.446 C57.918,50.781403 54.1307767,47 49.459,47 C44.7872233,47 41,50.781403 41,55.446 C41,60.110597 44.7872233,63.892 49.459,63.892 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_expired_cutout.svg b/Resources/graphics/status_signature_expired_cutout.svg
new file mode 100644
index 000000000..61ac8fdd0
--- /dev/null
+++ b/Resources/graphics/status_signature_expired_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M5.21763502,25.9334098 C4.62201801,24.5421709 5.31408066,20.2649627 6.50270803,18.5737297 C7.69394204,16.8824967 14.8139764,11.1118682 18.0827017,9.51888655 C21.3514269,7.92852492 25.1232335,10.7136228 25.1232335,10.7136228 L31.1693326,18.5737297 C21.6564037,21.754453 12.2477403,33.889148 12.2477403,33.889148 C12.2477403,33.889148 5.81325202,27.3259588 5.21763502,25.9334098 Z M50.7969868,98.0040129 C30.5564763,98.0040129 14.1592664,81.3860653 14.1592664,60.910451 C14.1592664,41.4138456 29.0387981,25.4459175 47.9423376,23.9707712 L47.9423376,18.3341735 L41.6333009,18.3341735 L41.6333009,9.02828206 L59.2561767,9.02828206 L59.2561767,18.3341735 L53.6477076,18.3341735 L53.6477076,23.9707712 C72.5460092,25.4445909 87.4333977,41.412519 87.4333977,60.910451 C87.4333977,81.3847387 71.0296405,98.0040129 50.7969868,98.0040129 Z M51.541054,71.6933659 C57.6539179,71.6933659 62.6093732,66.7455263 62.6093732,60.6420567 C62.6093732,54.5385872 57.6539179,49.5907476 51.541054,49.5907476 C45.4281901,49.5907476 40.4727348,54.5385872 40.4727348,60.6420567 C40.4727348,66.7455263 45.4281901,71.6933659 51.541054,71.6933659 Z M96.3766201,25.9341425 C95.7811759,27.3252533 89.3433427,33.889148 89.3433427,33.889148 C89.3433427,33.889148 79.9321974,21.7542607 70.4233315,18.5751403 L76.4676764,10.7157573 C76.4676764,10.7157573 80.2396916,7.92829614 83.5087714,9.5185113 C86.7765483,11.1152759 93.8997286,16.884063 95.0880111,18.5751403 C96.2775965,20.2635977 96.9694584,24.540412 96.3766201,25.9341425 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_invalid.svg b/Resources/graphics/status_signature_invalid.svg
new file mode 100644
index 000000000..3eb204fdd
--- /dev/null
+++ b/Resources/graphics/status_signature_invalid.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.169,0.282 L10.169,0.282 C4.647,0.282 0.169,4.76 0.169,10.282 L0.169,90.261 C0.169,95.782 4.647,100.261 10.169,100.261 L90.169,100.261 C95.692,100.261 100.169,95.782 100.169,90.261 L100.169,10.282 C100.169,4.76 95.692,0.282 90.169,0.282 L90.169,0.282 Z M80.248,68.521 L70.43,78.342 L52.17,60.082 L33.909,78.342 L24.092,68.521 L42.352,50.27 L24.09,32.017 L33.908,22.202 L52.17,40.456 L70.432,22.201 L80.25,32.014 L61.988,50.27 L80.248,68.521 L80.248,68.521 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_invalid_cutout.svg b/Resources/graphics/status_signature_invalid_cutout.svg
new file mode 100644
index 000000000..61fd2ace0
--- /dev/null
+++ b/Resources/graphics/status_signature_invalid_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M77.3119658,92 L50,64.6787909 L22.6865385,92 L8.00299145,77.3054987 L35.3149573,49.9977557 L8,22.6870202 L22.6850427,8.00149623 L50,35.3137279 L77.3149573,8 L92,22.6825315 L64.6850427,49.9977557 L91.9970085,77.3054987 L77.3119658,92 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_revoked.svg b/Resources/graphics/status_signature_revoked.svg
new file mode 100644
index 000000000..5b6f7a420
--- /dev/null
+++ b/Resources/graphics/status_signature_revoked.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.087,-0.389 L10.087,-0.389 C4.564,-0.389 0.087,4.089 0.087,9.611 L0.087,89.589 C0.087,95.112 4.564,99.589 10.087,99.589 L90.087,99.589 C95.61,99.589 100.087,95.112 100.087,89.589 L100.087,9.611 C100.087,4.089 95.61,-0.389 90.087,-0.389 Z M38.756,69.4 C42.098,71.32 45.966,72.423 50.088,72.423 C62.673,72.423 72.912,62.185 72.912,49.599 C72.912,45.476 71.808,41.608 69.887,38.266 L38.756,69.4 Z M50.088,26.776 C37.503,26.776 27.264,37.014 27.264,49.6 C27.264,53.648 28.329,57.45 30.184,60.75 L61.239,29.696 C57.938,27.84 54.137,26.776 50.088,26.776 Z M50.088,84.6 C30.79,84.6 15.087,68.901 15.087,49.6 C15.087,30.301 30.791,14.6 50.088,14.6 C69.385,14.6 85.088,30.301 85.088,49.6 C85.088,68.9 69.386,84.6 50.088,84.6 L50.088,84.6 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_revoked_cutout.svg b/Resources/graphics/status_signature_revoked_cutout.svg
new file mode 100644
index 000000000..0421286fe
--- /dev/null
+++ b/Resources/graphics/status_signature_revoked_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M50.1457786,95.2902674 C25.2543974,95.2902674 5,75.0407401 5,50.1451337 C5,25.252107 25.2556872,5 50.1457786,5 C75.03587,5 95.2902674,25.252107 95.2902674,50.1451337 C95.2902674,75.0394503 75.0371599,95.2902674 50.1457786,95.2902674 Z M35.5297191,75.6701923 C39.8404345,78.1467253 44.8296167,79.569442 50.1464236,79.569442 C66.3793238,79.569442 79.5862102,66.3638454 79.5862102,50.1296554 C79.5862102,44.8115586 78.1622037,39.8223764 75.6843808,35.5116611 L35.5297191,75.6701923 Z M50.1464236,20.6911586 C33.9135233,20.6911586 20.7066369,33.8967551 20.7066369,50.1309452 C20.7066369,55.3523024 22.0803389,60.2563538 24.473031,64.512895 L64.5296632,24.4575526 C60.2718321,22.0635707 55.3690706,20.6911586 50.1464236,20.6911586 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unknown.svg b/Resources/graphics/status_signature_unknown.svg
new file mode 100644
index 000000000..f0494aa33
--- /dev/null
+++ b/Resources/graphics/status_signature_unknown.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.478,0 0,4.478 0,10 L0,89.977 C0,95.5 4.478,99.977 10,99.977 L90,99.977 C95.523,99.977 100,95.5 100,89.977 L100,10 C100,4.477 95.523,0 90,0 L90,0 Z M81.015,83.034 C80.993,83.04 17.896,83.083 17.895,83.034 C14.215,83.038 11.855,75.247 14.581,71.294 C14.855,70.567 40.853,18.404 40.924,18.251 C43.794,11.822 55.129,11.497 58.031,18.251 C58.005,18.235 85.745,71.73 85.475,71.285 C87.98,74.948 85.14,82.991 81.015,83.034 Z M50.046,65.331 C47.613,65.331 45.416,67.331 45.416,69.887 C45.416,72.448 47.535,74.448 50.046,74.448 C52.557,74.448 54.675,72.448 54.675,69.887 C54.676,67.331 52.479,65.331 50.046,65.331 Z M49.875,29.502 C47.117,29.502 44.27,30.961 44.537,33.082 L46.607,59.861 L53.144,59.861 L55.213,33.082 C55.48,30.961 52.633,29.502 49.875,29.502 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unknown_cutout.svg b/Resources/graphics/status_signature_unknown_cutout.svg
new file mode 100644
index 000000000..402bffcaa
--- /dev/null
+++ b/Resources/graphics/status_signature_unknown_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M11.4743662,97.2253545 C1.98936285,97.2253571 -1.69987039,86.6466353 1.98936288,81.2764443 C2.36018089,80.2888073 37.5445854,9.4248374 37.6406733,9.21698534 C41.524789,0.483122973 56.8650161,0.0416071437 60.7924391,9.21698534 C60.7572519,9.19524917 98.2991929,81.8687547 97.9337883,81.2642177 C101.323931,86.2404407 96.9260512,97.2253571 88.8978453,97.2253545 C88.8978453,97.2253545 11.4756386,97.2879401 11.4743662,97.2253545 Z M50.5378687,73.3388569 C47.2443918,73.3388569 44.2703808,76.046195 44.2703808,79.5061732 C44.2703808,82.9729198 47.1388056,85.6802579 50.5378687,85.6802579 C53.9369317,85.6802579 56.8040029,82.9729198 56.8040029,79.5061732 C56.8053565,76.046195 53.8313455,73.3388569 50.5378687,73.3388569 Z M50.3063913,28.5 C46.5729719,28.5 42.719076,30.2990258 43.0805057,32.9143334 L45.8826007,65.934287 L54.7315355,65.934287 L57.5322768,32.9143334 C57.8937065,30.2990258 54.0398106,28.5 50.3063913,28.5 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unverified.svg b/Resources/graphics/status_signature_unverified.svg
new file mode 100644
index 000000000..6ce6d14dd
--- /dev/null
+++ b/Resources/graphics/status_signature_unverified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.477,0 0,4.478 0,10 L0,89.978 C0,95.501 4.477,99.978 10,99.978 L90,99.978 C95.523,99.978 100,95.501 100,89.978 L100,10 C100,4.478 95.523,0 90,0 L90,0 Z M56.359,80.095 L42.883,80.095 L42.883,66.877 L56.359,66.877 L56.359,80.095 L56.359,80.095 Z M70.222,40.709 C69.505,42.482 68.62,43.998 67.562,45.256 C66.501,46.515 65.33,47.588 64.042,48.474 C62.755,49.362 61.538,50.246 60.394,51.133 C59.249,52.022 58.234,53.036 57.347,54.18 C56.458,55.325 55.902,56.753 55.673,58.469 L55.673,61.729 L44.086,61.729 L44.086,57.868 C44.258,55.411 44.729,53.35 45.502,51.692 C46.273,50.034 47.176,48.617 48.205,47.444 C49.236,46.272 50.322,45.256 51.466,44.398 C52.611,43.542 53.669,42.682 54.642,41.825 C55.615,40.967 56.402,40.024 57.003,38.994 C57.604,37.964 57.874,36.678 57.817,35.134 C57.817,32.503 57.174,30.558 55.887,29.298 C54.598,28.042 52.809,27.411 50.522,27.411 C48.977,27.411 47.647,27.712 46.53,28.312 C45.415,28.913 44.499,29.713 43.784,30.714 C43.067,31.715 42.54,32.888 42.196,34.231 C41.852,35.577 41.68,37.021 41.68,38.565 L29.065,38.565 C29.12,35.475 29.651,32.644 30.653,30.071 C31.653,27.497 33.055,25.266 34.858,23.38 C36.661,21.491 38.835,20.019 41.381,18.958 C43.928,17.899 46.776,17.37 49.922,17.37 C53.984,17.37 57.375,17.929 60.093,19.044 C62.81,20.16 64.999,21.548 66.659,23.208 C68.319,24.867 69.505,26.654 70.221,28.57 C70.936,30.487 71.293,32.273 71.293,33.932 C71.295,36.677 70.937,38.938 70.222,40.709 L70.222,40.709 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unverified_cutout.svg b/Resources/graphics/status_signature_unverified_cutout.svg
new file mode 100644
index 000000000..ffa98580a
--- /dev/null
+++ b/Resources/graphics/status_signature_unverified_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M49.8900274,96.5521596 C75.8474106,96.5521596 96.8900274,75.5095428 96.8900274,49.5521596 C96.8900274,23.5947764 75.8474106,2.5521596 49.8900274,2.5521596 C23.9326441,2.5521596 2.89002736,23.5947764 2.89002736,49.5521596 C2.89002736,75.5095428 23.9326441,96.5521596 49.8900274,96.5521596 Z M42.9188472,79.4349375 L42.9188472,67.0146143 L55.3391704,67.0146143 L55.3391704,79.4349375 L42.9188472,79.4349375 Z M68.652586,41.8646591 C67.9712562,43.583078 67.1302842,45.0524085 66.1249189,46.2716815 C65.1167028,47.4919237 64.0039592,48.5318919 62.7800362,49.3906167 C61.5570634,50.25128 60.4006082,51.1080664 59.3135213,51.9677605 C58.2254842,52.829393 57.2609796,53.8121774 56.4181072,54.9209598 C55.5733342,56.0307115 55.0449948,57.4147511 54.8273874,59.0779247 L54.8273874,62.237567 L43.8168316,62.237567 L43.8168316,58.4954263 C43.9802747,56.114064 44.4278428,54.1165111 45.1623867,52.5095519 C45.89503,50.9025928 46.7531065,49.5292145 47.7309145,48.3923248 C48.7106231,47.2564044 49.7425954,46.2716815 50.8296823,45.4400947 C51.9177195,44.6104463 52.9230848,43.7769211 53.8476788,42.9463035 C54.7722728,42.1147166 55.5201202,41.2007465 56.0912209,40.2024546 C56.6623216,39.2041628 56.9188893,37.9577517 56.8647251,36.4612832 C56.8647251,33.9112774 56.2537138,32.0261534 55.030741,30.804942 C53.8058678,29.5876075 52.1058691,28.9760325 49.9326456,28.9760325 C48.4645081,28.9760325 47.2006746,29.2677664 46.1392445,29.8492956 C45.0797149,30.4317941 44.2092851,31.2071664 43.5298558,32.177351 C42.848526,33.1475357 42.3477439,34.2844253 42.0208576,35.5860816 C41.6939713,36.8906456 41.5305282,38.2901926 41.5305282,39.7866612 L29.5431146,39.7866612 C29.5953784,36.7917856 30.0999615,34.0479368 31.0521128,31.5541455 C32.0023636,29.0593851 33.3346152,26.8970655 35.0479174,25.0691252 C36.7612195,23.2382773 38.8270647,21.8115922 41.2464032,20.7832547 C43.6666919,19.7568556 46.3730062,19.2441406 49.3624951,19.2441406 C53.2224138,19.2441406 56.4447142,19.785932 59.0274958,20.8666072 C61.6093272,21.9482516 63.6894262,23.2935226 65.2668425,24.9024202 C66.8442588,26.5103485 67.9712562,28.2423365 68.6516357,30.0993532 C69.3310651,31.9573391 69.6703046,33.6883579 69.6703046,35.2962862 C69.6722051,37.9567825 69.3320153,40.1481786 68.652586,41.8646591 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_verified.svg b/Resources/graphics/status_signature_verified.svg
new file mode 100644
index 000000000..197273c79
--- /dev/null
+++ b/Resources/graphics/status_signature_verified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.003,-0.017 L10.001,-0.017 C4.478,-0.017 0.001,4.46 0.001,9.983 L0.001,89.96 C0.001,95.483 4.478,99.96 10.001,99.96 L90.003,99.96 C95.526,99.96 100.003,95.483 100.003,89.96 L100.003,9.983 C100.003,4.46 95.526,-0.017 90.003,-0.017 L90.003,-0.017 Z M46.194,79.729 L17.364,58.104 L26.053,46.899 L42.942,59.955 L72.571,19.826 L84.155,28.374 L46.194,79.729 L46.194,79.729 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_verified_cutout.svg b/Resources/graphics/status_signature_verified_cutout.svg
new file mode 100644
index 000000000..04356a977
--- /dev/null
+++ b/Resources/graphics/status_signature_verified_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M50,97 C75.9573832,97 97,75.9573832 97,50 C97,24.0426168 75.9573832,3 50,3 C24.0426168,3 3,24.0426168 3,50 C3,75.9573832 24.0426168,97 50,97 Z M46.2732912,77.5085 L20,57.830916 L27.9184401,47.6349702 L43.3096859,59.5152262 L70.31112,23 L80.867825,30.7782191 L46.2732912,77.5085 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/update-drawables.sh b/Resources/graphics/update-drawables.sh
index c1dfc77e6..ae77d313a 100755
--- a/Resources/graphics/update-drawables.sh
+++ b/Resources/graphics/update-drawables.sh
@@ -39,8 +39,27 @@ inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg
for NAME in "ic_action_nfc" "ic_action_qr_code"
do
+echo $NAME
inkscape -w 32 -h 32 -e "$MDPI_DIR/$NAME.png" $NAME.svg
inkscape -w 48 -h 48 -e "$HDPI_DIR/$NAME.png" $NAME.svg
inkscape -w 64 -h 64 -e "$XDPI_DIR/$NAME.png" $NAME.svg
inkscape -w 96 -h 96 -e "$XXDPI_DIR/$NAME.png" $NAME.svg
+done
+
+for NAME in status*.svg
+do
+echo $NAME
+inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME%%.*}.png" $NAME
+inkscape -w 32 -h 32 -e "$HDPI_DIR/${NAME%%.*}.png" $NAME
+inkscape -w 48 -h 48 -e "$XDPI_DIR/${NAME%%.*}.png" $NAME
+inkscape -w 64 -h 64 -e "$XXDPI_DIR/${NAME%%.*}.png" $NAME
+done
+
+for NAME in "create_key_robot"
+do
+echo $NAME
+inkscape -w 48 -h 48 -e "$MDPI_DIR/$NAME.png" $NAME.svg
+inkscape -w 64 -h 64 -e "$HDPI_DIR/$NAME.png" $NAME.svg
+inkscape -w 96 -h 96 -e "$XDPI_DIR/$NAME.png" $NAME.svg
+inkscape -w 128 -h 128 -e "$XXDPI_DIR/$NAME.png" $NAME.svg
done \ No newline at end of file
diff --git a/build.gradle b/build.gradle
index ceb963cd8..2e7ab3d86 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,8 +5,7 @@ buildscript {
dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
- classpath 'com.android.tools.build:gradle:0.12.0'
- classpath 'org.robolectric:robolectric-gradle-plugin:0.11.0'
+ classpath 'com.android.tools.build:gradle:0.12.2'
}
}
@@ -19,3 +18,18 @@ allprojects {
task wrapper(type: Wrapper) {
gradleVersion = '1.12'
}
+
+subprojects {
+ tasks.withType(Test) {
+ maxParallelForks = 1
+ }
+}
+
+// Ignore tests for external dependency
+project(':extern:spongycastle') {
+ subprojects {
+ // Need to re-apply the plugin here otherwise the test property below can't be set.
+ apply plugin: 'java'
+ test.enabled = false
+ }
+}
diff --git a/extern/AppMsg b/extern/AppMsg
deleted file mode 160000
-Subproject 61e74741909a712db2e0d31ddffa5b7cf37c21f
diff --git a/extern/openkeychain-api-lib b/extern/openkeychain-api-lib
-Subproject 48941ca6ec58c4583cdcf9d647ad5174925e2f2
+Subproject 02cb8ad24b780f3b97073f2526f83057c99d424
diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib
-Subproject aa9ecf871ce4db462034662675b258f9839d3f6
+Subproject e515a49027fc5de36b8977cf8b096afc9838a9b
diff --git a/extern/spongycastle b/extern/spongycastle
-Subproject 968405ee5d4272330cffdf75f7eee4cd9f5c864
+Subproject 9e4fb80c4f8efb8a0f8fd0c1cc1e74a421d1eb7
diff --git a/prepare-tests.sh b/prepare-tests.sh
new file mode 100755
index 000000000..027c76f84
--- /dev/null
+++ b/prepare-tests.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# This script installs a plugin which is necessary to run OpenKeychain's tests
+# into the local maven repository, then puts a line to include the -Test
+# subproject into settings.gradle
+
+echo "checking jdk runtime.."
+if ! java -version 2>&1 | grep OpenJDK; then
+ echo "tests will only run on openjdk, see readme for details!" >&2
+ return
+fi
+
+tmpdir="$(mktemp -d)"
+(
+ cd "$tmpdir";
+ git clone https://github.com/nenick/gradle-android-test-plugin.git
+ cd gradle-android-test-plugin
+ echo "rootProject.name = 'gradle-android-test-plugin-parent'" > settings.gradle
+ echo "include ':gradle-android-test-plugin'" >> settings.gradle
+ ./gradlew :gradle-android-test-plugin:install
+)
+rm -rf "$tmpdir"
+
+echo -n "ok, adding tests to include list.. "
+if grep OpenKeychain-Test settings.gradle >/dev/null ; then
+ echo " already in."
+else
+ echo "include ':OpenKeychain-Test'" >> settings.gradle
+ echo "ok"
+fi
diff --git a/settings.gradle b/settings.gradle
index dea16de81..b9569509e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,7 +9,6 @@ include ':extern:spongycastle:core'
include ':extern:spongycastle:pg'
include ':extern:spongycastle:pkix'
include ':extern:spongycastle:prov'
-include ':extern:AppMsg:library'
include ':extern:SuperToasts:supertoasts'
include ':extern:minidns'
include ':extern:KeybaseLib:Lib'