diff --git a/.travis.yml b/.travis.yml index ef69eb556..2cf2f98f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,6 @@ before_install: - ./prepare-tests.sh install: echo "Installation done" script: - - gradle assemble -S -q - - gradle --info OpenKeychain-Test:testDebug + - ./gradlew assemble -S -q + - ./gradlew --info OpenKeychain-Test:testDebug diff --git a/CHANGELOG b/CHANGELOG index 8804a9c63..196fc9937 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ * New icons to show status of key (by Brennan Novak) * Important bug fix: Importing of large key collections from a file is now possible * Notification showing cached passphrases +* Keys are connected to Android's contacts This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java index 8e7d395fd..6a023c486 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 . + */ + package org.sufficientlysecure.keychain.pgp; import junit.framework.AssertionFailedError; @@ -16,11 +34,13 @@ import org.spongycastle.bcpg.SecretSubkeyPacket; import org.spongycastle.bcpg.SignaturePacket; import org.spongycastle.bcpg.UserIDPacket; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.bcpg.PublicKeyAlgorithmTags; -import org.sufficientlysecure.keychain.Constants; +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.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange; import org.sufficientlysecure.keychain.support.KeyringBuilder; @@ -31,6 +51,7 @@ import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.security.Security; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; @@ -42,7 +63,7 @@ import java.util.Random; public class PgpKeyOperationTest { static UncachedKeyRing staticRing; - static String passphrase; + final static String passphrase = genPassphrase(); UncachedKeyRing ring; PgpKeyOperation op; @@ -50,37 +71,29 @@ public class PgpKeyOperationTest { ArrayList onlyA = new ArrayList(); ArrayList onlyB = new ArrayList(); - @BeforeClass public static void setUpOnce() throws Exception { + @BeforeClass + public static void setUpOnce() throws Exception { + Security.insertProviderAt(new BouncyCastleProvider(), 1); 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( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); + Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, null)); + Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L)); parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("pink"); parcel.mNewPassphrase = passphrase; PgpKeyOperation op = new PgpKeyOperation(null); - staticRing = op.createSecretKeyRing(parcel).getRing(); + EditKeyResult result = op.createSecretKeyRing(parcel); + Assert.assertTrue("initial test key creation must succeed", result.success()); + Assert.assertNotNull("initial test key creation must succeed", result.getRing()); - Assert.assertNotNull("initial test key creation must succeed", staticRing); + staticRing = result.getRing(); // we sleep here for a second, to make sure all new certificates have different timestamps Thread.sleep(1000); @@ -107,57 +120,55 @@ public class PgpKeyOperationTest { { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, new Random().nextInt(256)+255, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, new Random().nextInt(256)+255, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddUserIds.add("shy"); parcel.mNewPassphrase = passphrase; - UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing(); - - Assert.assertNull("creating ring with < 512 bytes keysize should fail", ring); + assertFailure("creating ring with < 512 bytes keysize should fail", parcel, + LogType.MSG_CR_ERROR_KEYSIZE_512); } { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.ELGAMAL, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddUserIds.add("shy"); parcel.mNewPassphrase = passphrase; - UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing(); - - Assert.assertNull("creating ring with ElGamal master key should fail", ring); + assertFailure("creating ring with ElGamal master key should fail", parcel, + LogType.MSG_CR_ERROR_FLAGS_ELGAMAL); } { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - 12345, 1024, KeyFlags.CERTIFY_OTHER, null)); - parcel.mAddUserIds.add("shy"); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, null)); + parcel.mAddUserIds.add("lotus"); parcel.mNewPassphrase = passphrase; - UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing(); - Assert.assertNull("creating ring with bad algorithm choice should fail", ring); + assertFailure("creating master key with null expiry should fail", parcel, + LogType.MSG_CR_ERROR_NULL_EXPIRY); } { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); 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); + assertFailure("creating ring with non-certifying master key should fail", parcel, + LogType.MSG_CR_ERROR_NO_CERTIFY); } { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mNewPassphrase = passphrase; - UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing(); - Assert.assertNull("creating ring without user ids should fail", ring); + assertFailure("creating ring without user ids should fail", parcel, + LogType.MSG_CR_ERROR_NO_USER_ID); } { @@ -165,8 +176,8 @@ public class PgpKeyOperationTest { parcel.mAddUserIds.add("shy"); parcel.mNewPassphrase = passphrase; - UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing(); - Assert.assertNull("creating ring without subkeys should fail", ring); + assertFailure("creating ring with no master key should fail", parcel, + LogType.MSG_CR_ERROR_NO_MASTER); } } @@ -177,9 +188,9 @@ public class PgpKeyOperationTest { public void testMasterFlags() throws Exception { SaveKeyringParcel parcel = new SaveKeyringParcel(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, 0L)); parcel.mAddUserIds.add("luna"); - ring = op.createSecretKeyRing(parcel).getRing(); + ring = assertCreateSuccess("creating ring with master key flags must succeed", parcel); Assert.assertEquals("the keyring should contain only the master key", 1, KeyringTestingHelper.itToList(ring.getPublicKeys()).size()); @@ -239,10 +250,8 @@ public class PgpKeyOperationTest { 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); + assertModifyFailure("keyring modification with bad master key id should fail", + ring, parcel, LogType.MSG_MF_ERROR_KEYID); } { @@ -251,10 +260,8 @@ public class PgpKeyOperationTest { 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); + assertModifyFailure("keyring modification with null master key id should fail", + ring, parcel, LogType.MSG_MF_ERROR_KEYID); } { @@ -264,10 +271,8 @@ public class PgpKeyOperationTest { // 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); + assertModifyFailure("keyring modification with bad fingerprint should fail", + ring, parcel, LogType.MSG_MF_ERROR_FINGERPRINT); } { @@ -275,10 +280,8 @@ public class PgpKeyOperationTest { 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); + assertModifyFailure("keyring modification with null fingerprint should fail", + ring, parcel, LogType.MSG_MF_ERROR_FINGERPRINT); } { @@ -286,10 +289,9 @@ public class PgpKeyOperationTest { 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); + assertModifyFailure("keyring modification with bad passphrase should fail", + ring, parcel, badphrase, LogType.MSG_MF_UNLOCK_ERROR); } } @@ -300,7 +302,7 @@ public class PgpKeyOperationTest { long expiry = new Date().getTime() / 1000 + 159; int flags = KeyFlags.SIGN_DATA; int bits = 1024 + new Random().nextInt(8); - parcel.mAddSubKeys.add(new SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, bits, flags, expiry)); + parcel.mAddSubKeys.add(new SubkeyAdd(Algorithm.RSA, bits, null, flags, expiry)); UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); @@ -336,28 +338,32 @@ public class PgpKeyOperationTest { Assert.assertEquals("added key must have expected flags", flags, newKey.getKeyUsage()); Assert.assertEquals("added key must have expected bitsize", - bits, newKey.getBitStrength()); + bits, (int) newKey.getBitStrength()); { // bad keysize should fail parcel.reset(); parcel.mAddSubKeys.add(new SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, new Random().nextInt(512), KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, new Random().nextInt(512), null, KeyFlags.SIGN_DATA, 0L)); + assertModifyFailure("creating a subkey with keysize < 512 should fail", ring, parcel, + LogType.MSG_CR_ERROR_KEYSIZE_512); - 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); + { + parcel.reset(); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, null)); + + assertModifyFailure("creating master key with null expiry should fail", ring, parcel, + LogType.MSG_MF_ERROR_NULL_EXPIRY); } { // a past expiry should fail parcel.reset(); - parcel.mAddSubKeys.add(new SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, + parcel.mAddSubKeys.add(new SubkeyAdd(Algorithm.RSA, 1024, null, 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); + assertModifyFailure("creating subkey with past expiry date should fail", ring, parcel, + LogType.MSG_MF_ERROR_PAST_EXPIRY); } } @@ -394,6 +400,20 @@ public class PgpKeyOperationTest { ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage()); } + { // change expiry + expiry += 60*60*24; + + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); + + 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(); @@ -418,28 +438,171 @@ public class PgpKeyOperationTest { expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000); } + { // expiry of 0 should be "no expiry" + parcel.reset(); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, 0L)); + 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.assertNull("key must not expire anymore", modified.getPublicKey(keyId).getExpiryTime()); + } + { // 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); + assertModifyFailure("setting subkey expiry to a past date should fail", ring, parcel, + LogType.MSG_MF_ERROR_PAST_EXPIRY); } - { // modifying nonexistent keyring should fail + { // modifying nonexistent subkey 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); + assertModifyFailure("modifying non-existent subkey should fail", ring, parcel, + LogType.MSG_MF_ERROR_SUBKEY_MISSING); } } + @Test + public void testMasterModify() throws Exception { + + long expiry = new Date().getTime()/1000 + 1024; + long keyId = ring.getMasterKeyId(); + + UncachedKeyRing modified = ring; + + // to make this check less trivial, we add a user id, change the primary one and revoke one + parcel.mAddUserIds.add("aloe"); + parcel.mChangePrimaryUserId = "aloe"; + parcel.mRevokeUserIds.add("pink"); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); + + { + parcel.reset(); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); + + // this implies that only the two non-revoked signatures were changed! + Assert.assertEquals("two extra packets in original", 2, onlyA.size()); + Assert.assertEquals("two extra packets in modified", 2, onlyB.size()); + + Assert.assertEquals("first original packet must be a signature", + PacketTags.SIGNATURE, onlyA.get(0).tag); + Assert.assertEquals("second original packet must be a signature", + PacketTags.SIGNATURE, onlyA.get(1).tag); + Assert.assertEquals("first new packet must be signature", + PacketTags.SIGNATURE, onlyB.get(0).tag); + Assert.assertEquals("first new packet must be signature", + PacketTags.SIGNATURE, onlyB.get(1).tag); + + Assert.assertNotNull("modified key must have an expiry date", + modified.getPublicKey().getExpiryTime()); + Assert.assertEquals("modified key must have expected expiry date", + expiry, modified.getPublicKey().getExpiryTime().getTime() / 1000); + Assert.assertEquals("modified key must have same flags as before", + ring.getPublicKey().getKeyUsage(), modified.getPublicKey().getKeyUsage()); + } + + { // change expiry + expiry += 60*60*24; + + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry)); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); + + 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.CERTIFY_OTHER | KeyFlags.SIGN_DATA; + parcel.reset(); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, flags, null)); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); + + 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); + } + + { // expiry of 0 should be "no expiry" + parcel.reset(); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, 0L)); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); + + Assert.assertNull("key must not expire anymore", modified.getPublicKey(keyId).getExpiryTime()); + } + + { // if we revoke everything, nothing is left to properly sign... + parcel.reset(); + parcel.mRevokeUserIds.add("twi"); + parcel.mRevokeUserIds.add("pink"); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, KeyFlags.CERTIFY_OTHER, null)); + + assertModifyFailure("master key modification with all user ids revoked should fail", ring, parcel, + LogType.MSG_MF_ERROR_MASTER_NONE); + } + + { // any flag not including CERTIFY_OTHER should fail + parcel.reset(); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, KeyFlags.SIGN_DATA, null)); + + assertModifyFailure("setting master key flags without certify should fail", ring, parcel, + LogType.MSG_MF_ERROR_NO_CERTIFY); + } + + { // a past expiry should fail + parcel.reset(); + parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10)); + + assertModifyFailure("setting subkey expiry to a past date should fail", ring, parcel, + LogType.MSG_MF_ERROR_PAST_EXPIRY); + } + + } + + @Test + public void testMasterRevoke() throws Exception { + + parcel.reset(); + parcel.mRevokeSubKeys.add(ring.getMasterKeyId()); + + UncachedKeyRing 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.KEY_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().isRevoked()); + + } + @Test public void testSubkeyRevoke() throws Exception { @@ -555,10 +718,8 @@ public class PgpKeyOperationTest { 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); + assertModifyFailure("setting primary user id to a revoked user id should fail", modified, parcel, + LogType.MSG_MF_ERROR_REVOKED_PRIMARY); } @@ -596,6 +757,14 @@ public class PgpKeyOperationTest { ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID()); } + { // revocation of non-existent user id should fail + parcel.reset(); + parcel.mRevokeUserIds.add("nonexistent"); + + assertModifyFailure("revocation of nonexistent user id should fail", modified, parcel, + LogType.MSG_MF_ERROR_NOEXIST_REVOKE); + } + } @Test @@ -603,9 +772,8 @@ public class PgpKeyOperationTest { { 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); + assertModifyFailure("adding an empty user id should fail", ring, parcel, + LogType.MSG_MF_UID_ERROR_EMPTY); } parcel.reset(); @@ -673,22 +841,87 @@ public class PgpKeyOperationTest { 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); + assertModifyFailure("changing primary user id to a non-existent one should fail", + ring, parcel, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY); } // check for revoked primary user id already done in revoke test } + @Test + public void testPassphraseChange() throws Exception { + + // change passphrase to empty + parcel.mNewPassphrase = ""; + UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB); + + Assert.assertEquals("exactly three packets should have been modified (the secret keys)", + 3, onlyB.size()); + + // remember secret key packet with no passphrase for later + RawPacket sKeyNoPassphrase = onlyB.get(1); + Assert.assertEquals("extracted packet should be a secret subkey", + PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); + + // modify keyring, change to non-empty passphrase + String otherPassphrase = genPassphrase(true); + parcel.mNewPassphrase = otherPassphrase; + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, ""); + + RawPacket sKeyWithPassphrase = onlyB.get(1); + Assert.assertEquals("extracted packet should be a secret subkey", + PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); + + String otherPassphrase2 = genPassphrase(true); + parcel.mNewPassphrase = otherPassphrase2; + { + // if we replace a secret key with one without passphrase + modified = KeyringTestingHelper.removePacket(modified, sKeyNoPassphrase.position); + modified = KeyringTestingHelper.injectPacket(modified, sKeyNoPassphrase.buf, sKeyNoPassphrase.position); + + // we should still be able to modify it (and change its passphrase) without errors + PgpKeyOperation op = new PgpKeyOperation(null); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0); + EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, otherPassphrase); + Assert.assertTrue("key modification must succeed", result.success()); + Assert.assertFalse("log must not contain a warning", + result.getLog().containsWarnings()); + Assert.assertTrue("log must contain an empty passphrase retry notice", + result.getLog().containsType(LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY)); + modified = result.getRing(); + } + + { + // if we add one subkey with a different passphrase, that should produce a warning but also work + modified = KeyringTestingHelper.removePacket(modified, sKeyWithPassphrase.position); + modified = KeyringTestingHelper.injectPacket(modified, sKeyWithPassphrase.buf, sKeyWithPassphrase.position); + + PgpKeyOperation op = new PgpKeyOperation(null); + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0); + EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, otherPassphrase2); + Assert.assertTrue("key modification must succeed", result.success()); + Assert.assertTrue("log must contain a warning", + result.getLog().containsWarnings()); + Assert.assertTrue("log must contain a failed passphrase change warning", + result.getLog().containsType(LogType.MSG_MF_PASSPHRASE_FAIL)); + } + + } private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel, UncachedKeyRing ring, ArrayList onlyA, ArrayList onlyB) { - return applyModificationWithChecks(parcel, ring, onlyA, onlyB, true, true); + return applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase, true, true); + } + + private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel, + UncachedKeyRing ring, + ArrayList onlyA, + ArrayList onlyB, + String passphrase) { + return applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase, true, true); } // applies a parcel modification while running some integrity checks @@ -696,6 +929,7 @@ public class PgpKeyOperationTest { UncachedKeyRing ring, ArrayList onlyA, ArrayList onlyB, + String passphrase, boolean canonicalize, boolean constantCanonicalize) { @@ -705,8 +939,10 @@ public class PgpKeyOperationTest { 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); + EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase); + Assert.assertTrue("key modification must succeed", result.success()); + UncachedKeyRing rawModified = result.getRing(); + Assert.assertNotNull("key modification must not return null", rawModified); if (!canonicalize) { Assert.assertTrue("keyring must differ from original", KeyringTestingHelper.diffKeyrings( @@ -753,10 +989,75 @@ public class PgpKeyOperationTest { */ @Test public void testConcat() throws Exception { - byte[] actual = TestDataUtil.concatAll(new byte[]{1}, new byte[]{2,-2}, new byte[]{5},new byte[]{3}); + 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)); } + private void assertFailure(String reason, SaveKeyringParcel parcel, LogType expected) { + + EditKeyResult result = op.createSecretKeyRing(parcel); + + Assert.assertFalse(reason, result.success()); + Assert.assertNull(reason, result.getRing()); + Assert.assertTrue(reason + "(with correct error)", + result.getLog().containsType(expected)); + + } + + private void assertModifyFailure(String reason, UncachedKeyRing ring, + SaveKeyringParcel parcel, String passphrase, LogType expected) + throws Exception { + + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0); + EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase); + + Assert.assertFalse(reason, result.success()); + Assert.assertNull(reason, result.getRing()); + Assert.assertTrue(reason + "(with correct error)", + result.getLog().containsType(expected)); + + } + + private void assertModifyFailure(String reason, UncachedKeyRing ring, SaveKeyringParcel parcel, + LogType expected) + throws Exception { + + CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0); + EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase); + + Assert.assertFalse(reason, result.success()); + Assert.assertNull(reason, result.getRing()); + Assert.assertTrue(reason + "(with correct error)", + result.getLog().containsType(expected)); + + } + + private UncachedKeyRing assertCreateSuccess(String reason, SaveKeyringParcel parcel) { + + EditKeyResult result = op.createSecretKeyRing(parcel); + + Assert.assertTrue(reason, result.success()); + Assert.assertNotNull(reason, result.getRing()); + + return result.getRing(); + + } + + private static String genPassphrase() { + return genPassphrase(false); + } + + private static String genPassphrase(boolean noEmpty) { + String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_="; + Random r = new Random(); + StringBuilder passbuilder = new StringBuilder(); + // 20% chance for an empty passphrase + for(int i = 0, j = noEmpty || r.nextInt(10) > 2 ? r.nextInt(20)+1 : 0; i < j; i++) { + passbuilder.append(chars.charAt(r.nextInt(chars.length()))); + } + System.out.println("Generated passphrase: '" + passbuilder.toString() + "'"); + return passbuilder.toString(); + } } diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java index 535e9d01a..5792e8326 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 . + */ + package org.sufficientlysecure.keychain.pgp; import org.junit.BeforeClass; @@ -13,6 +31,7 @@ import org.spongycastle.bcpg.PacketTags; import org.spongycastle.bcpg.UserIDPacket; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; @@ -30,10 +49,12 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import java.io.ByteArrayInputStream; +import java.security.Security; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; @@ -60,15 +81,16 @@ public class UncachedKeyringCanonicalizeTest { @BeforeClass public static void setUpOnce() throws Exception { + Security.insertProviderAt(new BouncyCastleProvider(), 1); ShadowLog.stream = System.out; SaveKeyringParcel parcel = new SaveKeyringParcel(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, null)); + Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L)); parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("pink"); @@ -277,7 +299,7 @@ public class UncachedKeyringCanonicalizeTest { SaveKeyringParcel parcel = new SaveKeyringParcel(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddUserIds.add("trix"); PgpKeyOperation op = new PgpKeyOperation(null); diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java index 6e9381c06..64152a040 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 . + */ + package org.sufficientlysecure.keychain.pgp; import org.junit.Assert; @@ -10,14 +28,17 @@ import org.robolectric.shadows.ShadowLog; import org.spongycastle.bcpg.PacketTags; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; +import org.spongycastle.jce.provider.BouncyCastleProvider; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.util.ProgressScaler; +import java.security.Security; import java.util.ArrayList; import java.util.Iterator; @@ -59,14 +80,15 @@ public class UncachedKeyringMergeTest { @BeforeClass public static void setUpOnce() throws Exception { + Security.insertProviderAt(new BouncyCastleProvider(), 1); ShadowLog.stream = System.out; { SaveKeyringParcel parcel = new SaveKeyringParcel(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("pink"); @@ -83,7 +105,7 @@ public class UncachedKeyringMergeTest { { SaveKeyringParcel parcel = new SaveKeyringParcel(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddUserIds.add("shy"); // passphrase is tested in PgpKeyOperationTest, just use empty here @@ -189,7 +211,7 @@ public class UncachedKeyringMergeTest { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java index cbd1bc502..636dae90c 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 . + */ + package org.sufficientlysecure.keychain.pgp; import org.junit.Assert; @@ -13,6 +31,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import java.io.ByteArrayInputStream; @@ -37,11 +56,11 @@ public class UncachedKeyringTest { SaveKeyringParcel parcel = new SaveKeyringParcel(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null)); + Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null)); + Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, null)); + Algorithm.RSA, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L)); parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("pink"); diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java new file mode 100644 index 000000000..b5d950f59 --- /dev/null +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 . + */ + +package org.sufficientlysecure.keychain.provider; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowLog; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; + +import java.io.IOException; +import java.util.Iterator; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class ProviderHelperSaveTest { + + @BeforeClass + public static void setUpOnce() throws Exception { + ShadowLog.stream = System.out; + } + + @Test + public void testLongKeyIdCollision() throws Exception { + + UncachedKeyRing first = + readRingFromResource("/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc"); + UncachedKeyRing second = + readRingFromResource("/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc"); + + SaveKeyringResult result; + + // insert both keys, second should fail + result = new ProviderHelper(Robolectric.application).savePublicKeyRing(first); + Assert.assertTrue("first keyring import should succeed", result.success()); + result = new ProviderHelper(Robolectric.application).savePublicKeyRing(second); + Assert.assertFalse("second keyring import should fail", result.success()); + + new KeychainDatabase(Robolectric.application).clearDatabase(); + + // and the other way around + result = new ProviderHelper(Robolectric.application).savePublicKeyRing(second); + Assert.assertTrue("first keyring import should succeed", result.success()); + result = new ProviderHelper(Robolectric.application).savePublicKeyRing(first); + Assert.assertFalse("second keyring import should fail", result.success()); + + } + + UncachedKeyRing readRingFromResource(String name) throws Exception { + return UncachedKeyRing.fromStream(ProviderHelperSaveTest.class.getResourceAsStream(name)).next(); + } + +} \ No newline at end of file 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 index 015e134ea..c0ba3c9bb 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java @@ -1,5 +1,6 @@ /* - * Copyright (C) Art O Cathain, Vincent Breitmoser + * Copyright (C) Art O Cathain + * Copyright (C) 2014 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 @@ -14,6 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package org.sufficientlysecure.keychain.support; import android.content.Context; diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/FileImportCacheTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/FileImportCacheTest.java index b5708b46f..c5b53e95b 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/FileImportCacheTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/FileImportCacheTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 . + */ + package org.sufficientlysecure.keychain.util; import android.os.Bundle; @@ -25,7 +43,7 @@ public class FileImportCacheTest { @Test public void testInputOutput() throws Exception { - FileImportCache cache = new FileImportCache(Robolectric.application); + FileImportCache cache = new FileImportCache(Robolectric.application, "test.pcl"); ArrayList list = new ArrayList(); diff --git a/OpenKeychain-Test/src/test/resources/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc b/OpenKeychain-Test/src/test/resources/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc new file mode 100644 index 000000000..4f51252da --- /dev/null +++ b/OpenKeychain-Test/src/test/resources/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQINBFJtd/UBEACpw/psXoGNM8RHczviD7FnGdjMQPEJQ+nuWQ2AEGYouulg5hFv +0ChuSQVLiqQht2k5K2liyW1MeXoJ8tr9nSn/Zi9nttc0Wo6K7pvrDD40r2HNg305 +qLCzItr5st3x8cq2cIXvN4LOm2rqpBLZ/sqMmNiW2Y7/aAQqV1xtR35joHqamWHD +UPOmzBMs07YSUjXgC1EMx8kWQSV6cuARj93kxWj8R6eoYHHfrWCEGR313wov6QST +zIfVU7FqQqOmdLW3LaPHxcrI/TjsnkUN99qdlpjJH/YW925LDPJHAkliqPP5AvhU +F9KbY2F8mcIZBCDd8TH+xXynuN3BbIU4kCwVbdx/tcpO1npuJcKB1Go/udyow/Ei +Z3nHzJsCVkezvopek77wnwPaP0nAb7f4iIY3gJCoGirOx6N075TgF6MBe00q9oFE +y4rvnUnU9/QzOOes95eUMhM+9eK1cuLFEV5t47DfxRdq+fQip3FJ2l6v19sZvQ0G +j06pjYqg0of273rG8oXcDrFjb1Zqhj8x1mLl6u7d/ide5wTm9HylBWcYKQjIJJAi +WIScxEPIOINDJKgsKTuKtoyNvISJ3xUeS1yzxiIb3YGLIyPgFFx0vFyqJfbkXq70 +m1n2xnJlkTidfzbZvc6EA7vRGSDYK6FqqhlGhc7UypUEVW8FM/jZNAOS6QARAUGt +tCg5RTY2OTg2MTM2OEJDQTBCRTQyREFGN0REREEyNTJFQkI4RUJFMUFGiQI3BBMB +CgAhBQJSg/uTAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEN2iUuu46+Gv ++Z0P+wQhkLwm+WGcEsS98Lei9O7hit/k4g/VkLUUQV7BOR3n8uRZIFkdOtpvrFU3 +aKf246uCy6GM48Oh+1U2cv5InX/WEuKaFo5uF6t79wyt18BUn1weDcU+DQdOSG4f +fSnNa55wkN0l0svW4fGIthjmDTz6HZFntYD+9A20wZAqpPIs+vyG9Jp+e9E9Y/W/ +EFQbNlxHHb9+BMT2+DtNP+HSl3MPFlQPKOLZxyLAU5uzT0Sa0LxhrQy5FgkW6Jog +sbAJVM9z0pZw+grzGPciM66ZW1rxeICvbYsdWLytRjqxpY8GS8XudyseUGd+dZim +ptarsrE5yfSMg2gW5Z1PTc0tEMXJLUwtpyzQjpFpbb7dPuo2TUp09LgZKX63WCbS +Nb1RTaGfkeYudOTo2rh4Jfg+Tb/JRpO6clo0rxAq8nPH2WmG+9TB8Zbb7YRzGWuV +/e5SeVNR+zY8tXZKnmUIH1HIprc+BtT6Bupdvd0CT14Mg9MmsFvUXofwHLa4gahr +8/iG9y3uHSA6Rhz++yOpyOmNvO1LDxsYNaRCIXQJbqgNwF5YNYlMPsEeY/CG7FOb +Afv7rHiYtRRQfz2P4OF900DJO7QL9gdNXJ1+Hajy/5Lvvl7qwqMG4GvVQEsgFc5O +jjFCUhE2i20j2kEMxvA5RLBH/fOoGARn87tiKSfb+pqLNZQb +=fDJ8 +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/OpenKeychain-Test/src/test/resources/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc b/OpenKeychain-Test/src/test/resources/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc new file mode 100644 index 000000000..549bc51a2 --- /dev/null +++ b/OpenKeychain-Test/src/test/resources/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQINBFKD+38BEADSv5l4xOx9hCRJVcybq6yK5hTpGSFf3xo1bkhoMvyC62ehb4jD +MDLwwNRyzCBEWQJLbq/LLizPFN2qXFJpXJcsuqsHNYRtDqDBEjtriRQwSqHnqTXt +c0K46FYHldCJQ4/tBXxPI+WwtXjcNRWaV7n2BvR/Jk+B5e4Zz3LPnN0C4w5vORHs +hN1jil8A3Hs/F+OmlQYrU8ZtNwTpSo2EXxe2fVgSDCsKRyNsPZj++OyujPzW+yaN +lJ9I/q6s9gvX9o9o7nwZbqBETipWsdRK6RfBdTKpnyLNordbWwWTk6GxN8T5Ppit +P6a3UlQ71VuflcswCTmEQ1pEfZrlRFKa9psBOW+cZLNxT9h0jGFMh6/B3w48Sag+ +cFcPBFWParC+cAXBIURDxT9G6bzNLogg7YKoaPsyiXnLDH2VJUCXs27D2wPJL24Q +S7npvsg63MPPssWgG5cauLznmNR4y5pQi6oH/C10v0zrUJy6FPJzQhYRhWOvhtz6 +j88RGMrFNNCdB2VACtn699D+ixu3nRlXHIKCT+xLSfgslVYifmJOCNljBLGHOQ1e +FJxQuNVpmmxjvk/8kqK+pHLB9Qn6M1ZYzip7OyUL3OAWabCabgEw2bQmUhiBWD3u +buv0WAVOJEAFvBCAeYNQzrQMY+Rc3RnvynG4pI6Tbo8wC6/IJcDOw516JwARASB3 +tChBNTUxMjA0MjczNzRGM0Y3QUE1RjExNjZEREEyNTJFQkI4RUJFMUFGiQI3BBMB +CgAhBQJSg/uTAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEN2iUuu46+Gv +9L0P/3tFu0LOZ/dAPjUNfKJCZqcIuVnD5xShMTsUbVx+QoXMy7rt4iRLD7ofGi/I +vTAZehxk3sk/Slx5nbews+3NItyw6mcaP9HlmwKNr6k7BC2kJHcCxH4DNzhmIx1H +3T/CggtHX42JBYKlGf22y+M8jAbvsPOUfTznx96mYNrOY6s1dJyn0kRleqJ8+tGj +/5+0y90iZnGCa0FtacQkKUPkXwVodeZVxk8z5OEipShYKc+8dl+5WsvOzHqLC/KY +xCGRb4JaqEMwouLNg8dTNAXXUvFGqJNDX4+andggogmI1hdD9xExfSU9cAGegg2t +vvveC4S+CCHd+zt88iK5ze6F61RxwYhhNbkuFGjdgNGCpHtG/BQhKnYJuKEbq3oi +mgNyxJERlfgaWXveiMG0AmACXN+jCkTtqZjQnsg2N2QDL3tjY7usmuiwRL1aVOFG +Kw5/Cc+2nDeANS3Xi1403Ni269b1c6kNSoLe4zd0WsbO3Kouds8F8EQfeheXQe97 +ZxuvBOMsR9wHC3f0sl/vfxCGdUC+khmKk5taKnUeUFJmVmh5ghlVy8FySHGB0QHO +zd8GUl59rFpQJNpNFQW2YKDhrcjxIr2AeJrdoDI6NsQ02+Qtep/bbq53hqtAD4jF +t3S8vBbTXtRk6g2qn4ojF4SOIc8SAiZcURgVFuSJX8ngFbO4 +=OEw/ +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file diff --git a/OpenKeychain-Test/src/test/resources/cooperpair/readme b/OpenKeychain-Test/src/test/resources/cooperpair/readme new file mode 100644 index 000000000..fecb372d9 --- /dev/null +++ b/OpenKeychain-Test/src/test/resources/cooperpair/readme @@ -0,0 +1,3 @@ +"Cooperpair" testcase under public domain license, by @coruus: + +https://github.com/coruus/cooperpair/tree/master/pgpv4 diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index c10629c6d..e14fb44ba 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="org.sufficientlysecure.keychain" android:installLocation="auto" - android:versionCode="27000" - android:versionName="2.7"> + android:versionCode="28000" + android:versionName="2.8"> + @@ -184,7 +184,9 @@ - + + + @@ -556,6 +558,10 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".ui.KeyListActivity" /> + @@ -628,7 +634,10 @@ android:resource="@xml/account_desc" /> - + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 755d74ac2..9f84da815 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -35,6 +35,9 @@ public final class Constants { public static final String PACKAGE_NAME = "org.sufficientlysecure.keychain"; + public static final String ACCOUNT_NAME = "OpenKeychain"; + public static final String ACCOUNT_TYPE = PACKAGE_NAME + ".account"; + // as defined in http://tools.ietf.org/html/rfc3156, section 7 public static final String NFC_MIME = "application/pgp-keys"; @@ -68,11 +71,14 @@ public final class Constants { public static final String KEY_SERVERS_DEFAULT_VERSION = "keyServersDefaultVersion"; public static final String WRITE_VERSION_HEADER = "writeVersionHeader"; public static final String FIRST_TIME = "firstTime"; + public static final String CACHED_CONSOLIDATE = "cachedConsolidate"; + public static final String CACHED_CONSOLIDATE_SECRETS = "cachedConsolidateSecrets"; + public static final String CACHED_CONSOLIDATE_PUBLICS = "cachedConsolidatePublics"; } public static final class Defaults { - public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, subkeys.pgp.net, hkps://pgp.mit.edu"; - public static final int KEY_SERVERS_VERSION = 2; + public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, hkps://pgp.mit.edu"; + public static final int KEY_SERVERS_VERSION = 3; } public static final class DrawerItems { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index 9b9880533..57d74967b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -20,15 +20,20 @@ package org.sufficientlysecure.keychain; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Application; +import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Environment; +import android.provider.ContactsContract; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.TlsHelper; import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; +import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.PRNGFixes; @@ -89,14 +94,37 @@ public class KeychainApplication extends Application { TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer"); TemporaryStorageProvider.cleanUp(this); + + checkConsolidateRecovery(); + + } + + public void checkConsolidateRecovery() { + + // restart consolidate process if it has been interruped before + if (Preferences.getPreferences(this).getCachedConsolidate()) { + // do something which calls ProviderHelper.consolidateDatabaseStep2 with a progressable + Intent consolidateIntent = new Intent(this, ConsolidateDialogActivity.class); + consolidateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(consolidateIntent); + } + } public static void setupAccountAsNeeded(Context context) { - AccountManager manager = AccountManager.get(context); - Account[] accounts = manager.getAccountsByType(Constants.PACKAGE_NAME); - if (accounts == null || accounts.length == 0) { - Account dummy = new Account(context.getString(R.string.app_name), Constants.PACKAGE_NAME); - manager.addAccountExplicitly(dummy, null, null); + // only enabled for Jelly Bean because we need some newer methods in our sync adapter + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + AccountManager manager = AccountManager.get(context); + Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE); + if (accounts == null || accounts.length == 0) { + Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE); + if (manager.addAccountExplicitly(account, null, null)) { + ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); + } else { + Log.e(Constants.TAG, "Adding account failed!"); + } + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java index 8697e49f7..016d6dc14 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.helper; import android.accounts.Account; import android.accounts.AccountManager; +import android.annotation.TargetApi; import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentUris; @@ -234,6 +235,25 @@ public class ContactHelper { return new ArrayList(mails); } + public static List getContactNames(Context context) { + ContentResolver resolver = context.getContentResolver(); + Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, + new String[]{ContactsContract.Contacts.DISPLAY_NAME}, + null, null, null); + if (cursor == null) return null; + + Set names = new HashSet(); + while (cursor.moveToNext()) { + String name = cursor.getString(0); + if (name != null) { + names.add(name); + } + } + cursor.close(); + return new ArrayList(names); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static Uri dataUriFromContactUri(Context context, Uri contactUri) { Cursor contactMasterKey = context.getContentResolver().query(contactUri, new String[]{ContactsContract.Data.DATA2}, null, null, null, null); @@ -323,7 +343,7 @@ public class ContactHelper { // Delete fingerprints that are no longer present in OK for (String fingerprint : contactFingerprints) { resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, - new String[]{Constants.PACKAGE_NAME, fingerprint}); + new String[]{Constants.ACCOUNT_TYPE, fingerprint}); } } @@ -334,7 +354,7 @@ public class ContactHelper { private static Set getRawContactFingerprints(ContentResolver resolver) { HashSet result = new HashSet(); Cursor fingerprints = resolver.query(ContactsContract.RawContacts.CONTENT_URI, SOURCE_ID_PROJECTION, - ACCOUNT_TYPE_SELECTION, new String[]{Constants.PACKAGE_NAME}, null); + ACCOUNT_TYPE_SELECTION, new String[]{Constants.ACCOUNT_TYPE}, null); if (fingerprints != null) { while (fingerprints.moveToNext()) { result.add(fingerprints.getString(0)); @@ -349,10 +369,11 @@ public class ContactHelper { * * @return raw contact id or -1 if not found */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private static int findRawContactId(ContentResolver resolver, String fingerprint) { int rawContactId = -1; Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, ID_PROJECTION, - ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, new String[]{Constants.PACKAGE_NAME, fingerprint}, null, null); + ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, new String[]{Constants.ACCOUNT_TYPE, fingerprint}, null, null); if (raw != null) { if (raw.moveToNext()) { rawContactId = raw.getInt(0); @@ -367,8 +388,8 @@ public class ContactHelper { */ private static void insertContact(ArrayList ops, Context context, String fingerprint) { ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name)) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.PACKAGE_NAME) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE) .withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint) .build()); } 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 72e88d793..5190a550e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java @@ -25,7 +25,12 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.openpgp.PGPEncryptedData; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Constants.Pref; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.ListIterator; import java.util.Vector; /** @@ -130,6 +135,36 @@ public class Preferences { editor.commit(); } + public boolean getCachedConsolidate() { + return mSharedPreferences.getBoolean(Pref.CACHED_CONSOLIDATE, false); + } + + public void setCachedConsolidate(boolean value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.CACHED_CONSOLIDATE, value); + editor.commit(); + } + + public int getCachedConsolidateNumPublics() { + return mSharedPreferences.getInt(Pref.CACHED_CONSOLIDATE_PUBLICS, -1); + } + + public void setCachedConsolidateNumPublics(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Pref.CACHED_CONSOLIDATE_PUBLICS, value); + editor.commit(); + } + + public int getCachedConsolidateNumSecrets() { + return mSharedPreferences.getInt(Pref.CACHED_CONSOLIDATE_SECRETS, -1); + } + + public void setCachedConsolidateNumSecrets(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Pref.CACHED_CONSOLIDATE_SECRETS, value); + editor.commit(); + } + public boolean isFirstTime() { return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true); } @@ -175,15 +210,24 @@ public class Preferences { // migrate keyserver to hkps if (mSharedPreferences.getInt(Constants.Pref.KEY_SERVERS_DEFAULT_VERSION, 0) != Constants.Defaults.KEY_SERVERS_VERSION) { - String[] servers = getKeyServers(); - for (int i = 0; i < servers.length; i++) { - if (servers[i].equals("pool.sks-keyservers.net")) { - servers[i] = "hkps://hkps.pool.sks-keyservers.net"; - } else if (servers[i].equals("pgp.mit.edu")) { - servers[i] = "hkps://pgp.mit.edu"; + String[] serversArray = getKeyServers(); + ArrayList servers = new ArrayList(Arrays.asList(serversArray)); + ListIterator it = servers.listIterator(); + while (it.hasNext()) { + String server = it.next(); + if (server.equals("pool.sks-keyservers.net")) { + // use HKPS! + it.set("hkps://hkps.pool.sks-keyservers.net"); + } else if (server.equals("pgp.mit.edu")) { + // use HKPS! + it.set("hkps://pgp.mit.edu"); + } else if (server.equals("subkeys.pgp.net")) { + // remove, because often down and no HKPS! + it.remove(); } + } - setKeyServers(servers); + setKeyServers(servers.toArray(new String[servers.size()])); mSharedPreferences.edit() .putInt(Constants.Pref.KEY_SERVERS_DEFAULT_VERSION, Constants.Defaults.KEY_SERVERS_VERSION) .commit(); @@ -193,6 +237,11 @@ public class Preferences { if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, 0) == 0x21070001) { setDefaultFileCompression(CompressionAlgorithmTags.UNCOMPRESSED); } + + // migrate away from MD5 + if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, 0) == HashAlgorithmTags.MD5) { + setDefaultHashAlgorithm(HashAlgorithmTags.SHA512); + } } public void setWriteVersionHeader(boolean conceal) { 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 f617be62a..e30401180 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -24,8 +24,10 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.Log; -import java.io.DataOutputStream; +import java.io.BufferedWriter; import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; @@ -281,7 +283,7 @@ public class HkpKeyserver extends Keyserver { entry.setBitStrength(Integer.parseInt(matcher.group(3))); final int algorithmId = Integer.decode(matcher.group(2)); - entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); + entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null)); // group 1 contains the full fingerprint (v4) or the long key id if available // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr @@ -352,25 +354,38 @@ public class HkpKeyserver extends Keyserver { @Override public void add(String armoredKey) throws AddKeyException { try { - String query = getUrlPrefix() + mHost + ":" + mPort + "/pks/add"; + String request = "/pks/add"; String params; try { - params = "keytext=" + URLEncoder.encode(armoredKey, "utf8"); + params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new AddKeyException(); } - Log.d(Constants.TAG, "hkp keyserver add: " + query); + URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request); - HttpURLConnection connection = openConnection(new URL(query)); - connection.setRequestMethod("POST"); - connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - connection.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length)); - connection.setDoOutput(true); - DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); - wr.writeBytes(params); - wr.flush(); - wr.close(); + Log.d(Constants.TAG, "hkp keyserver add: " + url.toString()); + Log.d(Constants.TAG, "params: " + params); + + HttpURLConnection conn = openConnection(url); + conn.setRequestMethod("POST"); + conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length)); + conn.setDoInput(true); + conn.setDoOutput(true); + + OutputStream os = conn.getOutputStream(); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); + writer.write(params); + writer.flush(); + writer.close(); + os.close(); + + conn.connect(); + + Log.d(Constants.TAG, "response code: " + conn.getResponseCode()); + Log.d(Constants.TAG, "answer: " + readAll(conn.getInputStream(), conn.getContentEncoding())); } catch (IOException e) { + Log.e(Constants.TAG, "IOException", e); throw new AddKeyException(); } } 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 30e93f957..da70f1505 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -39,7 +39,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { private boolean mExpired; private Date mDate; // TODO: not displayed private String mFingerprintHex; - private int mBitStrength; + private Integer mBitStrength; + private String mCurveOid; private String mAlgorithm; private boolean mSecretKey; private String mPrimaryUserId; @@ -162,10 +163,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.mFingerprintHex = fingerprintHex; } - public int getBitStrength() { + public Integer getBitStrength() { return mBitStrength; } + public String getCurveOid() { + return mCurveOid; + } + public void setBitStrength(int bitStrength) { this.mBitStrength = bitStrength; } @@ -258,13 +263,15 @@ public class ImportKeysListEntry implements Serializable, Parcelable { mPrimaryUserId = mUserIds.get(0); } - this.mKeyId = key.getKeyId(); - this.mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId); + mKeyId = key.getKeyId(); + mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId); - this.mRevoked = key.isRevoked(); - this.mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); - this.mBitStrength = key.getBitStrength(); + mRevoked = key.isRevoked(); + mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); + mBitStrength = key.getBitStrength(); + mCurveOid = key.getCurveOid(); final int algorithm = key.getAlgorithm(); - this.mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm); + mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm, mBitStrength, mCurveOid); } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 0ca6f07fd..cbd06da90 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -75,7 +75,7 @@ public class KeybaseKeyserver extends Keyserver { entry.setExtraData(username); final int algorithmId = match.getAlgorithmId(); - entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); + entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null)); final int bitStrength = match.getBitStrength(); entry.setBitStrength(bitStrength); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index 8609a7082..55e6be9b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java index 1da66872d..8076a14f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java index ce6498df1..7a63a7a42 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java index 972e45c2e..2c27c5c37 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -60,10 +61,6 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing { return mRing; } - public void encode(ArmoredOutputStream stream) throws IOException { - getRing().encode(stream); - } - /** Getter that returns the subkey that should be used for signing. */ CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException { PGPPublicKey key = getRing().getPublicKey(getEncryptId()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index 877857553..c79dc45c3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java index 4f74a2336..d8b873d31 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 35020b815..3ef4b336e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 7d113241b..828bee848 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -523,13 +523,7 @@ public class PgpDecryptVerify { // update signature buffer if signature is also present if (signature != null) { - try { - signature.update(buffer, 0, length); - } catch (SignatureException e) { - Log.e(Constants.TAG, "SignatureException -> Not a valid signature!", e); - signatureResultBuilder.validSignature(false); - signature = null; - } + signature.update(buffer, 0, length); } alreadyWritten += length; 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 1cf027721..db360f810 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -29,7 +29,6 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.RandomAccessFile; import java.security.SecureRandom; import java.util.regex.Pattern; @@ -68,76 +67,6 @@ public class PgpHelper { } } -// public static final class content { -// public static final int unknown = 0; -// public static final int encrypted_data = 1; -// public static final int keys = 2; -// } -// -// public static int getStreamContent(Context context, InputStream inStream) throws IOException { -// -// InputStream in = PGPUtil.getDecoderStream(inStream); -// PGPObjectFactory pgpF = new PGPObjectFactory(in); -// Object object = pgpF.nextObject(); -// while (object != null) { -// if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) { -// return Id.content.keys; -// } else if (object instanceof PGPEncryptedDataList) { -// return Id.content.encrypted_data; -// } -// object = pgpF.nextObject(); -// } -// -// return Id.content.unknown; -// } - - /** - * Generate a random filename - * - * @param length - * @return - */ - public static String generateRandomFilename(int length) { - SecureRandom random = new SecureRandom(); - - byte bytes[] = new byte[length]; - random.nextBytes(bytes); - String result = ""; - for (int i = 0; i < length; ++i) { - int v = (bytes[i] + 256) % 64; - if (v < 10) { - result += (char) ('0' + v); - } else if (v < 36) { - result += (char) ('A' + v - 10); - } else if (v < 62) { - result += (char) ('a' + v - 36); - } else if (v == 62) { - result += '_'; - } else if (v == 63) { - result += '.'; - } - } - return result; - } - - /** - * Go once through stream to get length of stream. The length is later used to display progress - * when encrypting/decrypting - * - * @param in - * @return - * @throws IOException - */ - public static long getLengthOfStream(InputStream in) throws IOException { - long size = 0; - long n = 0; - byte dummy[] = new byte[0x10000]; - while ((n = in.read(dummy)) > 0) { - size += n; - } - return size; - } - /** * Deletes file securely by overwriting it with random data before deleting it. *

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 f14eacda2..0bc3ac0ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; +import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -43,6 +44,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; public class PgpImportExport { @@ -60,10 +62,14 @@ public class PgpImportExport { private ProviderHelper mProviderHelper; public PgpImportExport(Context context, Progressable progressable) { + this(context, new ProviderHelper(context), progressable); + } + + public PgpImportExport(Context context, ProviderHelper providerHelper, Progressable progressable) { super(); this.mContext = context; this.mProgressable = progressable; - this.mProviderHelper = new ProviderHelper(context); + this.mProviderHelper = providerHelper; } public PgpImportExport(Context context, @@ -93,7 +99,7 @@ public class PgpImportExport { } } - public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) { + public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = null; try { @@ -103,13 +109,9 @@ public class PgpImportExport { String armoredKey = bos.toString("UTF-8"); server.add(armoredKey); - - return true; } catch (IOException e) { - return false; - } catch (AddKeyException e) { - // TODO: tell the user? - return false; + Log.e(Constants.TAG, "IOException", e); + throw new AddKeyException(); } finally { try { if (aos != null) { @@ -124,20 +126,23 @@ public class PgpImportExport { /** Imports keys from given data. If keyIds is given only those are imported */ public ImportKeyResult importKeyRings(List entries) { + return importKeyRings(entries.iterator(), entries.size()); + } + public ImportKeyResult importKeyRings(Iterator entries, int num) { updateProgress(R.string.progress_importing, 0, 100); // If there aren't even any keys, do nothing here. - if (entries == null || entries.size() == 0) { + if (entries == null || !entries.hasNext()) { return new ImportKeyResult( - ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0); + ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0, 0); } - int newKeys = 0, oldKeys = 0, badKeys = 0; + int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0; int position = 0; - int progSteps = 100 / entries.size(); - for (ParcelableKeyRing entry : entries) { + double progSteps = 100.0 / num; + for (ParcelableKeyRing entry : new IterableIterator(entries)) { try { UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes()); @@ -157,10 +162,10 @@ public class PgpImportExport { SaveKeyringResult result; if (key.isSecret()) { result = mProviderHelper.saveSecretKeyRing(key, - new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); + new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100)); } else { result = mProviderHelper.savePublicKeyRing(key, - new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100)); + new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100)); } if (!result.success()) { badKeys += 1; @@ -168,6 +173,9 @@ public class PgpImportExport { oldKeys += 1; } else { newKeys += 1; + if (key.isSecret()) { + secret += 1; + } } } catch (IOException e) { @@ -204,7 +212,7 @@ public class PgpImportExport { } } - return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys); + return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 459b80be2..05acb86df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -24,10 +24,16 @@ import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.bcpg.ECPublicBCPGKey; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; import org.sufficientlysecure.keychain.util.Log; import java.security.DigestException; @@ -37,18 +43,14 @@ import java.util.Locale; public class PgpKeyHelper { - public static String getAlgorithmInfo(int algorithm) { - return getAlgorithmInfo(null, algorithm, 0); - } - - public static String getAlgorithmInfo(Context context, int algorithm) { - return getAlgorithmInfo(context, algorithm, 0); + public static String getAlgorithmInfo(int algorithm, Integer keySize, String oid) { + return getAlgorithmInfo(null, algorithm, keySize, oid); } /** * Based on OpenPGP Message Format */ - public static String getAlgorithmInfo(Context context, int algorithm, int keySize) { + public static String getAlgorithmInfo(Context context, int algorithm, Integer keySize, String oid) { String algorithmStr; switch (algorithm) { @@ -69,10 +71,19 @@ public class PgpKeyHelper { break; } - case PublicKeyAlgorithmTags.ECDSA: + case PublicKeyAlgorithmTags.ECDSA: { + if (oid == null) { + return "ECDSA"; + } + String oidName = PgpKeyHelper.getCurveInfo(context, oid); + return "ECDSA (" + oidName + ")"; + } case PublicKeyAlgorithmTags.ECDH: { - algorithmStr = "ECC"; - break; + if (oid == null) { + return "ECDH"; + } + String oidName = PgpKeyHelper.getCurveInfo(context, oid); + return "ECDH (" + oidName + ")"; } default: { @@ -90,6 +101,106 @@ public class PgpKeyHelper { return algorithmStr; } + public static String getAlgorithmInfo(Algorithm algorithm, Integer keySize, Curve curve) { + return getAlgorithmInfo(null, algorithm, keySize, curve); + } + + /** + * Based on OpenPGP Message Format + */ + public static String getAlgorithmInfo(Context context, Algorithm algorithm, Integer keySize, Curve curve) { + String algorithmStr; + + switch (algorithm) { + case RSA: { + algorithmStr = "RSA"; + break; + } + case DSA: { + algorithmStr = "DSA"; + break; + } + + case ELGAMAL: { + algorithmStr = "ElGamal"; + break; + } + + case ECDSA: { + algorithmStr = "ECDSA"; + if (curve != null) { + algorithmStr += " (" + getCurveInfo(context, curve) + ")"; + } + return algorithmStr; + } + case ECDH: { + algorithmStr = "ECDH"; + if (curve != null) { + algorithmStr += " (" + getCurveInfo(context, curve) + ")"; + } + return algorithmStr; + } + + default: { + if (context != null) { + algorithmStr = context.getResources().getString(R.string.unknown_algorithm); + } else { + algorithmStr = "unknown"; + } + break; + } + } + if (keySize != null && keySize > 0) + return algorithmStr + ", " + keySize + " bit"; + else + return algorithmStr; + } + + // Return name of a curve. These are names, no need for translation + public static String getCurveInfo(Context context, Curve curve) { + switch(curve) { + case NIST_P256: + return "NIST P-256"; + case NIST_P384: + return "NIST P-384"; + case NIST_P521: + return "NIST P-521"; + + /* see SaveKeyringParcel + case BRAINPOOL_P256: + return "Brainpool P-256"; + case BRAINPOOL_P384: + return "Brainpool P-384"; + case BRAINPOOL_P512: + return "Brainpool P-512"; + */ + } + if (context != null) { + return context.getResources().getString(R.string.unknown_algorithm); + } else { + return "unknown"; + } + } + + public static String getCurveInfo(Context context, String oidStr) { + ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(oidStr); + + String name; + name = NISTNamedCurves.getName(oid); + if (name != null) { + return name; + } + name = TeleTrusTNamedCurves.getName(oid); + if (name != null) { + return name; + } + if (context != null) { + return context.getResources().getString(R.string.unknown_algorithm); + } else { + return "unknown"; + } + } + /** * Converts fingerprint to hex *

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 bb692555e..967a7caa9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2010-2014 Thialfihar + * Copyright (C) 2014 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 @@ -20,12 +20,12 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.spongycastle.bcpg.sig.Features; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.jce.spec.ElGamalParameterSpec; -import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyFlags; import org.spongycastle.openpgp.PGPKeyPair; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPublicKey; @@ -34,7 +34,6 @@ 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.PBESecretKeyEncryptor; import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; @@ -53,6 +52,8 @@ 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.Algorithm; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -67,6 +68,7 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.SignatureException; +import java.security.spec.ECGenParameterSpec; import java.util.Arrays; import java.util.Date; import java.util.Iterator; @@ -84,15 +86,45 @@ import java.util.Stack; public class PgpKeyOperation { private Stack mProgress; + // most preferred is first private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{ - SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, - SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5, - SymmetricKeyAlgorithmTags.TRIPLE_DES}; - private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{HashAlgorithmTags.SHA1, - HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160}; + SymmetricKeyAlgorithmTags.AES_256, + SymmetricKeyAlgorithmTags.AES_192, + SymmetricKeyAlgorithmTags.AES_128, + SymmetricKeyAlgorithmTags.CAST5 + }; + private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{ + HashAlgorithmTags.SHA512, + HashAlgorithmTags.SHA384, + HashAlgorithmTags.SHA224, + HashAlgorithmTags.SHA256, + HashAlgorithmTags.RIPEMD160 + }; private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{ - CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2, - CompressionAlgorithmTags.ZIP}; + CompressionAlgorithmTags.ZLIB, + CompressionAlgorithmTags.BZIP2, + CompressionAlgorithmTags.ZIP + }; + + /* + * Note: s2kcount is a number between 0 and 0xff that controls the + * number of times to iterate the password hash before use. More + * iterations are useful against offline attacks, as it takes more + * time to check each password. The actual number of iterations is + * rather complex, and also depends on the hash function in use. + * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give + * you more iterations. As a rough rule of thumb, when using + * SHA256 as the hashing function, 0x10 gives you about 64 + * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0, + * or about 1 million iterations. The maximum you can go to is + * 0xff, or about 2 million iterations. I'll use 0xc0 as a + * default -- about 130,000 iterations. + * + * http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html + */ + private static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA512; + private static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256; + private static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x60; public PgpKeyOperation(Progressable progress) { super(); @@ -126,31 +158,65 @@ public class PgpKeyOperation { mProgress.peek().setProgress(message, current, 100); } + private ECGenParameterSpec getEccParameterSpec(Curve curve) { + switch (curve) { + case NIST_P256: return new ECGenParameterSpec("P-256"); + case NIST_P384: return new ECGenParameterSpec("P-384"); + case NIST_P521: return new ECGenParameterSpec("P-521"); + + // @see SaveKeyringParcel + // case BRAINPOOL_P256: return new ECGenParameterSpec("brainpoolp256r1"); + // case BRAINPOOL_P384: return new ECGenParameterSpec("brainpoolp384r1"); + // case BRAINPOOL_P512: return new ECGenParameterSpec("brainpoolp512r1"); + } + throw new RuntimeException("Invalid choice! (can't happen)"); + } + /** Creates new secret key. */ - private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) { + private PGPKeyPair createKey(SubkeyAdd add, OperationLog log, int indent) { try { - if (keySize < 512) { - log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent); - return null; + // Some safety checks + if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) { + if (add.mCurve == null) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CURVE, indent); + return null; + } + } else { + if (add.mKeySize == null) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_KEYSIZE, indent); + return null; + } + if (add.mKeySize < 512) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent); + return null; + } } int algorithm; KeyPairGenerator keyGen; - switch (algorithmChoice) { - case PublicKeyAlgorithmTags.DSA: { + switch (add.mAlgorithm) { + case DSA: { + if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_DSA, indent); + return null; + } progress(R.string.progress_generating_dsa, 30); keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); + keyGen.initialize(add.mKeySize, new SecureRandom()); algorithm = PGPPublicKey.DSA; break; } - case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: { + case ELGAMAL: { + if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent); + return null; + } progress(R.string.progress_generating_elgamal, 30); keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - BigInteger p = Primes.getBestPrime(keySize); + BigInteger p = Primes.getBestPrime(add.mKeySize); BigInteger g = new BigInteger("2"); ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g); @@ -160,15 +226,44 @@ public class PgpKeyOperation { break; } - case PublicKeyAlgorithmTags.RSA_GENERAL: { + case RSA: { progress(R.string.progress_generating_rsa, 30); keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); + keyGen.initialize(add.mKeySize, new SecureRandom()); algorithm = PGPPublicKey.RSA_GENERAL; break; } + case ECDSA: { + if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent); + return null; + } + progress(R.string.progress_generating_ecdsa, 30); + ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve); + keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(ecParamSpec, new SecureRandom()); + + algorithm = PGPPublicKey.ECDSA; + break; + } + + case ECDH: { + // make sure there are no sign or certify flags set + if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDH, indent); + return null; + } + progress(R.string.progress_generating_ecdh, 30); + ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve); + keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(ecParamSpec, new SecureRandom()); + + algorithm = PGPPublicKey.ECDH; + break; + } + default: { log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent); return null; @@ -181,7 +276,8 @@ public class PgpKeyOperation { } catch(NoSuchProviderException e) { throw new RuntimeException(e); } catch(NoSuchAlgorithmException e) { - throw new RuntimeException(e); + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent); + return null; } catch(InvalidAlgorithmParameterException e) { throw new RuntimeException(e); } catch(PGPException e) { @@ -218,13 +314,13 @@ public class PgpKeyOperation { return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } - if (add.mAlgorithm == PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT) { - log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent); + if (add.mExpiry == null) { + log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NULL_EXPIRY, indent); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } subProgressPush(10, 30); - PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); + PGPKeyPair keyPair = createKey(add, log, indent); subProgressPop(); // return null if this failed (an error will already have been logged by createKey) @@ -234,13 +330,16 @@ public class PgpKeyOperation { progress(R.string.progress_building_master_key, 40); - // define hashing and signing algos + // Build key encrypter and decrypter based on passphrase + PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder() + .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); + + // NOTE: only SHA1 is supported for key checksum calculations. 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()); PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), sha1Calc, true, keyEncryptor); @@ -248,7 +347,7 @@ public class PgpKeyOperation { masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); subProgressPush(50, 100); - return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log); + return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log); } catch (PGPException e) { log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); @@ -314,14 +413,17 @@ public class PgpKeyOperation { // 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; + PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); + int masterKeyFlags = readKeyFlags(masterPublicKey) | KeyFlags.CERTIFY_OTHER; + long masterKeyExpiry = masterPublicKey.getValidSeconds() == 0L ? 0L : + masterPublicKey.getCreationTime().getTime() / 1000 + masterPublicKey.getValidSeconds(); - return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log); + return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log); } private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, - int masterKeyFlags, + int masterKeyFlags, long masterKeyExpiry, SaveKeyringParcel saveParcel, String passphrase, OperationLog log) { @@ -346,177 +448,196 @@ public class PgpKeyOperation { } } - // work on master secret key try { - PGPPublicKey modifiedPublicKey = masterPublicKey; + { // work on master secret key - // 2a. Add certificates for new user ids - subProgressPush(15, 25); - for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) { + PGPPublicKey modifiedPublicKey = masterPublicKey; - progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddUserIds.size())); - String userId = saveParcel.mAddUserIds.get(i); - log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId); + // 2a. Add certificates for new user ids + subProgressPush(15, 25); + for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) { - if (userId.equals("")) { - log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1); - return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); - } + progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size())); + String userId = saveParcel.mAddUserIds.get(i); + log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId); - // this operation supersedes all previous binding and revocation certificates, - // so remove those to retain assertions from canonicalization for later operations - @SuppressWarnings("unchecked") - Iterator it = modifiedPublicKey.getSignaturesForID(userId); - if (it != null) { - for (PGPSignature cert : new IterableIterator(it)) { - if (cert.getKeyID() != masterPublicKey.getKeyID()) { - // 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 - || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION - || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION - || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) { - modifiedPublicKey = PGPPublicKey.removeCertification( - modifiedPublicKey, userId, cert); - } - } - } - - // if it's supposed to be primary, we can do that here as well - boolean isPrimary = saveParcel.mChangePrimaryUserId != null - && userId.equals(saveParcel.mChangePrimaryUserId); - // generate and add new certificate - PGPSignature cert = generateUserIdSignature(masterPrivateKey, - masterPublicKey, userId, isPrimary, masterKeyFlags); - modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); - } - subProgressPop(); - - // 2b. Add revocations for revoked user ids - subProgressPush(25, 40); - for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) { - - progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mRevokeUserIds.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(modifiedPublicKey, userId, cert); - } - subProgressPop(); - - // 3. If primary user id changed, generate new certificates for both old and new - 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 - for (String userId : new IterableIterator(modifiedPublicKey.getUserIDs())) { - boolean isRevoked = false; - PGPSignature currentCert = null; - // noinspection unchecked - for (PGPSignature cert : new IterableIterator( - modifiedPublicKey.getSignaturesForID(userId))) { - if (cert.getKeyID() != masterPublicKey.getKeyID()) { - // 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. - if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) { - isRevoked = true; - continue; - } - // we know from canonicalization that there is only one binding - // certification here, so we can just work with the first one. - if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION || - cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION || - cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION || - cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) { - currentCert = cert; - } - } - - if (currentCert == null) { - // no certificate found?! error error error - log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); + if (userId.equals("")) { + log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1); 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.mChangePrimaryUserId)) { - log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent); + // this operation supersedes all previous binding and revocation certificates, + // so remove those to retain assertions from canonicalization for later operations + @SuppressWarnings("unchecked") + Iterator it = modifiedPublicKey.getSignaturesForID(userId); + if (it != null) { + for (PGPSignature cert : new IterableIterator(it)) { + if (cert.getKeyID() != masterPublicKey.getKeyID()) { + // 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 + || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION + || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION + || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) { + modifiedPublicKey = PGPPublicKey.removeCertification( + modifiedPublicKey, userId, cert); + } + } + } + + // if it's supposed to be primary, we can do that here as well + boolean isPrimary = saveParcel.mChangePrimaryUserId != null + && userId.equals(saveParcel.mChangePrimaryUserId); + // generate and add new certificate + PGPSignature cert = generateUserIdSignature(masterPrivateKey, + masterPublicKey, userId, isPrimary, masterKeyFlags, masterKeyExpiry); + modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert); + } + subProgressPop(); + + // 2b. Add revocations for revoked user ids + subProgressPush(25, 40); + for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) { + + progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size())); + String userId = saveParcel.mRevokeUserIds.get(i); + log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId); + + // Make sure the user id exists (yes these are 10 LoC in Java!) + boolean exists = false; + //noinspection unchecked + for (String uid : new IterableIterator(modifiedPublicKey.getUserIDs())) { + if (userId.equals(uid)) { + exists = true; + break; + } + } + if (!exists) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent); + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); + } + + // 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(modifiedPublicKey, userId, cert); + } + subProgressPop(); + + // 3. If primary user id changed, generate new certificates for both old and new + 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 + for (String userId : new IterableIterator(modifiedPublicKey.getUserIDs())) { + boolean isRevoked = false; + PGPSignature currentCert = null; + // noinspection unchecked + for (PGPSignature cert : new IterableIterator( + modifiedPublicKey.getSignaturesForID(userId))) { + if (cert.getKeyID() != masterPublicKey.getKeyID()) { + // 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. + if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) { + isRevoked = true; + continue; + } + // we know from canonicalization that there is only one binding + // certification here, so we can just work with the first one. + if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION || + cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION || + cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION || + cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) { + currentCert = cert; + } + } + + if (currentCert == null) { + // no certificate found?! error error error + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } - continue; - } - // if this is~ the/a primary user id - if (currentCert.getHashedSubPackets() != null - && currentCert.getHashedSubPackets().isPrimaryUserID()) { - // if it's the one we want, just leave it as is - if (userId.equals(saveParcel.mChangePrimaryUserId)) { - ok = true; + // 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.mChangePrimaryUserId)) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent); + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); + } 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, masterKeyFlags); - modifiedPublicKey = PGPPublicKey.addCertification( - modifiedPublicKey, userId, newCert); - continue; + + // if this is~ the/a primary user id + if (currentCert.getHashedSubPackets() != null + && currentCert.getHashedSubPackets().isPrimaryUserID()) { + // if it's the one we want, just leave it as is + 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, + masterKeyFlags, masterKeyExpiry); + modifiedPublicKey = PGPPublicKey.addCertification( + modifiedPublicKey, userId, newCert); + continue; + } + + // if we are here, this is not currently a primary user id + + // if it should be + 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, + masterKeyFlags, masterKeyExpiry); + modifiedPublicKey = PGPPublicKey.addCertification( + modifiedPublicKey, userId, newCert); + ok = true; + } + + // user id is not primary and is not supposed to be - nothing to do here. + } - // if we are here, this is not currently a primary user id + indent -= 1; - // if it should be - 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, masterKeyFlags); - modifiedPublicKey = PGPPublicKey.addCertification( - modifiedPublicKey, userId, newCert); - ok = true; + if (!ok) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent); + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } - - // 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 + if (modifiedPublicKey != masterPublicKey) { + masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey); + masterPublicKey = modifiedPublicKey; + sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); } - } - // Update the secret key ring - if (modifiedPublicKey != masterPublicKey) { - masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey); - masterPublicKey = modifiedPublicKey; - sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); } // 4a. For each subkey change, generate new subkey binding certificate @@ -528,27 +649,47 @@ public class PgpKeyOperation { 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, + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SUBKEY_MISSING, indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId)); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } - PGPPublicKey pKey = sKey.getPublicKey(); // 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, + if (change.mExpiry != null && change.mExpiry != 0 && + new Date(change.mExpiry*1000).before(new Date())) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY, indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId)); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } + // if this is the master key, update uid certificates instead + if (change.mKeyId == masterPublicKey.getKeyID()) { + int flags = change.mFlags == null ? masterKeyFlags : change.mFlags; + long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry; + + if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1); + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); + } + + PGPPublicKey pKey = + updateMasterCertificates(masterPrivateKey, masterPublicKey, + flags, expiry, indent, log); + if (pKey == null) { + // error log entry has already been added by updateMasterCertificates itself + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); + } + masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, pKey); + masterPublicKey = pKey; + sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey); + continue; + } + + // otherwise, continue working on the public key + PGPPublicKey pKey = sKey.getPublicKey(); + // keep old flags, or replace with new ones int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags; long expiry; @@ -565,7 +706,7 @@ public class PgpKeyOperation { //noinspection unchecked for (PGPSignature sig : new IterableIterator(pKey.getSignatures())) { // special case: if there is a revocation, don't use expiry from before - if (change.mExpiry == null + if ( (change.mExpiry == null || change.mExpiry == 0L) && sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) { expiry = 0; } @@ -591,7 +732,7 @@ public class PgpKeyOperation { PGPSecretKey sKey = sKR.getSecretKey(revocation); if (sKey == null) { - log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING, + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SUBKEY_MISSING, indent+1, PgpKeyHelper.convertKeyIdToHex(revocation)); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } @@ -611,10 +752,16 @@ public class PgpKeyOperation { progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size())); SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i); - log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent); + log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent, + PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) ); - if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) { - log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1); + if (add.mExpiry == null) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1); + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); + } + + if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } @@ -623,9 +770,10 @@ public class PgpKeyOperation { (i-1) * (100 / saveParcel.mAddSubKeys.size()), i * (100 / saveParcel.mAddSubKeys.size()) ); - PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent); + PGPKeyPair keyPair = createKey(add, log, indent); subProgressPop(); - if(keyPair == null) { + if (keyPair == null) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent +1); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } @@ -633,21 +781,21 @@ public class PgpKeyOperation { PGPPublicKey pKey = keyPair.getPublicKey(); PGPSignature cert = generateSubkeyBindingSignature( masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey, - add.mFlags, add.mExpiry == null ? 0 : add.mExpiry); + add.mFlags, add.mExpiry); pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert); PGPSecretKey sKey; { - // define hashing and signing algos + // Build key encrypter and decrypter based on passphrase + PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder() + .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + + // NOTE: only SHA1 is supported for key checksum calculations. 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); + sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor); } log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID, @@ -662,21 +810,67 @@ public class PgpKeyOperation { 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); + indent += 1; + + PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() + .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); // Build key encryptor based on new passphrase PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) + SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( saveParcel.mNewPassphrase.toCharArray()); - sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew); + // noinspection unchecked + for (PGPSecretKey sKey : new IterableIterator(sKR.getSecretKeys())) { + log.add(LogLevel.DEBUG, LogType.MSG_MF_PASSPHRASE_KEY, indent, + PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID())); + + boolean ok = false; + + try { + // try to set new passphrase + sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew); + ok = true; + } catch (PGPException e) { + + // if this is the master key, error! + if (sKey.getKeyID() == masterPublicKey.getKeyID()) { + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1); + return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); + } + + // being in here means decrypt failed, likely due to a bad passphrase try + // again with an empty passphrase, maybe we can salvage this + try { + log.add(LogLevel.DEBUG, LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1); + PBESecretKeyDecryptor emptyDecryptor = + new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray()); + sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew); + ok = true; + } catch (PGPException e2) { + // non-fatal but not ok, handled below + } + } + + if (!ok) { + // for a subkey, it's merely a warning + log.add(LogLevel.WARN, LogType.MSG_MF_PASSPHRASE_FAIL, indent+1, + PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID())); + continue; + } + + sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey); + + } + + indent -= 1; } - // This one must only be thrown by } catch (IOException e) { + Log.e(Constants.TAG, "encountered IOException while modifying key", e); log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } catch (PGPException e) { @@ -684,6 +878,7 @@ public class PgpKeyOperation { log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } catch (SignatureException e) { + Log.e(Constants.TAG, "encountered SignatureException while modifying key", e); log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1); return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null); } @@ -694,21 +889,121 @@ public class PgpKeyOperation { } + /** Update all (non-revoked) uid signatures with new flags and expiry time. */ + private static PGPPublicKey updateMasterCertificates( + PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey, + int flags, long expiry, int indent, OperationLog log) + throws PGPException, IOException, SignatureException { + + // keep track if we actually changed one + boolean ok = false; + log.add(LogLevel.DEBUG, LogType.MSG_MF_MASTER, indent); + indent += 1; + + PGPPublicKey modifiedPublicKey = masterPublicKey; + + // we work on the modifiedPublicKey here, to respect new or newly revoked uids + // noinspection unchecked + for (String userId : new IterableIterator(modifiedPublicKey.getUserIDs())) { + boolean isRevoked = false; + PGPSignature currentCert = null; + // noinspection unchecked + for (PGPSignature cert : new IterableIterator( + modifiedPublicKey.getSignaturesForID(userId))) { + if (cert.getKeyID() != masterPublicKey.getKeyID()) { + // foreign certificate?! error error error + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); + return null; + } + // we know from canonicalization that if there is any revocation here, it + // is valid and not superseded by a newer certification. + if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) { + isRevoked = true; + continue; + } + // we know from canonicalization that there is only one binding + // certification here, so we can just work with the first one. + if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION || + cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION || + cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION || + cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) { + currentCert = cert; + } + } + + if (currentCert == null) { + // no certificate found?! error error error + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent); + return null; + } + + // we definitely should not update certifications of revoked keys, so just leave it. + if (isRevoked) { + continue; + } + + // add shiny new user id certificate + boolean isPrimary = currentCert.getHashedSubPackets() != null && + currentCert.getHashedSubPackets().isPrimaryUserID(); + modifiedPublicKey = PGPPublicKey.removeCertification( + modifiedPublicKey, userId, currentCert); + PGPSignature newCert = generateUserIdSignature( + masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry); + modifiedPublicKey = PGPPublicKey.addCertification( + modifiedPublicKey, userId, newCert); + ok = true; + + } + + if (!ok) { + // might happen, theoretically, if there is a key with no uid.. + log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_MASTER_NONE, indent); + return null; + } + + return modifiedPublicKey; + + } + private static PGPSignature generateUserIdSignature( - PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags) + PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, + int flags, long expiry) throws IOException, PGPException, SignatureException { PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - pKey.getAlgorithm(), PGPUtil.SHA1) + masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, new Date()); - subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); - subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); - subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - subHashedPacketsGen.setPrimaryUserID(false, primary); - subHashedPacketsGen.setKeyFlags(false, flags); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); + + PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + { + /* + * From RFC about critical subpackets: + * If a subpacket is encountered that is + * marked critical but is unknown to the evaluating software, the + * evaluator SHOULD consider the signature to be in error. + * An evaluator may "recognize" a subpacket, but not implement it. The + * purpose of the critical bit is to allow the signer to tell an + * evaluator that it would prefer a new, unknown feature to generate an + * error than be ignored. + */ + /* non-critical subpackets: */ + hashedPacketsGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS); + hashedPacketsGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS); + hashedPacketsGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS); + hashedPacketsGen.setPrimaryUserID(false, primary); + + /* critical subpackets: we consider those important for a modern pgp implementation */ + hashedPacketsGen.setSignatureCreationTime(true, new Date()); + // Request that senders add the MDC to the message (authenticate unsigned messages) + hashedPacketsGen.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION); + hashedPacketsGen.setKeyFlags(true, flags); + if (expiry > 0) { + hashedPacketsGen.setKeyExpirationTime( + true, expiry - pKey.getCreationTime().getTime() / 1000); + } + } + + sGen.setHashedSubpackets(hashedPacketsGen.generate()); sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); return sGen.generateCertification(userId, pKey); } @@ -717,11 +1012,11 @@ public class PgpKeyOperation { PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId) throws IOException, PGPException, SignatureException { PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - pKey.getAlgorithm(), PGPUtil.SHA1) + masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, new Date()); + subHashedPacketsGen.setSignatureCreationTime(true, new Date()); sGen.setHashedSubpackets(subHashedPacketsGen.generate()); sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey); return sGen.generateCertification(userId, pKey); @@ -731,11 +1026,11 @@ public class PgpKeyOperation { PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey) throws IOException, PGPException, SignatureException { PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - pKey.getAlgorithm(), PGPUtil.SHA1) + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, new Date()); + subHashedPacketsGen.setSignatureCreationTime(true, new Date()); sGen.setHashedSubpackets(subHashedPacketsGen.generate()); // Generate key revocation or subkey revocation, depending on master/subkey-ness if (masterPublicKey.getKeyID() == pKey.getKeyID()) { @@ -765,38 +1060,38 @@ public class PgpKeyOperation { throws IOException, PGPException, SignatureException { // date for signing - Date todayDate = new Date(); + Date creationTime = new Date(); + PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); // If this key can sign, we need a primary key binding signature if ((flags & KeyFlags.SIGN_DATA) > 0) { // cross-certify signing keys PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, todayDate); + subHashedPacketsGen.setSignatureCreationTime(false, creationTime); PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - pKey.getAlgorithm(), PGPUtil.SHA1) + pKey.getAlgorithm(), HashAlgorithmTags.SHA512) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); sGen.setHashedSubpackets(subHashedPacketsGen.generate()); PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey); - unhashedPacketsGen.setEmbeddedSignature(false, certification); + unhashedPacketsGen.setEmbeddedSignature(true, certification); } PGPSignatureSubpacketGenerator hashedPacketsGen; { hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - hashedPacketsGen.setSignatureCreationTime(false, todayDate); - hashedPacketsGen.setKeyFlags(false, flags); - } - - if (expiry > 0) { - long creationTime = pKey.getCreationTime().getTime() / 1000; - hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime); + hashedPacketsGen.setSignatureCreationTime(true, creationTime); + hashedPacketsGen.setKeyFlags(true, flags); + if (expiry > 0) { + hashedPacketsGen.setKeyExpirationTime(true, + expiry - pKey.getCreationTime().getTime() / 1000); + } } PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - pKey.getAlgorithm(), PGPUtil.SHA1) + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey); 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 3fe535f65..2e4eafe41 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java @@ -70,7 +70,7 @@ public class PgpSignEncrypt { private long mSignatureMasterKeyId; private int mSignatureHashAlgorithm; private String mSignaturePassphrase; - private boolean mEncryptToSigner; + private long mAdditionalEncryptId; private boolean mCleartextInput; private String mOriginalFilename; @@ -103,7 +103,7 @@ public class PgpSignEncrypt { this.mSignatureMasterKeyId = builder.mSignatureMasterKeyId; this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm; this.mSignaturePassphrase = builder.mSignaturePassphrase; - this.mEncryptToSigner = builder.mEncryptToSigner; + this.mAdditionalEncryptId = builder.mAdditionalEncryptId; this.mCleartextInput = builder.mCleartextInput; this.mNfcSignedHash = builder.mNfcSignedHash; this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp; @@ -127,7 +127,7 @@ public class PgpSignEncrypt { private long mSignatureMasterKeyId = Constants.key.none; private int mSignatureHashAlgorithm = 0; private String mSignaturePassphrase = null; - private boolean mEncryptToSigner = false; + private long mAdditionalEncryptId = Constants.key.none; private boolean mCleartextInput = false; private String mOriginalFilename = ""; private byte[] mNfcSignedHash = null; @@ -175,7 +175,7 @@ public class PgpSignEncrypt { } public Builder setSignatureMasterKeyId(long signatureMasterKeyId) { - this.mSignatureMasterKeyId = signatureMasterKeyId; + mSignatureMasterKeyId = signatureMasterKeyId; return this; } @@ -192,11 +192,11 @@ public class PgpSignEncrypt { /** * Also encrypt with the signing keyring * - * @param encryptToSigner + * @param additionalEncryptId * @return */ - public Builder setEncryptToSigner(boolean encryptToSigner) { - mEncryptToSigner = encryptToSigner; + public Builder setAdditionalEncryptId(long additionalEncryptId) { + mAdditionalEncryptId = additionalEncryptId; return this; } @@ -288,10 +288,10 @@ public class PgpSignEncrypt { + "\nenableCompression:" + enableCompression + "\nenableAsciiArmorOutput:" + mEnableAsciiArmorOutput); - // add signature key id to encryption ids (self-encrypt) - if (enableEncryption && enableSignature && mEncryptToSigner) { + // add additional key id to encryption ids (mostly to do self-encryption) + if (enableEncryption && mAdditionalEncryptId != Constants.key.none) { mEncryptionMasterKeyIds = Arrays.copyOf(mEncryptionMasterKeyIds, mEncryptionMasterKeyIds.length + 1); - mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mSignatureMasterKeyId; + mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mAdditionalEncryptId; } ArmoredOutputStream armorOut = null; 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 73a51942d..f00383e0f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -44,6 +45,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.Iterator; @@ -55,7 +58,8 @@ import java.util.TreeSet; * This class and its relatives UncachedPublicKey and UncachedSecretKey are * used to move around pgp key rings in non crypto related (UI, mostly) code. * It should be used for simple inspection only until it saved in the database, - * all actual crypto operations should work with WrappedKeyRings exclusively. + * all actual crypto operations should work with CanonicalizedKeyRings + * exclusively. * * This class is also special in that it can hold either the PGPPublicKeyRing * or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are @@ -118,6 +122,10 @@ public class UncachedKeyRing { return mRing.getPublicKey().getFingerprint(); } + public int getVersion() { + return mRing.getPublicKey().getVersion(); + } + public static UncachedKeyRing decodeFromData(byte[] data) throws PgpGeneralException, IOException { @@ -211,8 +219,7 @@ public class UncachedKeyRing { aos.close(); } - /** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be - * applied to public keyrings only. + /** "Canonicalizes" a public key, removing inconsistencies in the process. * * More specifically: * - Remove all non-verifying self-certificates @@ -229,9 +236,9 @@ public class UncachedKeyRing { * - If the key is a secret key, remove all certificates by foreign keys * - If no valid user id remains, log an error and return null * - * This operation writes an OperationLog which can be used as part of a OperationResultParcel. + * This operation writes an OperationLog which can be used as part of an OperationResultParcel. * - * @return A canonicalized key, or null on fatal error + * @return A canonicalized key, or null on fatal error (log will include a message in this case) * */ @SuppressWarnings("ConstantConditions") @@ -241,6 +248,12 @@ public class UncachedKeyRing { indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId())); indent += 1; + // do not accept v3 keys + if (getVersion() <= 3) { + log.add(LogLevel.ERROR, LogType.MSG_KC_V3_KEY, indent); + return null; + } + final Date now = new Date(); int redundantCerts = 0, badCerts = 0; @@ -259,13 +272,12 @@ public class UncachedKeyRing { for (PGPSignature zert : new IterableIterator(masterKey.getKeySignatures())) { int type = zert.getSignatureType(); - // Disregard certifications on user ids, we will deal with those later + // These should most definitely not be here... if (type == PGPSignature.NO_CERTIFICATION || type == PGPSignature.DEFAULT_CERTIFICATION || 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; @@ -328,7 +340,17 @@ public class UncachedKeyRing { } } + ArrayList processedUserIds = new ArrayList(); for (String userId : new IterableIterator(masterKey.getUserIDs())) { + // check for duplicate user ids + if (processedUserIds.contains(userId)) { + log.add(LogLevel.WARN, LogType.MSG_KC_UID_DUP, + indent, userId); + // strip out the first found user id with this name + modified = PGPPublicKey.removeCertification(modified, userId); + } + processedUserIds.add(userId); + PGPSignature selfCert = null; revocation = null; @@ -405,13 +427,13 @@ public class UncachedKeyRing { if (selfCert == null) { selfCert = zert; } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { - log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP, indent, userId); modified = PGPPublicKey.removeCertification(modified, userId, selfCert); redundantCerts += 1; selfCert = zert; } else { - log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP, + log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP, indent, userId); modified = PGPPublicKey.removeCertification(modified, userId, zert); redundantCerts += 1; @@ -474,6 +496,10 @@ public class UncachedKeyRing { // Replace modified key in the keyring ring = replacePublicKey(ring, modified); + if (ring == null) { + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent); + return null; + } indent -= 1; } @@ -580,8 +606,8 @@ public class UncachedKeyRing { } - // if we already have a cert, and this one is not newer: skip it - if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) { + // if we already have a cert, and this one is older: skip it + if (selfCert != null && cert.getCreationTime().before(selfCert.getCreationTime())) { log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_DUP, indent); redundantCerts += 1; continue; @@ -641,6 +667,10 @@ public class UncachedKeyRing { } // replace pubkey in keyring ring = replacePublicKey(ring, modified); + if (ring == null) { + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent); + return null; + } indent -= 1; } @@ -681,8 +711,9 @@ public class UncachedKeyRing { long masterKeyId = other.getMasterKeyId(); - if (getMasterKeyId() != masterKeyId) { - log.add(LogLevel.ERROR, LogType.MSG_MG_HETEROGENEOUS, indent); + if (getMasterKeyId() != masterKeyId + || !Arrays.equals(getFingerprint(), other.getFingerprint())) { + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_HETEROGENEOUS, indent); return null; } @@ -729,6 +760,10 @@ public class UncachedKeyRing { } else { // otherwise, just insert the public key result = replacePublicKey(result, key); + if (result == null) { + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent); + return null; + } } continue; } @@ -757,6 +792,10 @@ public class UncachedKeyRing { if (!key.isMasterKey()) { if (modified != resultKey) { result = replacePublicKey(result, modified); + if (result == null) { + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent); + return null; + } } continue; } @@ -781,6 +820,10 @@ public class UncachedKeyRing { // If anything changed, save the updated (sub)key if (modified != resultKey) { result = replacePublicKey(result, modified); + if (result == null) { + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent); + return null; + } } } @@ -795,7 +838,7 @@ public class UncachedKeyRing { return new UncachedKeyRing(result); } catch (IOException e) { - log.add(LogLevel.ERROR, LogType.MSG_MG_FATAL_ENCODE, indent); + log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_ENCODE, indent); return null; } @@ -826,16 +869,19 @@ public class UncachedKeyRing { */ private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) { if (ring instanceof PGPPublicKeyRing) { - return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key); + PGPPublicKeyRing pubRing = (PGPPublicKeyRing) ring; + return PGPPublicKeyRing.insertPublicKey(pubRing, key); + } else { + PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; + PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); + // TODO generate secret key with S2K dummy, if none exists! + if (sKey == null) { + Log.e(Constants.TAG, "dummy secret key generation not yet implemented"); + return null; + } + sKey = PGPSecretKey.replacePublicKey(sKey, key); + return PGPSecretKeyRing.insertSecretKey(secRing, sKey); } - PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring; - PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID()); - // TODO generate secret key with S2K dummy, if none exists! for now, just die. - if (sKey == null) { - throw new RuntimeException("dummy secret key generation not yet implemented"); - } - sKey = PGPSecretKey.replacePublicKey(sKey, key); - return PGPSecretKeyRing.insertSecretKey(secRing, sKey); } /** This method removes a subkey in a keyring. 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 341ca6d04..c7a8bb1d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -17,6 +18,10 @@ package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.asn1.ASN1ObjectIdentifier; +import org.spongycastle.asn1.nist.NISTNamedCurves; +import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; +import org.spongycastle.bcpg.ECPublicBCPGKey; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; @@ -93,10 +98,23 @@ public class UncachedPublicKey { return mPublicKey.getAlgorithm(); } - public int getBitStrength() { + public Integer getBitStrength() { + if (isEC()) { + return null; + } return mPublicKey.getBitStrength(); } + public String getCurveOid() { + if ( ! isEC()) { + return null; + } + if ( ! (mPublicKey.getPublicKeyPacket().getKey() instanceof ECPublicBCPGKey)) { + return null; + } + return ((ECPublicBCPGKey) mPublicKey.getPublicKeyPacket().getKey()).getCurveOID().getId(); + } + /** 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) @@ -185,6 +203,10 @@ public class UncachedPublicKey { return getAlgorithm() == PGPPublicKey.DSA; } + public boolean isEC() { + return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA; + } + @SuppressWarnings("unchecked") // TODO make this safe public int getKeyUsage() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java index 8dc28c2b3..4dfd93289 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 ebd110dc5..f24259ba7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -95,9 +96,6 @@ public class WrappedSignature { } 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; } @@ -149,27 +147,17 @@ public class WrappedSignature { } } - public void update(byte[] data, int offset, int length) throws PgpGeneralException { - try { - mSig.update(data, offset, length); - } catch(SignatureException e) { - throw new PgpGeneralException(e); - } + public void update(byte[] data, int offset, int length) { + mSig.update(data, offset, length); } - public void update(byte data) throws PgpGeneralException { - try { - mSig.update(data); - } catch(SignatureException e) { - throw new PgpGeneralException(e); - } + public void update(byte data) { + mSig.update(data); } public boolean verify() throws PgpGeneralException { try { return mSig.verify(); - } catch(SignatureException e) { - throw new PgpGeneralException(e); } catch(PGPException e) { throw new PgpGeneralException(e); } @@ -178,8 +166,6 @@ public class WrappedSignature { boolean verifySignature(PGPPublicKey key) throws PgpGeneralException { try { return mSig.verifyCertification(key); - } catch (SignatureException e) { - throw new PgpGeneralException("Sign!", e); } catch (PGPException e) { throw new PgpGeneralException("Error!", e); } @@ -188,8 +174,6 @@ public class WrappedSignature { boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException { try { return mSig.verifyCertification(masterKey, subKey); - } catch (SignatureException e) { - throw new PgpGeneralException("Sign!", e); } catch (PGPException e) { throw new PgpGeneralException("Error!", e); } @@ -198,8 +182,6 @@ public class WrappedSignature { boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException { try { return mSig.verifyCertification(uid, key); - } catch (SignatureException e) { - throw new PgpGeneralException("Error!", e); } catch (PGPException e) { throw new PgpGeneralException("Error!", e); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java index 3700b4c34..aa0d86bb3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann - * Copyright (C) 2010-2014 Thialfihar + * Copyright (C) 2014 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 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 e076fd9cc..21f0dddf6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index dd59f8603..4f6e878b4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -38,6 +39,7 @@ public class KeychainContract { String FINGERPRINT = "fingerprint"; String KEY_SIZE = "key_size"; + String KEY_CURVE_OID = "key_curve_oid"; String CAN_SIGN = "can_sign"; String CAN_ENCRYPT = "can_encrypt"; String CAN_CERTIFY = "can_certify"; @@ -111,6 +113,7 @@ public class KeychainContract { public static final String HAS_ANY_SECRET = "has_any_secret"; public static final String HAS_ENCRYPT = "has_encrypt"; public static final String HAS_SIGN = "has_sign"; + public static final String HAS_CERTIFY = "has_certify"; public static final String PUBKEY_DATA = "pubkey_data"; public static final String PRIVKEY_DATA = "privkey_data"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 3a859f505..0bb43d47f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -51,7 +52,7 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 2; + private static final int DATABASE_VERSION = 3; static Boolean apgHack = false; public interface Tables { @@ -85,6 +86,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + KeysColumns.KEY_ID + " INTEGER, " + KeysColumns.KEY_SIZE + " INTEGER, " + + KeysColumns.KEY_CURVE_OID + " TEXT, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.FINGERPRINT + " BLOB, " @@ -201,13 +203,20 @@ public class KeychainDatabase extends SQLiteOpenHelper { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion == 1) { - // add has_secret for all who are upgrading from a beta version - try { - db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN"); - } catch (Exception e) { - // never mind, the column probably already existed - } + // add has_secret for all who are upgrading from a beta version + switch (oldVersion) { + case 1: + try { + db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN"); + } catch(Exception e){ + // never mind, the column probably already existed + } + case 2: + try { + db.execSQL("ALTER TABLE keys ADD COLUMN " + KeysColumns.KEY_CURVE_OID + " TEXT"); + } catch(Exception e){ + // never mind, the column probably already existed + } } } @@ -227,7 +236,8 @@ public class KeychainDatabase extends SQLiteOpenHelper { if (db.equals("apg.db")) { hasApgDb = true; } else if (db.equals("apg_old.db")) { - Log.d(Constants.TAG, "Found apg_old.db"); + Log.d(Constants.TAG, "Found apg_old.db, delete it!"); + context.getDatabasePath("apg_old.db").delete(); } } } @@ -310,9 +320,8 @@ public class KeychainDatabase extends SQLiteOpenHelper { } } - // Move to a different file (but don't delete, just to be safe) - Log.d(Constants.TAG, "All done - moving apg.db to apg_old.db"); - context.getDatabasePath("apg.db").renameTo(context.getDatabasePath("apg_old.db")); + // delete old database + context.getDatabasePath("apg.db").delete(); } private static void copy(File in, File out) throws IOException { @@ -349,4 +358,9 @@ public class KeychainDatabase extends SQLiteOpenHelper { copy(in, out); } + // DANGEROUS, use in test code ONLY! + public void clearDatabase() { + getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index c914cb5b7..f6df4a3eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann * Copyright (C) 2010-2014 Thialfihar + * Copyright (C) 2014 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 @@ -245,6 +246,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID); projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID); projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE); + projectionMap.put(KeyRings.KEY_CURVE_OID, Tables.KEYS + "." + Keys.KEY_CURVE_OID); projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED); projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY); projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT); @@ -271,6 +273,8 @@ public class KeychainProvider extends ContentProvider { "kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT); projectionMap.put(KeyRings.HAS_SIGN, "kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN); + projectionMap.put(KeyRings.HAS_CERTIFY, + "kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_CERTIFY); projectionMap.put(KeyRings.IS_EXPIRED, "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY + " < " + new Date().getTime() / 1000 + ") AS " + KeyRings.IS_EXPIRED); @@ -324,6 +328,15 @@ public class KeychainProvider extends ContentProvider { + " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY + " >= " + new Date().getTime() / 1000 + " )" + ")" : "") + + (plist.contains(KeyRings.HAS_CERTIFY) ? + " LEFT JOIN " + Tables.KEYS + " AS kC ON (" + +"kC." + Keys.MASTER_KEY_ID + + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + + " AND kC." + Keys.IS_REVOKED + " = 0" + + " AND kC." + Keys.CAN_CERTIFY + " = 1" + + " AND ( kC." + Keys.EXPIRY + " IS NULL OR kC." + Keys.EXPIRY + + " >= " + new Date().getTime() / 1000 + " )" + + ")" : "") ); qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0"); // in case there are multiple verifying certificates @@ -400,6 +413,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK); projectionMap.put(Keys.KEY_ID, Keys.KEY_ID); projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE); + projectionMap.put(Keys.KEY_CURVE_OID, Keys.KEY_CURVE_OID); projectionMap.put(Keys.IS_REVOKED, Keys.IS_REVOKED); projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY); projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT); @@ -674,6 +688,11 @@ public class KeychainProvider extends ContentProvider { final int match = mUriMatcher.match(uri); switch (match) { + // dangerous + case KEY_RINGS_UNIFIED: { + count = db.delete(Tables.KEY_RINGS_PUBLIC, null, null); + break; + } case KEY_RING_PUBLIC: { @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1); 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 a13bb9c98..bb095c340 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -28,12 +29,16 @@ import android.os.RemoteException; import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; 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.PgpHelper; +import org.sufficientlysecure.keychain.pgp.PgpImportExport; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -51,9 +56,12 @@ import org.sufficientlysecure.keychain.remote.AppSettings; 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.ConsolidateResult; import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; +import org.sufficientlysecure.keychain.util.FileImportCache; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ProgressFixedScaler; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -63,6 +71,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -86,6 +95,10 @@ public class ProviderHelper { this(context, new OperationLog(), 0); } + public ProviderHelper(Context context, OperationLog log) { + this(context, log, 0); + } + public ProviderHelper(Context context, OperationLog log, int indent) { mContext = context; mContentResolver = context.getContentResolver(); @@ -93,14 +106,6 @@ public class ProviderHelper { mIndent = indent; } - public void resetLog() { - if(mLog != null) { - // Start a new log (leaving the old one intact) - mLog = new OperationLog(); - mIndent = 0; - } - } - public OperationLog getLog() { return mLog; } @@ -322,6 +327,7 @@ public class ProviderHelper { values.put(Keys.KEY_ID, key.getKeyId()); values.put(Keys.KEY_SIZE, key.getBitStrength()); + values.put(Keys.KEY_CURVE_OID, key.getCurveOid()); values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.FINGERPRINT, key.getFingerprint()); @@ -644,7 +650,7 @@ public class ProviderHelper { if (publicRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } CanonicalizedPublicKeyRing canPublicRing; @@ -658,20 +664,20 @@ public class ProviderHelper { // If this is null, there is an error in the log so we can just return if (publicRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } // Canonicalize this keyring, to assert a number of assumptions made about it. canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent); if (canPublicRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } // Early breakout if nothing changed if (Arrays.hashCode(publicRing.getEncoded()) == Arrays.hashCode(oldPublicRing.getEncoded())) { log(LogLevel.OK, LogType.MSG_IP_SUCCESS_IDENTICAL); - return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog); + return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog, null); } } catch (NotFoundException e) { // Not an issue, just means we are dealing with a new keyring. @@ -679,7 +685,7 @@ public class ProviderHelper { // Canonicalize this keyring, to assert a number of assumptions made about it. canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent); if (canPublicRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } } @@ -692,12 +698,12 @@ public class ProviderHelper { // 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); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, 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); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } } catch (NotFoundException e) { @@ -716,11 +722,11 @@ public class ProviderHelper { } } - return new SaveKeyringResult(result, mLog); + return new SaveKeyringResult(result, mLog, canSecretRing); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } finally { mIndent -= 1; } @@ -736,7 +742,7 @@ public class ProviderHelper { if ( ! secretRing.isSecret()) { log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } CanonicalizedSecretKeyRing canSecretRing; @@ -750,14 +756,14 @@ public class ProviderHelper { // If this is null, there is an error in the log so we can just return if (secretRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } // Canonicalize this keyring, to assert a number of assumptions made about it. // 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); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } // Early breakout if nothing changed @@ -765,7 +771,7 @@ public class ProviderHelper { == Arrays.hashCode(oldSecretRing.getEncoded())) { log(LogLevel.OK, LogType.MSG_IS_SUCCESS_IDENTICAL, PgpKeyHelper.convertKeyIdToHex(masterKeyId) ); - return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog); + return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog, null); } } catch (NotFoundException e) { // Not an issue, just means we are dealing with a new keyring @@ -774,7 +780,7 @@ public class ProviderHelper { // 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); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } } @@ -787,7 +793,7 @@ public class ProviderHelper { // 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); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } } catch (NotFoundException e) { @@ -797,30 +803,289 @@ public class ProviderHelper { CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent); if (canPublicRing == null) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } int result; result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true); if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) { - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100); result = saveCanonicalizedSecretKeyRing(canSecretRing); - return new SaveKeyringResult(result, mLog); + return new SaveKeyringResult(result, mLog, canSecretRing); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); - return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); } finally { mIndent -= 1; } } + public ConsolidateResult consolidateDatabaseStep1(Progressable progress) { + + // 1a. fetch all secret keyrings into a cache file + log(LogLevel.START, LogType.MSG_CON); + mIndent += 1; + + progress.setProgress(R.string.progress_con_saving, 0, 100); + + try { + + log(LogLevel.DEBUG, LogType.MSG_CON_SAVE_SECRET); + mIndent += 1; + + final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{ + KeyRings.PRIVKEY_DATA, KeyRings.FINGERPRINT, KeyRings.HAS_ANY_SECRET + }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); + + if (cursor == null || !cursor.moveToFirst()) { + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_DB); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } + + Preferences.getPreferences(mContext).setCachedConsolidateNumSecrets(cursor.getCount()); + + FileImportCache cache = + new FileImportCache(mContext, "consolidate_secret.pcl"); + cache.writeCache(new Iterator() { + ParcelableKeyRing ring; + + @Override + public boolean hasNext() { + if (ring != null) { + return true; + } + if (cursor.isAfterLast()) { + return false; + } + ring = new ParcelableKeyRing(cursor.getBlob(0), + PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1))); + cursor.moveToNext(); + return true; + } + + @Override + public ParcelableKeyRing next() { + try { + return ring; + } finally { + ring = null; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }); + + } catch (IOException e) { + Log.e(Constants.TAG, "error saving secret", e); + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_IO_SECRET); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; + } + + progress.setProgress(R.string.progress_con_saving, 3, 100); + + // 1b. fetch all public keyrings into a cache file + try { + + log(LogLevel.DEBUG, LogType.MSG_CON_SAVE_PUBLIC); + mIndent += 1; + + final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{ + KeyRings.PUBKEY_DATA, KeyRings.FINGERPRINT + }, null, null, null); + + if (cursor == null || !cursor.moveToFirst()) { + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_DB); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } + + Preferences.getPreferences(mContext).setCachedConsolidateNumPublics(cursor.getCount()); + + FileImportCache cache = + new FileImportCache(mContext, "consolidate_public.pcl"); + cache.writeCache(new Iterator() { + ParcelableKeyRing ring; + + @Override + public boolean hasNext() { + if (ring != null) { + return true; + } + if (cursor.isAfterLast()) { + return false; + } + ring = new ParcelableKeyRing(cursor.getBlob(0), + PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1))); + cursor.moveToNext(); + return true; + } + + @Override + public ParcelableKeyRing next() { + try { + return ring; + } finally { + ring = null; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }); + + } catch (IOException e) { + Log.e(Constants.TAG, "error saving public", e); + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_IO_PUBLIC); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; + } + + log(LogLevel.INFO, LogType.MSG_CON_CRITICAL_IN); + Preferences.getPreferences(mContext).setCachedConsolidate(true); + + return consolidateDatabaseStep2(progress, false); + } + + public ConsolidateResult consolidateDatabaseStep2(Progressable progress) { + return consolidateDatabaseStep2(progress, true); + } + + private static boolean mConsolidateCritical = false; + + private ConsolidateResult consolidateDatabaseStep2(Progressable progress, boolean recovery) { + + synchronized (ProviderHelper.class) { + if (mConsolidateCritical) { + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_CONCURRENT); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } + mConsolidateCritical = true; + } + + try { + Preferences prefs = Preferences.getPreferences(mContext); + + // Set flag that we have a cached consolidation here + int numSecrets = prefs.getCachedConsolidateNumSecrets(); + int numPublics = prefs.getCachedConsolidateNumPublics(); + + if (recovery) { + if (numSecrets >= 0 && numPublics >= 0) { + log(LogLevel.START, LogType.MSG_CON_RECOVER, numSecrets, numPublics); + } else { + log(LogLevel.START, LogType.MSG_CON_RECOVER_UNKNOWN); + } + mIndent += 1; + } + + if (!prefs.getCachedConsolidate()) { + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_BAD_STATE); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } + + // 2. wipe database (IT'S DANGEROUS) + log(LogLevel.DEBUG, LogType.MSG_CON_DB_CLEAR); + mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null); + + FileImportCache cacheSecret = + new FileImportCache(mContext, "consolidate_secret.pcl"); + FileImportCache cachePublic = + new FileImportCache(mContext, "consolidate_public.pcl"); + + // 3. Re-Import secret keyrings from cache + if (numSecrets > 0) try { + log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_SECRET, numSecrets); + mIndent += 1; + + new PgpImportExport(mContext, this, + new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport)) + .importKeyRings(cacheSecret.readCache(false), numSecrets); + } catch (IOException e) { + Log.e(Constants.TAG, "error importing secret", e); + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_SECRET); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; + } + else { + log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_SECRET_SKIP); + } + + // 4. Re-Import public keyrings from cache + if (numPublics > 0) try { + log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_PUBLIC, numPublics); + mIndent += 1; + + new PgpImportExport(mContext, this, + new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport)) + .importKeyRings(cachePublic.readCache(false), numPublics); + } catch (IOException e) { + Log.e(Constants.TAG, "error importing public", e); + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_PUBLIC); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; + } + else { + log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_PUBLIC_SKIP); + } + + log(LogLevel.INFO, LogType.MSG_CON_CRITICAL_OUT); + Preferences.getPreferences(mContext).setCachedConsolidate(false); + + // 5. Delete caches + try { + log(LogLevel.DEBUG, LogType.MSG_CON_DELETE_SECRET); + mIndent += 1; + cacheSecret.delete(); + } catch (IOException e) { + // doesn't /really/ matter + Log.e(Constants.TAG, "IOException during delete of secret cache", e); + log(LogLevel.WARN, LogType.MSG_CON_WARN_DELETE_SECRET); + } finally { + mIndent -= 1; + } + + try { + log(LogLevel.DEBUG, LogType.MSG_CON_DELETE_PUBLIC); + mIndent += 1; + cachePublic.delete(); + } catch (IOException e) { + // doesn't /really/ matter + Log.e(Constants.TAG, "IOException during deletion of public cache", e); + log(LogLevel.WARN, LogType.MSG_CON_WARN_DELETE_PUBLIC); + } finally { + mIndent -= 1; + } + + progress.setProgress(100, 100); + log(LogLevel.OK, LogType.MSG_CON_SUCCESS); + mIndent -= 1; + + return new ConsolidateResult(ConsolidateResult.RESULT_OK, mLog); + + } finally { + mConsolidateCritical = false; + } + + } + /** * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ @@ -863,9 +1128,7 @@ public class ProviderHelper { ByteArrayOutputStream bos = new ByteArrayOutputStream(); String version = PgpHelper.getVersionForHeader(mContext); - if (version != null) { - keyRing.encodeArmored(bos, version); - } + keyRing.encodeArmored(bos, version); String armoredKey = bos.toString("UTF-8"); Log.d(Constants.TAG, "armoredKey:" + armoredKey); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java index 87c0cc0a6..a65d222da 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 44d37b926..20dfac36d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -23,6 +23,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.text.TextUtils; import org.openintents.openpgp.IOpenPgpService; import org.openintents.openpgp.OpenPgpMetadata; @@ -38,6 +39,7 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; @@ -56,11 +58,16 @@ import java.util.Set; public class OpenPgpService extends RemoteService { - static final String[] KEYRING_PROJECTION = - new String[]{ - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - }; + static final String[] EMAIL_SEARCH_PROJECTION = new String[]{ + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + KeyRings.IS_EXPIRED, + KeyRings.IS_REVOKED, + }; + + // do not pre-select revoked or expired keys + static final String EMAIL_SEARCH_WHERE = KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + + KeychainContract.KeyRings.IS_EXPIRED + " = 0"; /** * Search database for key ids based on emails. @@ -69,52 +76,61 @@ public class OpenPgpService extends RemoteService { * @return */ private Intent getKeyIdsFromEmails(Intent data, String[] encryptionUserIds) { - // find key ids to given emails in database - ArrayList keyIds = new ArrayList(); - + boolean noUserIdsCheck = (encryptionUserIds == null || encryptionUserIds.length == 0); boolean missingUserIdsCheck = false; boolean duplicateUserIdsCheck = false; + + ArrayList keyIds = new ArrayList(); ArrayList missingUserIds = new ArrayList(); ArrayList duplicateUserIds = new ArrayList(); + if (!noUserIdsCheck) { + for (String email : encryptionUserIds) { + // try to find the key for this specific email + Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email); + Cursor cursor = getContentResolver().query(uri, EMAIL_SEARCH_PROJECTION, EMAIL_SEARCH_WHERE, null, null); + try { + // result should be one entry containing the key id + if (cursor != null && cursor.moveToFirst()) { + long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); + keyIds.add(id); + } else { + missingUserIdsCheck = true; + missingUserIds.add(email); + Log.d(Constants.TAG, "user id missing"); + } + // another entry for this email -> too keys with the same email inside user id + if (cursor != null && cursor.moveToNext()) { + duplicateUserIdsCheck = true; + duplicateUserIds.add(email); - for (String email : encryptionUserIds) { - Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email); - 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)); - keyIds.add(id); - } else { - missingUserIdsCheck = true; - missingUserIds.add(email); - Log.d(Constants.TAG, "user id missing"); - } - if (cursor != null && cursor.moveToNext()) { - duplicateUserIdsCheck = true; - duplicateUserIds.add(email); - Log.d(Constants.TAG, "more than one user id with the same email"); - } - } finally { - if (cursor != null) { - cursor.close(); + // also pre-select + long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); + keyIds.add(id); + Log.d(Constants.TAG, "more than one user id with the same email"); + } + } finally { + if (cursor != null) { + cursor.close(); + } } } } - // convert to long[] + // convert ArrayList to long[] long[] keyIdsArray = new long[keyIds.size()]; for (int i = 0; i < keyIdsArray.length; i++) { keyIdsArray[i] = keyIds.get(i); } - // allow the user to verify pub key selection - if (missingUserIdsCheck || duplicateUserIdsCheck) { - // build PendingIntent + if (noUserIdsCheck || missingUserIdsCheck || duplicateUserIdsCheck) { + // allow the user to verify pub key selection + Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS); intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray); + intent.putExtra(RemoteServiceActivity.EXTRA_NO_USER_IDS_CHECK, noUserIdsCheck); intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds); - intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, duplicateUserIds); + intent.putExtra(RemoteServiceActivity.EXTRA_DUPLICATE_USER_IDS, duplicateUserIds); intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, @@ -126,16 +142,18 @@ public class OpenPgpService extends RemoteService { result.putExtra(OpenPgpApi.RESULT_INTENT, pi); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); return result; - } + } else { + // everything was easy, we have exactly one key for every email - if (keyIdsArray.length == 0) { - return null; - } + if (keyIdsArray.length == 0) { + Log.e(Constants.TAG, "keyIdsArray.length == 0, should never happen!"); + } - Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_KEY_IDS, keyIdsArray); - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); - return result; + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_KEY_IDS, keyIdsArray); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } } private Intent getNfcIntent(Intent data, byte[] hashToSign, int hashAlgo) { @@ -191,7 +209,7 @@ public class OpenPgpService extends RemoteService { } catch (PassphraseCacheService.KeyNotFoundException e) { // secret key that is set for this account is deleted? // show account config again! - return getCreateAccountIntent(data, data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME)); + return getCreateAccountIntent(data, getAccountName(data)); } } if (passphrase == null) { @@ -270,10 +288,9 @@ public class OpenPgpService extends RemoteService { originalFilename = ""; } - long[] keyIds; - if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) { - keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); - } else if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS)) { + // first try to get key ids from non-ambiguous key id extra + long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); + if (keyIds == null) { // get key ids based on given user ids String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS); // give params through to activity... @@ -285,20 +302,8 @@ public class OpenPgpService extends RemoteService { // if not success -> result contains a PendingIntent for user interaction return result; } - } else { - Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_ERROR, - new OpenPgpError(OpenPgpError.GENERIC_ERROR, - "Missing parameter user_ids or key_ids!") - ); - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); - return result; } - // add own key for encryption - keyIds = Arrays.copyOf(keyIds, keyIds.length + 1); - keyIds[keyIds.length - 1] = accSettings.getKeyId(); - // build InputData and write into OutputStream // Get Input- and OutputStream from ParcelFileDescriptor InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); @@ -315,7 +320,8 @@ public class OpenPgpService extends RemoteService { .setCompressionId(accSettings.getCompression()) .setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm()) .setEncryptionMasterKeyIds(keyIds) - .setOriginalFilename(originalFilename); + .setOriginalFilename(originalFilename) + .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption if (sign) { String passphrase; @@ -334,9 +340,6 @@ public class OpenPgpService extends RemoteService { builder.setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) .setSignatureMasterKeyId(accSettings.getKeyId()) .setSignaturePassphrase(passphrase); - } else { - // encrypt only - builder.setSignatureMasterKeyId(Constants.key.none); } try { @@ -448,7 +451,7 @@ public class OpenPgpService extends RemoteService { // If signature is unknown we return an _additional_ PendingIntent // to retrieve the missing key Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE); intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId()); intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data); @@ -514,7 +517,7 @@ public class OpenPgpService extends RemoteService { // If keys are not in db we return an additional PendingIntent // to retrieve the missing key Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE); intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, masterKeyId); intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data); @@ -596,6 +599,16 @@ public class OpenPgpService extends RemoteService { return null; } + private String getAccountName(Intent data) { + String accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME); + // if no account name is given use name "default" + if (TextUtils.isEmpty(accName)) { + accName = "default"; + } + Log.d(Constants.TAG, "accName: " + accName); + return accName; + } + // TODO: multi-threading private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { @@ -606,12 +619,7 @@ public class OpenPgpService extends RemoteService { return errorResult; } - String accName; - if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) { - accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME); - } else { - accName = "default"; - } + String accName = getAccountName(data); final AccountSettings accSettings = getAccSettings(accName); if (accSettings == null) { return getCreateAccountIntent(data, accName); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index f70324e2c..e71b52ccd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.net.Uri; import android.os.Binder; +import android.text.TextUtils; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.util.OpenPgpApi; @@ -160,7 +161,7 @@ public abstract class RemoteService extends Service { */ protected AccountSettings getAccSettings(String accountName) { String currentPkg = getCurrentCallingPackage(); - Log.d(Constants.TAG, "accountName: " + accountName); + Log.d(Constants.TAG, "getAccSettings accountName: "+ accountName); Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); @@ -171,7 +172,7 @@ public abstract class RemoteService extends Service { protected Intent getCreateAccountIntent(Intent data, String accountName) { String packageName = getCurrentCallingPackage(); - Log.d(Constants.TAG, "accountName: " + accountName); + Log.d(Constants.TAG, "getCreateAccountIntent accountName: " + accountName); Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java index 666252353..67ad0822d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java @@ -30,6 +30,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.remote.AccountSettings; +import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.util.Log; public class AccountSettingsActivity extends ActionBarActivity { @@ -106,4 +107,15 @@ public class AccountSettingsActivity extends ActionBarActivity { finish(); } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify + 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); + } + } + } 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 8468f5eca..2cab23e51 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 @@ -36,6 +36,8 @@ 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.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResults; import org.sufficientlysecure.keychain.ui.CreateKeyActivity; import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment; import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter; @@ -177,24 +179,19 @@ public class AccountSettingsFragment extends Fragment implements switch (requestCode) { case REQUEST_CODE_CREATE_KEY: { if (resultCode == Activity.RESULT_OK) { - // select newly created key - try { - long masterKeyId = new ProviderHelper(getActivity()) - .getCachedPublicKeyRing(data.getData()) - .extractOrGetMasterKeyId(); - mSelectKeyFragment.selectKey(masterKeyId); - } catch (PgpGeneralException e) { - Log.e(Constants.TAG, "key not found!", e); + if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) { + OperationResults.SaveKeyringResult result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT); + mSelectKeyFragment.selectKey(result.mRingMasterKeyId); + } else { + Log.e(Constants.TAG, "missing result!"); } } break; } - - default: - super.onActivityResult(requestCode, resultCode, data); - - break; } + + // execute activity's onActivityResult to show log notify + super.onActivityResult(requestCode, resultCode, data); } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index 48c76d561..4b27e115b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -18,11 +18,20 @@ package org.sufficientlysecure.keychain.remote.ui; import android.content.Intent; +import android.graphics.Color; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.ActionBarActivity; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.SpannedString; +import android.text.TextUtils; +import android.text.style.BulletSpan; +import android.text.style.StyleSpan; import android.view.View; import android.widget.TextView; @@ -39,7 +48,6 @@ import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; -import java.security.Provider; import java.util.ArrayList; public class RemoteServiceActivity extends ActionBarActivity { @@ -68,7 +76,8 @@ public class RemoteServiceActivity extends ActionBarActivity { // select pub keys action public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids"; public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids"; - public static final String EXTRA_DUBLICATE_USER_IDS = "dublicate_user_ids"; + public static final String EXTRA_DUPLICATE_USER_IDS = "dublicate_user_ids"; + public static final String EXTRA_NO_USER_IDS_CHECK = "no_user_ids"; // error message public static final String EXTRA_ERROR_MESSAGE = "error_message"; @@ -229,32 +238,41 @@ public class RemoteServiceActivity extends ActionBarActivity { } else if (ACTION_SELECT_PUB_KEYS.equals(action)) { long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); + boolean noUserIdsCheck = intent.getBooleanExtra(EXTRA_NO_USER_IDS_CHECK, true); ArrayList missingUserIds = intent .getStringArrayListExtra(EXTRA_MISSING_USER_IDS); ArrayList dublicateUserIds = intent - .getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS); + .getStringArrayListExtra(EXTRA_DUPLICATE_USER_IDS); + + SpannableStringBuilder ssb = new SpannableStringBuilder(); + final SpannableString textIntro = new SpannableString( + noUserIdsCheck ? getString(R.string.api_select_pub_keys_text_no_user_ids) + : getString(R.string.api_select_pub_keys_text) + ); + textIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, textIntro.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append(textIntro); - // TODO: do this with spannable instead of HTML to prevent parsing failures with weird user ids - String text = "" + getString(R.string.api_select_pub_keys_text) + ""; - text += "

"; if (missingUserIds != null && missingUserIds.size() > 0) { - text += getString(R.string.api_select_pub_keys_missing_text); - text += "
"; - text += "

    "; + ssb.append("\n\n"); + ssb.append(getString(R.string.api_select_pub_keys_missing_text)); + ssb.append("\n"); for (String userId : missingUserIds) { - text += "
  • " + userId + "
  • "; + SpannableString ss = new SpannableString(userId + "\n"); + ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append(ss); } - text += "
"; - text += "
"; } if (dublicateUserIds != null && dublicateUserIds.size() > 0) { - text += getString(R.string.api_select_pub_keys_dublicates_text); - text += "
"; - text += "
    "; + ssb.append("\n\n"); + ssb.append(getString(R.string.api_select_pub_keys_dublicates_text)); + ssb.append("\n"); for (String userId : dublicateUserIds) { - text += "
  • " + userId + "
  • "; + SpannableString ss = new SpannableString(userId + "\n"); + ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append(ss); } - text += "
"; } // Inflate a "Done"/"Cancel" custom action bar view @@ -284,8 +302,8 @@ public class RemoteServiceActivity extends ActionBarActivity { setContentView(R.layout.api_remote_select_pub_keys); // set text on view - HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text); - textView.setHtmlFromString(text, true); + TextView textView = (TextView) findViewById(R.id.api_select_pub_keys_text); + textView.setText(ssb, TextView.BufferType.SPANNABLE); /* Load select pub keys fragment */ // Check that the activity is using the layout version with diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index 43ed2dad0..fbe914b78 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -21,6 +21,8 @@ import android.accounts.Account; import android.app.Service; import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.content.SyncResult; import android.os.Bundle; @@ -29,9 +31,11 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.provider.ContactsContract; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.KeychainApplication; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.helper.EmailKeyHelper; import org.sufficientlysecure.keychain.util.Log; @@ -42,7 +46,7 @@ public class ContactSyncAdapterService extends Service { private class ContactSyncAdapter extends AbstractThreadedSyncAdapter { - private final AtomicBoolean importDone = new AtomicBoolean(false); +// private final AtomicBoolean importDone = new AtomicBoolean(false); public ContactSyncAdapter() { super(ContactSyncAdapterService.this, true); @@ -51,47 +55,59 @@ public class ContactSyncAdapterService extends Service { @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, final SyncResult syncResult) { - importDone.set(false); - KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); - EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(), - new Handler.Callback() { - @Override - public boolean handleMessage(Message msg) { - Bundle data = msg.getData(); - switch (msg.arg1) { - case KeychainIntentServiceHandler.MESSAGE_OKAY: - Log.d(Constants.TAG, "Syncing... Done."); - synchronized (importDone) { - importDone.set(true); - importDone.notifyAll(); - } - return true; - case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS: - if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) && - data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) { - Log.d(Constants.TAG, "Syncing... Progress: " + - data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" + - data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)); - return false; - } - default: - Log.d(Constants.TAG, "Syncing... " + msg.toString()); - return false; - } - } - }))); - synchronized (importDone) { - try { - if (!importDone.get()) importDone.wait(); - } catch (InterruptedException e) { - Log.w(Constants.TAG, e); - return; - } - } + Log.d(Constants.TAG, "Performing a sync!"); + // TODO: Import is currently disabled for 2.8, until we implement proper origin management +// importDone.set(false); +// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); +// EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(), +// new Handler.Callback() { +// @Override +// public boolean handleMessage(Message msg) { +// Bundle data = msg.getData(); +// switch (msg.arg1) { +// case KeychainIntentServiceHandler.MESSAGE_OKAY: +// Log.d(Constants.TAG, "Syncing... Done."); +// synchronized (importDone) { +// importDone.set(true); +// importDone.notifyAll(); +// } +// return true; +// case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS: +// if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) && +// data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) { +// Log.d(Constants.TAG, "Syncing... Progress: " + +// data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" + +// data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)); +// return false; +// } +// default: +// Log.d(Constants.TAG, "Syncing... " + msg.toString()); +// return false; +// } +// } +// }))); +// synchronized (importDone) { +// try { +// if (!importDone.get()) importDone.wait(); +// } catch (InterruptedException e) { +// Log.w(Constants.TAG, e); +// return; +// } +// } ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this); } } + public static void requestSync() { + Bundle extras = new Bundle(); + // no need to wait for internet connection! + extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + ContentResolver.requestSync( + new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), + ContactsContract.AUTHORITY, + extras); + } + @Override public IBinder onBind(Intent intent) { return new ContactSyncAdapter().getSyncAdapterBinder(); 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 d6c470e11..021e6bc07 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -49,11 +50,14 @@ import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult; import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; +import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult; import org.sufficientlysecure.keychain.util.FileImportCache; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; @@ -102,6 +106,10 @@ public class KeychainIntentService extends IntentService public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING"; + public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE"; + + public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE"; + /* keys for data bundle */ // encrypt, decrypt, import export @@ -139,8 +147,13 @@ public class KeychainIntentService extends IntentService // delete file securely public static final String DELETE_FILE = "deleteFile"; + // delete keyring(s) + public static final String DELETE_KEY_LIST = "delete_list"; + public static final String DELETE_IS_SECRET = "delete_is_secret"; + // import key public static final String IMPORT_KEY_LIST = "import_key_list"; + public static final String IMPORT_KEY_FILE = "import_key_file"; // export key public static final String EXPORT_OUTPUT_STREAM = "export_output_stream"; @@ -162,6 +175,10 @@ public class KeychainIntentService extends IntentService public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id"; public static final String CERTIFY_KEY_UIDS = "sign_key_uids"; + // consolidate + public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery"; + + /* * possible data keys as result send over messenger */ @@ -176,8 +193,6 @@ public class KeychainIntentService extends IntentService // export public static final String RESULT_EXPORT = "exported"; - public static final String RESULT_IMPORT = "result"; - Messenger mMessenger; private boolean mIsCanceled; @@ -246,27 +261,31 @@ public class KeychainIntentService extends IntentService String originalFilename = getOriginalFilename(data); /* Operation */ - PgpSignEncrypt.Builder builder = - new PgpSignEncrypt.Builder( - new ProviderHelper(this), - inputData, outStream); - builder.setProgressable(this); - - builder.setEnableAsciiArmorOutput(useAsciiArmor) + PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder( + new ProviderHelper(this), + inputData, outStream + ); + builder.setProgressable(this) + .setEnableAsciiArmorOutput(useAsciiArmor) .setVersionHeader(PgpHelper.getVersionForHeader(this)) .setCompressionId(compressionId) .setSymmetricEncryptionAlgorithm( Preferences.getPreferences(this).getDefaultEncryptionAlgorithm()) .setEncryptionMasterKeyIds(encryptionKeyIds) .setSymmetricPassphrase(symmetricPassphrase) - .setSignatureMasterKeyId(signatureKeyId) - .setEncryptToSigner(true) - .setSignatureHashAlgorithm( - Preferences.getPreferences(this).getDefaultHashAlgorithm()) - .setSignaturePassphrase( - PassphraseCacheService.getCachedPassphrase(this, signatureKeyId)) .setOriginalFilename(originalFilename); + try { + builder.setSignatureMasterKeyId(signatureKeyId) + .setSignaturePassphrase( + PassphraseCacheService.getCachedPassphrase(this, signatureKeyId)) + .setSignatureHashAlgorithm( + Preferences.getPreferences(this).getDefaultHashAlgorithm()) + .setAdditionalEncryptId(signatureKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + // encrypt-only + } + // this assumes that the bytes are cleartext (valid for current implementation!) if (source == IO_BYTES) { builder.setCleartextInput(true); @@ -391,23 +410,41 @@ public class KeychainIntentService extends IntentService } /* Operation */ - ProviderHelper providerHelper = new ProviderHelper(this); PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100)); - EditKeyResult result; + EditKeyResult modifyResult; if (saveParcel.mMasterKeyId != null) { String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE); CanonicalizedSecretKeyRing secRing = - providerHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); + new ProviderHelper(this).getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId); - result = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); + modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase); } else { - result = keyOperations.createSecretKeyRing(saveParcel); + modifyResult = keyOperations.createSecretKeyRing(saveParcel); } - UncachedKeyRing ring = result.getRing(); + // If the edit operation didn't succeed, exit here + if (!modifyResult.success()) { + // always return SaveKeyringResult, so create one out of the EditKeyResult + SaveKeyringResult saveResult = new SaveKeyringResult( + SaveKeyringResult.RESULT_ERROR, + modifyResult.getLog(), + null); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + return; + } - providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); + UncachedKeyRing ring = modifyResult.getRing(); + + // Save the keyring. The ProviderHelper is initialized with the previous log + SaveKeyringResult saveResult = new ProviderHelper(this, modifyResult.getLog()) + .saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); + + // If the edit operation didn't succeed, exit here + if (!saveResult.success()) { + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); + return; + } // cache new passphrase if (saveParcel.mNewPassphrase != null) { @@ -417,8 +454,11 @@ public class KeychainIntentService extends IntentService setProgress(R.string.progress_done, 100, 100); + // make sure new data is synced into contacts + ContactSyncAdapterService.requestSync(); + /* Output */ - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult); } catch (Exception e) { sendErrorToHandler(e); } @@ -454,17 +494,21 @@ public class KeychainIntentService extends IntentService } else { // get entries from cached file FileImportCache cache = - new FileImportCache(this); + new FileImportCache(this, "key_import.pcl"); entries = cache.readCacheIntoList(); } - PgpImportExport pgpImportExport = new PgpImportExport(this, this); + ProviderHelper providerHelper = new ProviderHelper(this); + PgpImportExport pgpImportExport = new PgpImportExport(this, providerHelper, this); ImportKeyResult result = pgpImportExport.importKeyRings(entries); - Bundle resultData = new Bundle(); - resultData.putParcelable(RESULT_IMPORT, result); + if (result.mSecret > 0) { + providerHelper.consolidateDatabaseStep1(this); + } + // make sure new data is synced into contacts + ContactSyncAdapterService.requestSync(); - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); } catch (Exception e) { sendErrorToHandler(e); } @@ -549,8 +593,9 @@ public class KeychainIntentService extends IntentService CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri); PgpImportExport pgpImportExport = new PgpImportExport(this, null); - boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring); - if (!uploaded) { + try { + pgpImportExport.uploadKeyRingToServer(server, keyring); + } catch (Keyserver.AddKeyException e) { throw new PgpGeneralException("Unable to export key to selected server"); } @@ -639,7 +684,56 @@ public class KeychainIntentService extends IntentService } catch (Exception e) { sendErrorToHandler(e); } + + } else if (ACTION_DELETE.equals(action)) { + + try { + + long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST); + boolean isSecret = data.getBoolean(DELETE_IS_SECRET); + + if (masterKeyIds.length == 0) { + throw new PgpGeneralException("List of keys to delete is empty"); + } + + if (isSecret && masterKeyIds.length > 1) { + throw new PgpGeneralException("Secret keys can only be deleted individually!"); + } + + boolean success = false; + for (long masterKeyId : masterKeyIds) { + int count = getContentResolver().delete( + KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null + ); + success |= count > 0; + } + + if (isSecret && success) { + ConsolidateResult result = + new ProviderHelper(this).consolidateDatabaseStep1(this); + } + + if (success) { + // make sure new data is synced into contacts + ContactSyncAdapterService.requestSync(); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } + + } catch (Exception e) { + sendErrorToHandler(e); + } + + } else if (ACTION_CONSOLIDATE.equals(action)) { + ConsolidateResult result; + if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) { + result = new ProviderHelper(this).consolidateDatabaseStep2(this); + } else { + result = new ProviderHelper(this).consolidateDatabaseStep1(this); + } + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); } + } private void sendErrorToHandler(Exception e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index d7d98fd68..142bf65cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -155,10 +156,8 @@ public class OperationResultParcel implements Parcelable { if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) { if (getLog().containsWarnings()) { - duration = 0; color = Style.ORANGE; } else { - duration = SuperToast.Duration.LONG; color = Style.GREEN; } @@ -167,7 +166,6 @@ public class OperationResultParcel implements Parcelable { } else { - duration = 0; color = Style.RED; str = "operation failed"; @@ -180,8 +178,8 @@ public class OperationResultParcel implements Parcelable { button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD, Style.getStyle(color, SuperToast.Animations.POPUP)); toast.setText(str); - toast.setDuration(duration); - toast.setIndeterminate(duration == 0); + toast.setDuration(SuperToast.Duration.EXTRA_LONG); + toast.setIndeterminate(false); toast.setSwipeToDismiss(true); // If we have a log and it's non-empty, show a View Log button if (button) { @@ -289,6 +287,7 @@ public class OperationResultParcel implements Parcelable { MSG_IS_SUCCESS (R.string.msg_is_success), // keyring canonicalization + MSG_KC_V3_KEY (R.string.msg_kc_v3_key), MSG_KC_PUBLIC (R.string.msg_kc_public), MSG_KC_SECRET (R.string.msg_kc_secret), MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid), @@ -324,6 +323,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_BAD_TIME (R.string.msg_kc_uid_bad_time), MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type), MSG_KC_UID_BAD (R.string.msg_kc_uid_bad), + MSG_KC_UID_CERT_DUP (R.string.msg_kc_uid_cert_dup), MSG_KC_UID_DUP (R.string.msg_kc_uid_dup), MSG_KC_UID_FOREIGN (R.string.msg_kc_uid_foreign), MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), @@ -333,10 +333,11 @@ public class OperationResultParcel implements Parcelable { // keyring consolidation + MSG_MG_ERROR_SECRET_DUMMY(R.string.msg_mg_error_secret_dummy), + MSG_MG_ERROR_ENCODE(R.string.msg_mg_error_encode), + MSG_MG_ERROR_HETEROGENEOUS(R.string.msg_mg_error_heterogeneous), MSG_MG_PUBLIC (R.string.msg_mg_public), MSG_MG_SECRET (R.string.msg_mg_secret), - MSG_MG_FATAL_ENCODE (R.string.msg_mg_fatal_encode), - 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), @@ -346,10 +347,16 @@ public class OperationResultParcel implements Parcelable { 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_NULL_EXPIRY(R.string.msg_cr_error_null_expiry), MSG_CR_ERROR_KEYSIZE_512 (R.string.msg_cr_error_keysize_512), + MSG_CR_ERROR_NO_KEYSIZE (R.string.msg_cr_error_no_keysize), + MSG_CR_ERROR_NO_CURVE (R.string.msg_cr_error_no_curve), 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), + MSG_CR_ERROR_FLAGS_DSA (R.string.msg_cr_error_flags_dsa), + MSG_CR_ERROR_FLAGS_ELGAMAL (R.string.msg_cr_error_flags_elgamal), + MSG_CR_ERROR_FLAGS_ECDSA (R.string.msg_cr_error_flags_ecdsa), + MSG_CR_ERROR_FLAGS_ECDH (R.string.msg_cr_error_flags_ecdh), // secret key modify MSG_MF (R.string.msg_mr), @@ -357,18 +364,27 @@ 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_MASTER_NONE(R.string.msg_mf_error_master_none), + MSG_MF_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify), 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_NOEXIST_REVOKE (R.string.msg_mf_error_noexist_revoke), + MSG_MF_ERROR_NULL_EXPIRY (R.string.msg_mf_error_null_expiry), + MSG_MF_ERROR_PASSPHRASE_MASTER(R.string.msg_mf_error_passphrase_master), + MSG_MF_ERROR_PAST_EXPIRY(R.string.msg_mf_error_past_expiry), MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp), + MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary), MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig), + MSG_MF_ERROR_SUBKEY_MISSING(R.string.msg_mf_error_subkey_missing), + MSG_MF_MASTER (R.string.msg_mf_master), MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase), + MSG_MF_PASSPHRASE_KEY (R.string.msg_mf_passphrase_key), + MSG_MF_PASSPHRASE_EMPTY_RETRY (R.string.msg_mf_passphrase_empty_retry), + MSG_MF_PASSPHRASE_FAIL (R.string.msg_mf_passphrase_fail), 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), MSG_MF_SUBKEY_NEW (R.string.msg_mf_subkey_new), - MSG_MF_SUBKEY_PAST_EXPIRY (R.string.msg_mf_subkey_past_expiry), MSG_MF_SUBKEY_REVOKE (R.string.msg_mf_subkey_revoke), MSG_MF_SUCCESS (R.string.msg_mf_success), MSG_MF_UID_ADD (R.string.msg_mf_uid_add), @@ -377,6 +393,32 @@ public class OperationResultParcel implements Parcelable { 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), + + // consolidate + MSG_CON_CRITICAL_IN (R.string.msg_con_critical_in), + MSG_CON_CRITICAL_OUT (R.string.msg_con_critical_out), + MSG_CON_DB_CLEAR (R.string.msg_con_db_clear), + MSG_CON_DELETE_PUBLIC (R.string.msg_con_delete_public), + MSG_CON_DELETE_SECRET (R.string.msg_con_delete_secret), + MSG_CON_ERROR_BAD_STATE (R.string.msg_con_error_bad_state), + MSG_CON_ERROR_CONCURRENT(R.string.msg_con_error_concurrent), + MSG_CON_ERROR_DB (R.string.msg_con_error_db), + MSG_CON_ERROR_IO_PUBLIC (R.string.msg_con_error_io_public), + MSG_CON_ERROR_IO_SECRET (R.string.msg_con_error_io_secret), + MSG_CON_ERROR_PUBLIC (R.string.msg_con_error_public), + MSG_CON_ERROR_SECRET (R.string.msg_con_error_secret), + MSG_CON_RECOVER (R.plurals.msg_con_recover), + MSG_CON_RECOVER_UNKNOWN (R.string.msg_con_recover_unknown), + MSG_CON_REIMPORT_PUBLIC (R.plurals.msg_con_reimport_public), + MSG_CON_REIMPORT_PUBLIC_SKIP (R.string.msg_con_reimport_public_skip), + MSG_CON_REIMPORT_SECRET (R.plurals.msg_con_reimport_secret), + MSG_CON_REIMPORT_SECRET_SKIP (R.string.msg_con_reimport_secret_skip), + MSG_CON (R.string.msg_con), + MSG_CON_SAVE_PUBLIC (R.string.msg_con_save_public), + MSG_CON_SAVE_SECRET (R.string.msg_con_save_secret), + MSG_CON_SUCCESS (R.string.msg_con_success), + MSG_CON_WARN_DELETE_PUBLIC (R.string.msg_con_warn_delete_public), + MSG_CON_WARN_DELETE_SECRET (R.string.msg_con_warn_delete_secret), ; private final int mMsgId; @@ -406,7 +448,9 @@ public class OperationResultParcel implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mResult); - dest.writeTypedList(mLog.toList()); + if (mLog != null) { + dest.writeTypedList(mLog.toList()); + } } public static final Creator CREATOR = new Creator() { @@ -432,6 +476,15 @@ public class OperationResultParcel implements Parcelable { mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null)); } + public boolean containsType(LogType type) { + for(LogEntryParcel entry : new IterableIterator(mParcels.iterator())) { + if (entry.mType == type) { + return true; + } + } + return false; + } + public boolean containsWarnings() { for(LogEntryParcel entry : new IterableIterator(mParcels.iterator())) { if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) { 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 543b83edb..f3d0b9e9b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -28,7 +29,10 @@ 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.pgp.CanonicalizedKeyRing; +import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.ui.LogDisplayActivity; import org.sufficientlysecure.keychain.ui.LogDisplayFragment; @@ -37,7 +41,7 @@ public abstract class OperationResults { public static class ImportKeyResult extends OperationResultParcel { - public final int mNewKeys, mUpdatedKeys, mBadKeys; + public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret; // At least one new key public static final int RESULT_OK_NEWKEYS = 2; @@ -49,18 +53,21 @@ public abstract class OperationResults { public static final int RESULT_WITH_WARNINGS = 16; // No keys to import... - public static final int RESULT_FAIL_NOTHING = 32 +1; + public static final int RESULT_FAIL_NOTHING = 32 + 1; public boolean isOkBoth() { return (mResult & (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED)) == (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED); } + public boolean isOkNew() { return (mResult & RESULT_OK_NEWKEYS) == RESULT_OK_NEWKEYS; } + public boolean isOkUpdated() { return (mResult & RESULT_OK_UPDATED) == RESULT_OK_UPDATED; } + public boolean isFailNothing() { return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING; } @@ -70,14 +77,16 @@ public abstract class OperationResults { mNewKeys = source.readInt(); mUpdatedKeys = source.readInt(); mBadKeys = source.readInt(); + mSecret = source.readInt(); } public ImportKeyResult(int result, OperationLog log, - int newKeys, int updatedKeys, int badKeys) { + int newKeys, int updatedKeys, int badKeys, int secret) { super(result, log); mNewKeys = newKeys; mUpdatedKeys = updatedKeys; mBadKeys = badKeys; + mSecret = secret; } @Override @@ -86,6 +95,7 @@ public abstract class OperationResults { dest.writeInt(mNewKeys); dest.writeInt(mUpdatedKeys); dest.writeInt(mBadKeys); + dest.writeInt(mSecret); } public static Creator CREATOR = new Creator() { @@ -124,7 +134,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( @@ -185,13 +195,13 @@ public abstract class OperationResults { public static class EditKeyResult extends OperationResultParcel { private transient UncachedKeyRing mRing; - public final Long mRingMasterKeyId; + public final long mRingMasterKeyId; public EditKeyResult(int result, OperationLog log, - UncachedKeyRing ring) { + UncachedKeyRing ring) { super(result, log); mRing = ring; - mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : null; + mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none; } public UncachedKeyRing getRing() { @@ -224,8 +234,12 @@ public abstract class OperationResults { public static class SaveKeyringResult extends OperationResultParcel { - public SaveKeyringResult(int result, OperationLog log) { + public final long mRingMasterKeyId; + + public SaveKeyringResult(int result, OperationLog log, + CanonicalizedKeyRing ring) { super(result, log); + mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none; } // Some old key was updated @@ -240,6 +254,34 @@ public abstract class OperationResults { return (mResult & UPDATED) == UPDATED; } + public SaveKeyringResult(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 CREATOR = new Creator() { + public SaveKeyringResult createFromParcel(final Parcel source) { + return new SaveKeyringResult(source); + } + + public SaveKeyringResult[] newArray(final int size) { + return new SaveKeyringResult[size]; + } + }; + } + + public static class ConsolidateResult extends OperationResultParcel { + + public ConsolidateResult(int result, OperationLog log) { + super(result, 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 8cd9876eb..1b357bd65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -78,7 +78,7 @@ public class PassphraseCacheService extends Service { private static final int NOTIFICATION_ID = 1; private static final int MSG_PASSPHRASE_CACHE_GET_OKAY = 1; - private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND = 2; + private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND = 2; private BroadcastReceiver mIntentReceiver; @@ -170,7 +170,7 @@ public class PassphraseCacheService extends Service { switch (returnMessage.what) { case MSG_PASSPHRASE_CACHE_GET_OKAY: return returnMessage.getData().getString(EXTRA_PASSPHRASE); - case MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND: + case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND: throw new KeyNotFoundException(); default: throw new KeyNotFoundException("should not happen!"); @@ -322,7 +322,7 @@ public class PassphraseCacheService extends Service { msg.setData(bundle); } catch (ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!"); - msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND; + msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND; } try { 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 490a8e738..996ce6a5a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -79,14 +80,16 @@ public class SaveKeyringParcel implements Parcelable { // performance gain for using Parcelable here would probably be negligible, // use Serializable instead. public static class SubkeyAdd implements Serializable { - public int mAlgorithm; - public int mKeysize; + public Algorithm mAlgorithm; + public Integer mKeySize; + public Curve mCurve; public int mFlags; public Long mExpiry; - public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) { + public SubkeyAdd(Algorithm algorithm, Integer keySize, Curve curve, int flags, Long expiry) { mAlgorithm = algorithm; - mKeysize = keysize; + mKeySize = keySize; + mCurve = curve; mFlags = flags; mExpiry = expiry; } @@ -94,7 +97,8 @@ public class SaveKeyringParcel implements Parcelable { @Override public String toString() { String out = "mAlgorithm: " + mAlgorithm + ", "; - out += "mKeysize: " + mKeysize + ", "; + out += "mKeySize: " + mKeySize + ", "; + out += "mCurve: " + mCurve + ", "; out += "mFlags: " + mFlags; out += "mExpiry: " + mExpiry; @@ -213,4 +217,20 @@ public class SaveKeyringParcel implements Parcelable { return out; } + + // All supported algorithms + public enum Algorithm { + RSA, DSA, ELGAMAL, ECDSA, ECDH + } + + // All curves defined in the standard + // http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269 + public enum Curve { + NIST_P256, NIST_P384, NIST_P521, + + // these are supported by gpg, but they are not in rfc6637 and not supported by BouncyCastle yet + // (adding support would be trivial though -> JcaPGPKeyConverter.java:190) + // BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512 + } + } 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 c1986825c..8b99d474d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser * Copyright (C) 2011 Senecaso * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,6 +41,7 @@ import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageView; import android.widget.ListView; +import android.widget.ScrollView; import android.widget.Spinner; import android.widget.TextView; @@ -53,9 +55,12 @@ 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.OperationResults; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -64,18 +69,18 @@ import java.util.ArrayList; /** * Signs the specified public key with the specified secret master key */ -public class CertifyKeyActivity extends ActionBarActivity implements - SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks { +public class CertifyKeyActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks { private View mCertifyButton; private ImageView mActionCertifyImage; private CheckBox mUploadKeyCheckbox; private Spinner mSelectKeyserverSpinner; + private ScrollView mScrollView; - private SelectSecretKeyLayoutFragment mSelectKeyFragment; + private CertifyKeySpinner mCertifyKeySpinner; private Uri mDataUri; - private long mPubKeyId = 0; - private long mMasterKeyId = 0; + private long mPubKeyId = Constants.key.none; + private long mMasterKeyId = Constants.key.none; private ListView mUserIds; private UserIdsAdapter mUserIdsAdapter; @@ -89,20 +94,24 @@ public class CertifyKeyActivity extends ActionBarActivity implements setContentView(R.layout.certify_key_activity); - mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager() - .findFragmentById(R.id.sign_key_select_key_fragment); + mCertifyKeySpinner = (CertifyKeySpinner) findViewById(R.id.certify_key_spinner); 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); + mScrollView = (ScrollView) findViewById(R.id.certify_scroll_view); // 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); + mCertifyKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { + @Override + public void onKeyChanged(long masterKeyId) { + mMasterKeyId = masterKeyId; + } + }); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, Preferences.getPreferences(this) @@ -135,9 +144,9 @@ public class CertifyKeyActivity extends ActionBarActivity implements 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); + scrollUp(); } else { initiateCertifying(); } @@ -162,6 +171,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); } + private void scrollUp() { + mScrollView.post(new Runnable() { + public void run() { + mScrollView.fullScroll(ScrollView.FOCUS_UP); + } + }); + } + static final String USER_IDS_SELECTION = UserIds.IS_REVOKED + " = 0"; static final String[] KEYRING_PROJECTION = @@ -199,6 +216,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements if (data.moveToFirst()) { // TODO: put findViewById in onCreate! mPubKeyId = data.getLong(INDEX_MASTER_KEY_ID); + mCertifyKeySpinner.setHiddenMasterKeyId(mPubKeyId); String keyIdStr = PgpKeyHelper.convertKeyIdToHex(mPubKeyId); ((TextView) findViewById(R.id.key_id)).setText(keyIdStr); @@ -213,6 +231,9 @@ public class CertifyKeyActivity extends ActionBarActivity implements break; case LOADER_ID_USER_IDS: mUserIdsAdapter.swapCursor(data); + // when some user ids are pre-checked, the focus is requested and the scroll view goes + // down. This fixes it. + scrollUp(); break; } } @@ -292,8 +313,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success, - Notify.Style.INFO); +// Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success, +// Notify.Style.INFO); + + OperationResultParcel result = new OperationResultParcel(OperationResultParcel.RESULT_OK, null); + Intent intent = new Intent(); + intent.putExtra(OperationResultParcel.EXTRA_RESULT, result); + CertifyKeyActivity.this.setResult(RESULT_OK, intent); + CertifyKeyActivity.this.finish(); // check if we need to send the key to the server or not if (mUploadKeyCheckbox.isChecked()) { @@ -345,13 +372,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - Intent intent = new Intent(); - intent.putExtra(OperationResultParcel.EXTRA_RESULT, message.getData()); - Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success, - Notify.Style.INFO); + //Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success, + //Notify.Style.INFO); - setResult(RESULT_OK); - finish(); + OperationResultParcel result = new OperationResultParcel(OperationResultParcel.RESULT_OK, null); + Intent intent = new Intent(); + intent.putExtra(OperationResultParcel.EXTRA_RESULT, result); + CertifyKeyActivity.this.setResult(RESULT_OK, intent); + CertifyKeyActivity.this.finish(); } } }; @@ -367,14 +395,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements startService(intent); } - /** - * callback from select key fragment - */ - @Override - public void onKeySelected(long secretKeyId) { - mMasterKeyId = secretKeyId; - } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java new file mode 100644 index 000000000..edc05e28d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.FragmentActivity; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; + +/** + * We can not directly create a dialog on the application context. + * This activity encapsulates a DialogFragment to emulate a dialog. + */ +public class ConsolidateDialogActivity extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // this activity itself has no content view (see manifest) + + consolidateRecovery(); + + } + + private void consolidateRecovery() { + // Message is received after importing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + this, + getString(R.string.progress_importing), + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + /* don't care about the results (for now?) + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final ConsolidateResult result = + returnData.getParcelable(KeychainIntentService.RESULT_CONSOLIDATE); + if (result == null) { + return; + } + result.createNotify(ConsolidateDialogActivity.this).show(); + */ + + ConsolidateDialogActivity.this.finish(); + } + } + }; + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE); + + // fill values for this action + Bundle data = new Bundle(); + data.putBoolean(KeychainIntentService.CONSOLIDATE_RECOVERY, true); + 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); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index 773be816a..69f4af04b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -33,6 +33,7 @@ import android.widget.TextView; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; 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.pgp.KeyRing; @@ -42,6 +43,8 @@ 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.service.SaveKeyringParcel.Algorithm; +import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; public class CreateKeyFinalFragment extends Fragment { @@ -125,10 +128,9 @@ public class CreateKeyFinalFragment extends Fragment { 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), + getString(R.string.progress_building_key), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first @@ -140,26 +142,21 @@ public class CreateKeyFinalFragment extends Fragment { if (returnData == null) { return; } - final OperationResults.EditKeyResult result = + final OperationResults.SaveKeyringResult result = returnData.getParcelable(OperationResultParcel.EXTRA_RESULT); if (result == null) { + Log.e(Constants.TAG, "result == null"); return; } - if (result.getResult() == OperationResultParcel.RESULT_OK) { - if (mUploadCheckbox.isChecked()) { - // result will be displayed after upload - uploadKey(result); - } else { - // TODO: return result - result.createNotify(getActivity()); - - getActivity().setResult(Activity.RESULT_OK); - getActivity().finish(); - } + if (mUploadCheckbox.isChecked()) { + // result will be displayed after upload + uploadKey(result); } else { - // display result on error without finishing activity - result.createNotify(getActivity()); + Intent data = new Intent(); + data.putExtra(OperationResultParcel.EXTRA_RESULT, result); + getActivity().setResult(Activity.RESULT_OK, data); + getActivity().finish(); } } } @@ -169,9 +166,12 @@ public class CreateKeyFinalFragment extends Fragment { Bundle data = new Bundle(); SaveKeyringParcel parcel = new SaveKeyringParcel(); - parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.CERTIFY_OTHER, null)); - parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.SIGN_DATA, null)); - parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null)); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 0L)); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L)); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); String userId = KeyRing.createUserId(mName, mEmail, null); parcel.mAddUserIds.add(userId); parcel.mChangePrimaryUserId = userId; @@ -191,7 +191,7 @@ public class CreateKeyFinalFragment extends Fragment { getActivity().startService(intent); } - private void uploadKey(final OperationResults.EditKeyResult editKeyResult) { + private void uploadKey(final OperationResults.SaveKeyringResult saveKeyResult) { // Send all information needed to service to upload key in other thread final Intent intent = new Intent(getActivity(), KeychainIntentService.class); @@ -199,7 +199,7 @@ public class CreateKeyFinalFragment extends Fragment { // set data uri as path to keyring Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri( - editKeyResult.mRingMasterKeyId); + saveKeyResult.mRingMasterKeyId); intent.setData(blobUri); // fill values for this action @@ -211,7 +211,6 @@ public class CreateKeyFinalFragment extends Fragment { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after uploading is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), getString(R.string.progress_uploading), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { @@ -219,20 +218,16 @@ public class CreateKeyFinalFragment extends Fragment { super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - // TODO: not supported by upload? -// if (result.getResult() == OperationResultParcel.RESULT_OK) { - // TODO: return result - editKeyResult.createNotify(getActivity()); + // TODO: upload operation needs a result! + // TODO: then combine these results + //if (result.getResult() == OperationResultParcel.RESULT_OK) { + //Notify.showNotify(getActivity(), R.string.key_send_success, + //Notify.Style.INFO); - Notify.showNotify(getActivity(), R.string.key_send_success, - Notify.Style.INFO); - - getActivity().setResult(Activity.RESULT_OK); + Intent data = new Intent(); + data.putExtra(OperationResultParcel.EXTRA_RESULT, saveKeyResult); + getActivity().setResult(Activity.RESULT_OK, data); getActivity().finish(); -// } else { -// // display result on error without finishing activity -// editKeyResult.createNotify(getActivity()); -// } } } }; 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 03074bb6a..28692f638 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -48,8 +48,7 @@ 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.OperationResultParcel; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter; @@ -340,7 +339,8 @@ public class EditKeyFragment extends LoaderFragment implements } else { mSaveKeyringParcel.mRevokeUserIds.add(userId); // not possible to revoke and change to primary user id - if (mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { + if (mSaveKeyringParcel.mChangePrimaryUserId != null + && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { mSaveKeyringParcel.mChangePrimaryUserId = null; } } @@ -407,10 +407,10 @@ public class EditKeyFragment extends LoaderFragment implements @Override public void handleMessage(Message message) { switch (message.what) { - case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE: - Long expiry = (Long) message.getData(). - getSerializable(EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY_DATE); - mSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry = expiry; + case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY: + mSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry = + (Long) message.getData().getSerializable( + EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY); break; } getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad(); @@ -504,7 +504,6 @@ public class EditKeyFragment extends LoaderFragment implements private void save(String passphrase) { Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString()); - // Message is received after importing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( getActivity(), getString(R.string.progress_saving), @@ -520,8 +519,8 @@ public class EditKeyFragment extends LoaderFragment implements if (returnData == null) { return; } - final OperationResults.EditKeyResult result = - returnData.getParcelable(EditKeyResult.EXTRA_RESULT); + final OperationResultParcel result = + returnData.getParcelable(OperationResultParcel.EXTRA_RESULT); if (result == null) { return; } @@ -534,7 +533,7 @@ public class EditKeyFragment extends LoaderFragment implements // if good -> finish, return result to showkey and display there! Intent intent = new Intent(); - intent.putExtra(EditKeyResult.EXTRA_RESULT, result); + intent.putExtra(OperationResultParcel.EXTRA_RESULT, result); getActivity().setResult(EditKeyActivity.RESULT_OK, intent); getActivity().finish(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 7e08f6b7c..4cd694d48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -73,16 +73,13 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids"; // view - ViewPager mViewPagerMode; - //PagerTabStrip mPagerTabStripMode; - PagerTabStripAdapter mTabsAdapterMode; - ViewPager mViewPagerContent; - PagerTabStrip mPagerTabStripContent; - PagerTabStripAdapter mTabsAdapterContent; + private int mCurrentMode = PAGER_MODE_ASYMMETRIC; + private ViewPager mViewPagerContent; + private PagerTabStrip mPagerTabStripContent; + private PagerTabStripAdapter mTabsAdapterContent; // tabs - int mSwitchToMode = PAGER_MODE_ASYMMETRIC; - int mSwitchToContent = PAGER_CONTENT_MESSAGE; + private int mSwitchToContent = PAGER_CONTENT_MESSAGE; private static final int PAGER_MODE_ASYMMETRIC = 0; private static final int PAGER_MODE_SYMMETRIC = 1; @@ -102,7 +99,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn private String mMessage = ""; public boolean isModeSymmetric() { - return PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem(); + return PAGER_MODE_SYMMETRIC == mCurrentMode; } public boolean isContentMessage() { @@ -312,59 +309,60 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn String title = isContentMessage() ? getString(R.string.title_share_message) : getString(R.string.title_share_file); - // fallback on Android 2.3, otherwise we would get weird results - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - return Intent.createChooser(prototype, title); - } - - // prevent recursion aka Inception :P - String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"}; - - List targetedShareIntents = new ArrayList(); - - List resInfoList = getPackageManager().queryIntentActivities(prototype, 0); - List resInfoListFiltered = new ArrayList(); - if (!resInfoList.isEmpty()) { - for (ResolveInfo resolveInfo : resInfoList) { - // do not add blacklisted ones - if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name)) - continue; - - resInfoListFiltered.add(resolveInfo); - } - - if (!resInfoListFiltered.isEmpty()) { - // sorting for nice readability - Collections.sort(resInfoListFiltered, new Comparator() { - @Override - public int compare(ResolveInfo first, ResolveInfo second) { - String firstName = first.loadLabel(getPackageManager()).toString(); - String secondName = second.loadLabel(getPackageManager()).toString(); - return firstName.compareToIgnoreCase(secondName); - } - }); - - // create the custom intent list - for (ResolveInfo resolveInfo : resInfoListFiltered) { - Intent targetedShareIntent = (Intent) prototype.clone(); - targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName); - targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); - - LabeledIntent lIntent = new LabeledIntent(targetedShareIntent, - resolveInfo.activityInfo.packageName, - resolveInfo.loadLabel(getPackageManager()), - resolveInfo.activityInfo.icon); - targetedShareIntents.add(lIntent); - } - - // Create chooser with only one Intent in it - Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title); - // append all other Intents - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{})); - return chooserIntent; - } - - } + // Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3 +// // fallback on Android 2.3, otherwise we would get weird results +// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { +// return Intent.createChooser(prototype, title); +// } +// +// // prevent recursion aka Inception :P +// String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"}; +// +// List targetedShareIntents = new ArrayList(); +// +// List resInfoList = getPackageManager().queryIntentActivities(prototype, 0); +// List resInfoListFiltered = new ArrayList(); +// if (!resInfoList.isEmpty()) { +// for (ResolveInfo resolveInfo : resInfoList) { +// // do not add blacklisted ones +// if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name)) +// continue; +// +// resInfoListFiltered.add(resolveInfo); +// } +// +// if (!resInfoListFiltered.isEmpty()) { +// // sorting for nice readability +// Collections.sort(resInfoListFiltered, new Comparator() { +// @Override +// public int compare(ResolveInfo first, ResolveInfo second) { +// String firstName = first.loadLabel(getPackageManager()).toString(); +// String secondName = second.loadLabel(getPackageManager()).toString(); +// return firstName.compareToIgnoreCase(secondName); +// } +// }); +// +// // create the custom intent list +// for (ResolveInfo resolveInfo : resInfoListFiltered) { +// Intent targetedShareIntent = (Intent) prototype.clone(); +// targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName); +// targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); +// +// LabeledIntent lIntent = new LabeledIntent(targetedShareIntent, +// resolveInfo.activityInfo.packageName, +// resolveInfo.loadLabel(getPackageManager()), +// resolveInfo.activityInfo.icon); +// targetedShareIntents.add(lIntent); +// } +// +// // Create chooser with only one Intent in it +// Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title); +// // append all other Intents +// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{})); +// return chooserIntent; +// } +// +// } // fallback to Android's default chooser return Intent.createChooser(prototype, title); @@ -385,7 +383,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE); sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris); } - sendIntent.setType("application/pgp-encrypted"); + sendIntent.setType("application/octet-stream"); } if (!isModeSymmetric() && mEncryptionUserIds != null) { Set users = new HashSet(); @@ -474,13 +472,9 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn } private void initView() { - mViewPagerMode = (ViewPager) findViewById(R.id.encrypt_pager_mode); - //mPagerTabStripMode = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_mode); mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content); mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content); - mTabsAdapterMode = new PagerTabStripAdapter(this); - mViewPagerMode.setAdapter(mTabsAdapterMode); mTabsAdapterContent = new PagerTabStripAdapter(this); mViewPagerContent.setAdapter(mTabsAdapterContent); } @@ -502,10 +496,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn // Handle intent actions handleActions(getIntent()); - - mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class, null, getString(R.string.label_asymmetric)); - mTabsAdapterMode.addTab(EncryptSymmetricFragment.class, null, getString(R.string.label_symmetric)); - mViewPagerMode.setCurrentItem(mSwitchToMode); + updateModeFragment(); mTabsAdapterContent.addTab(EncryptMessageFragment.class, null, getString(R.string.label_message)); mTabsAdapterContent.addTab(EncryptFileFragment.class, null, getString(R.string.label_files)); @@ -521,6 +512,16 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn return super.onCreateOptionsMenu(menu); } + private void updateModeFragment() { + getSupportFragmentManager().beginTransaction() + .replace(R.id.encrypt_pager_mode, + mCurrentMode == PAGER_MODE_SYMMETRIC + ? new EncryptSymmetricFragment() + : new EncryptAsymmetricFragment()) + .commitAllowingStateLoss(); + getSupportFragmentManager().executePendingTransactions(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.isCheckable()) { @@ -528,9 +529,8 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn } switch (item.getItemId()) { case R.id.check_use_symmetric: - mSwitchToMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC; - - mViewPagerMode.setCurrentItem(mSwitchToMode); + mCurrentMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC; + updateModeFragment(); notifyUpdate(); break; case R.id.check_use_armor: @@ -604,7 +604,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); // preselect keys given by intent - mSwitchToMode = PAGER_MODE_ASYMMETRIC; + mCurrentMode = PAGER_MODE_ASYMMETRIC; /** * Main Actions diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java index a402b6f68..a508472d6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java @@ -18,35 +18,22 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.Spinner; -import android.widget.SpinnerAdapter; -import android.widget.TextView; import com.tokenautocomplete.TokenCompleteTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView; +import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -54,22 +41,20 @@ import java.util.Iterator; import java.util.List; public class EncryptAsymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener { - public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id"; - public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids"; - ProviderHelper mProviderHelper; // view - private Spinner mSign; + private KeySpinner mSign; private EncryptKeyCompletionView mEncryptKeyView; - private SelectSignKeyCursorAdapter mSignAdapter = new SelectSignKeyCursorAdapter(); // model private EncryptActivityInterface mEncryptInterface; @Override public void onNotifyUpdate() { - + if (mSign != null) { + mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey()); + } } @Override @@ -101,17 +86,11 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false); - mSign = (Spinner) view.findViewById(R.id.sign); - mSign.setAdapter(mSignAdapter); - mSign.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + mSign = (KeySpinner) view.findViewById(R.id.sign); + mSign.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - setSignatureKeyId(parent.getAdapter().getItemId(position)); - } - - @Override - public void onNothingSelected(AdapterView parent) { - setSignatureKeyId(Constants.key.none); + public void onKeyChanged(long masterKeyId) { + setSignatureKeyId(masterKeyId); } }); mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list); @@ -128,42 +107,6 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi // preselect keys given preselectKeys(); - getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks() { - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); - - // These are the rows that we will retrieve. - String[] projection = new String[]{ - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - KeyRings.KEY_ID, - KeyRings.USER_ID, - KeyRings.IS_EXPIRED, - KeyRings.HAS_SIGN, - KeyRings.HAS_ANY_SECRET - }; - - String where = KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeyRings.HAS_SIGN + " NOT NULL AND " - + KeyRings.IS_REVOKED + " = 0 AND " + KeyRings.IS_EXPIRED + " = 0"; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, where, null, null); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - mSignAdapter.swapCursor(data); - } - - @Override - public void onLoaderReset(Loader loader) { - mSignAdapter.swapCursor(null); - } - }); mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() { @Override public void onTokenAdded(Object token) { @@ -194,6 +137,7 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi KeyRings.buildUnifiedKeyRingUri(signatureKey)); if(keyring.hasAnySecret()) { setSignatureKeyId(keyring.getMasterKeyId()); + mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey()); } } catch (PgpGeneralException e) { Log.e(Constants.TAG, "key not found!", e); @@ -211,6 +155,8 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi Log.e(Constants.TAG, "key not found!", e); } } + // This is to work-around a rendering bug in TokenCompleteTextView + mEncryptKeyView.requestFocus(); updateEncryptionKeys(); } } @@ -233,95 +179,4 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi setEncryptionKeyIds(keyIdsArr); setEncryptionUserIds(userIds.toArray(new String[userIds.size()])); } - - private class SelectSignKeyCursorAdapter extends BaseAdapter implements SpinnerAdapter { - private CursorAdapter inner; - private int mIndexUserId; - private int mIndexKeyId; - private int mIndexMasterKeyId; - - public SelectSignKeyCursorAdapter() { - inner = new CursorAdapter(null, null, 0) { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return getActivity().getLayoutInflater().inflate(R.layout.encrypt_asymmetric_signkey, null); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); - ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")")); - ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]); - ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId))); - } - - @Override - public long getItemId(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(mIndexMasterKeyId); - } - }; - } - - public Cursor swapCursor(Cursor newCursor) { - if (newCursor == null) return inner.swapCursor(null); - - mIndexKeyId = newCursor.getColumnIndex(KeyRings.KEY_ID); - mIndexUserId = newCursor.getColumnIndex(KeyRings.USER_ID); - mIndexMasterKeyId = newCursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - if (newCursor.moveToFirst()) { - do { - if (newCursor.getLong(mIndexMasterKeyId) == mEncryptInterface.getSignatureKey()) { - mSign.setSelection(newCursor.getPosition() + 1); - } - } while (newCursor.moveToNext()); - } - return inner.swapCursor(newCursor); - } - - @Override - public int getCount() { - return inner.getCount() + 1; - } - - @Override - public Object getItem(int position) { - if (position == 0) return null; - return inner.getItem(position - 1); - } - - @Override - public long getItemId(int position) { - if (position == 0) return Constants.key.none; - return inner.getItemId(position - 1); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = getDropDownView(position, convertView, parent); - v.findViewById(android.R.id.text1).setVisibility(View.GONE); - return v; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - View v; - if (position == 0) { - if (convertView == null) { - v = inner.newView(null, null, parent); - } else { - v = convertView; - } - ((TextView) v.findViewById(android.R.id.title)).setText("None"); - v.findViewById(android.R.id.text1).setVisibility(View.GONE); - v.findViewById(android.R.id.text2).setVisibility(View.GONE); - } else { - v = inner.getView(position - 1, convertView, parent); - v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE); - v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE); - } - return v; - } - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java index 5f3f170a1..7b608a0a2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java @@ -51,7 +51,7 @@ public class FirstTimeActivity extends ActionBarActivity { mSkipSetup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - finishSetup(); + finishSetup(null); } }); @@ -80,18 +80,22 @@ public class FirstTimeActivity extends ActionBarActivity { if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) { if (resultCode == RESULT_OK) { - finishSetup(); + finishSetup(data); } } else { Log.e(Constants.TAG, "No valid request code!"); } } - private void finishSetup() { + private void finishSetup(Intent srcData) { Preferences prefs = Preferences.getPreferences(this); prefs.setFirstTime(false); - Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class); - startActivity(intent); + Intent intent = new Intent(this, KeyListActivity.class); + // give intent through to display notify + if (srcData != null) { + intent.putExtras(srcData); + } + startActivityForResult(intent, 0); 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 255290de3..96fa11363 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; @@ -64,7 +65,7 @@ public class ImportKeysActivity extends ActionBarActivity { + "IMPORT_KEY_FROM_KEYSERVER"; public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN_RESULT"; - public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX + public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN"; public static final String ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_FILE_AND_RETURN"; @@ -87,7 +88,7 @@ public class ImportKeysActivity extends ActionBarActivity { public static final String EXTRA_KEY_ID = "key_id"; public static final String EXTRA_FINGERPRINT = "fingerprint"; - // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN when used from OpenPgpService + // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE when used from OpenPgpService public static final String EXTRA_PENDING_INTENT_DATA = "data"; private Intent mPendingIntentData; @@ -170,7 +171,7 @@ public class ImportKeysActivity extends ActionBarActivity { startListFragment(savedInstanceState, importData, null, null); } } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action) - || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(action) + || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(action) || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(action)) { // only used for OpenPgpService @@ -287,7 +288,9 @@ public class ImportKeysActivity extends ActionBarActivity { @Override public void onPageSelected(int position) { // cancel loader and clear list - mListFragment.destroyLoader(); + if (mListFragment != null) { + mListFragment.destroyLoader(); + } } @Override @@ -456,28 +459,25 @@ public class ImportKeysActivity extends ActionBarActivity { return; } final ImportKeyResult result = - returnData.getParcelable(KeychainIntentService.RESULT_IMPORT); + returnData.getParcelable(OperationResultParcel.EXTRA_RESULT); if (result == null) { + Log.e(Constants.TAG, "result == null"); return; } - if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())) { + if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction()) + || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) { Intent intent = new Intent(); intent.putExtra(ImportKeyResult.EXTRA_RESULT, result); ImportKeysActivity.this.setResult(RESULT_OK, intent); ImportKeysActivity.this.finish(); return; } - if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { + if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) { ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData); ImportKeysActivity.this.finish(); return; } - if (ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) { - ImportKeysActivity.this.setResult(RESULT_OK); - ImportKeysActivity.this.finish(); - return; - } result.createNotify(ImportKeysActivity.this).show(); } @@ -503,7 +503,8 @@ public class ImportKeysActivity extends ActionBarActivity { // to prevent Java Binder problems on heavy imports // read FileImportCache for more info. try { - FileImportCache cache = new FileImportCache(this); + FileImportCache cache = + new FileImportCache(this, "key_import.pcl"); cache.writeCache(selectedEntries); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index d339bc132..88caebc32 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -29,15 +29,19 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ContactHelper; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.util.Log; +import java.util.List; + public class ImportKeysServerFragment extends Fragment { public static final String ARG_QUERY = "query"; public static final String ARG_KEYSERVER = "keyserver"; @@ -46,7 +50,7 @@ public class ImportKeysServerFragment extends Fragment { private ImportKeysActivity mImportActivity; private View mSearchButton; - private EditText mQueryEditText; + private AutoCompleteTextView mQueryEditText; private View mConfigButton; private View mConfigLayout; private Spinner mServerSpinner; @@ -75,7 +79,7 @@ public class ImportKeysServerFragment extends Fragment { View view = inflater.inflate(R.layout.import_keys_server_fragment, container, false); mSearchButton = view.findViewById(R.id.import_server_search); - mQueryEditText = (EditText) view.findViewById(R.id.import_server_query); + mQueryEditText = (AutoCompleteTextView) view.findViewById(R.id.import_server_query); mConfigButton = view.findViewById(R.id.import_server_config_button); mConfigLayout = view.findViewById(R.id.import_server_config); mServerSpinner = (Spinner) view.findViewById(R.id.import_server_spinner); @@ -93,6 +97,16 @@ public class ImportKeysServerFragment extends Fragment { mSearchButton.setEnabled(false); } + List namesAndEmails = ContactHelper.getContactNames(getActivity()); + namesAndEmails.addAll(ContactHelper.getContactMails(getActivity())); + mQueryEditText.setThreshold(3); + mQueryEditText.setAdapter( + new ArrayAdapter + (getActivity(), android.R.layout.simple_spinner_dropdown_item, + namesAndEmails + ) + ); + mSearchButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { 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 7a6e78a7d..e2ad241e2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -17,8 +18,11 @@ 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; @@ -28,6 +32,10 @@ import org.sufficientlysecure.keychain.helper.ExportHelper; 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.OperationResultParcel; +import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -63,6 +71,7 @@ public class KeyListActivity extends DrawerActivity { getMenuInflater().inflate(R.menu.key_list, menu); if (Constants.DEBUG) { + menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true); 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); @@ -92,6 +101,10 @@ public class KeyListActivity extends DrawerActivity { mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); return true; + case R.id.menu_key_list_debug_cons: + consolidate(); + return true; + case R.id.menu_key_list_debug_read: try { KeychainDatabase.debugBackup(this, true); @@ -133,7 +146,67 @@ public class KeyListActivity extends DrawerActivity { private void createKey() { Intent intent = new Intent(this, CreateKeyActivity.class); - startActivity(intent); + startActivityForResult(intent, 0); + } + + private void consolidate() { + // Message is received after importing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + this, + getString(R.string.progress_importing), + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final ConsolidateResult result = + returnData.getParcelable(OperationResultParcel.EXTRA_RESULT); + if (result == null) { + return; + } + + result.createNotify(KeyListActivity.this).show(); + } + } + }; + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE); + + // fill values for this action + Bundle data = new Bundle(); + + 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); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify + 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); + } } } 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 3c97b1128..b4676f9b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2013-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -79,14 +80,15 @@ public class KeyListFragment extends LoaderFragment private KeyListAdapter mAdapter; private StickyListHeadersListView mStickyList; + // saves the mode object for multiselect, needed for reset at some point + private ActionMode mActionMode = null; + private String mQuery; private SearchView mSearchView; // empty list layout private Button mButtonEmptyCreate; private Button mButtonEmptyImport; - public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012; - /** * Load custom layout with StickyListView from library */ @@ -105,7 +107,7 @@ public class KeyListFragment extends LoaderFragment @Override public void onClick(View v) { Intent intent = new Intent(getActivity(), CreateKeyActivity.class); - startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); + startActivityForResult(intent, 0); } }); mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import); @@ -115,7 +117,7 @@ public class KeyListFragment extends LoaderFragment public void onClick(View v) { Intent intent = new Intent(getActivity(), ImportKeysActivity.class); intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); - startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); + startActivityForResult(intent, 0); } }); @@ -148,6 +150,7 @@ public class KeyListFragment extends LoaderFragment public boolean onCreateActionMode(ActionMode mode, Menu menu) { android.view.MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.key_list_multi, menu); + mActionMode = mode; return true; } @@ -193,6 +196,7 @@ public class KeyListFragment extends LoaderFragment @Override public void onDestroyActionMode(ActionMode mode) { + mActionMode = null; mAdapter.clearSelection(); } @@ -288,6 +292,13 @@ public class KeyListFragment extends LoaderFragment // this view is made visible if no data is available mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty)); + // end action mode, if any + if (mActionMode != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mActionMode.finish(); + } + } + // The list should now be shown. if (isResumed()) { setContentShown(true); @@ -330,12 +341,12 @@ public class KeyListFragment extends LoaderFragment * Show dialog to delete key * * @param masterKeyIds - * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not + * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { // Can only work on singular secret keys - if(hasSecret && masterKeyIds.length > 1) { + if (hasSecret && masterKeyIds.length > 1) { Notify.showNotify(getActivity(), R.string.secret_cannot_multiple, Notify.Style.ERROR); return; @@ -365,6 +376,7 @@ public class KeyListFragment extends LoaderFragment public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { // Get the searchview MenuItem searchItem = menu.findItem(R.id.menu_key_list_search); + mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem); // Execute this when searching @@ -383,7 +395,6 @@ public class KeyListFragment extends LoaderFragment @Override public boolean onMenuItemActionCollapse(MenuItem item) { mQuery = null; - mSearchView.setQuery("", true); getLoaderManager().restartLoader(0, null, KeyListFragment.this); return true; } @@ -399,11 +410,18 @@ public class KeyListFragment extends LoaderFragment @Override public boolean onQueryTextChange(String s) { + Log.d(Constants.TAG, "onQueryTextChange s:" + s); // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. - mQuery = !TextUtils.isEmpty(s) ? s : null; - getLoaderManager().restartLoader(0, null, this); + // If the nav drawer is opened, onQueryTextChange("") is executed. + // This hack prevents restarting the loader. + // TODO: better way to fix this? + String tmp = (mQuery == null) ? "" : mQuery; + if (!s.equals(tmp)) { + mQuery = s; + getLoaderManager().restartLoader(0, null, this); + } return true; } @@ -479,7 +497,7 @@ public class KeyListFragment extends LoaderFragment boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; boolean isExpired = !cursor.isNull(INDEX_EXPIRY) - && new Date(cursor.getLong(INDEX_EXPIRY)*1000).before(new Date()); + && new Date(cursor.getLong(INDEX_EXPIRY) * 1000).before(new Date()); boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; // Note: order is important! @@ -521,6 +539,7 @@ public class KeyListFragment extends LoaderFragment return mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0; } + public long getMasterKeyId(int id) { if (!mCursor.moveToPosition(id)) { throw new IllegalStateException("couldn't move cursor to position " + id); @@ -625,7 +644,7 @@ public class KeyListFragment extends LoaderFragment public boolean isAnySecretSelected() { for (int pos : mSelection.keySet()) { - if(mAdapter.isSecretAvailable(pos)) + if (mAdapter.isSecretAvailable(pos)) return true; } return false; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index b8386b144..248b8976a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 0e948bf7f..e524c3870 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 de3236d35..64c1e16be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java @@ -317,10 +317,10 @@ public class PreferencesActivity extends PreferenceActivity { private static void initializeHashAlgorithm (final IntegerListPreference mHashAlgorithm, int[] valueIds, String[] entries, String[] values) { - valueIds = new int[]{HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, + valueIds = new int[]{HashAlgorithmTags.RIPEMD160, HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512,}; - entries = new String[]{"MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", + entries = new String[]{"RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512",}; values = new String[valueIds.length]; for (int i = 0; i < values.length; ++i) { 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 341e11d1d..92f38a44c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2013-2014 Dominik Schürmann + * Copyright (C) 2014 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 @@ -166,7 +167,7 @@ public class ViewCertActivity extends ActionBarActivity mStatus.setTextColor(getResources().getColor(R.color.black)); } - String algorithmStr = PgpKeyHelper.getAlgorithmInfo(this, sig.getKeyAlgorithm(), 0); + String algorithmStr = PgpKeyHelper.getAlgorithmInfo(this, sig.getKeyAlgorithm(), null, null); mAlgorithm.setText(algorithmStr); mRowReason.setVisibility(View.GONE); 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 28f7b8bf5..2c0881ea4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -54,6 +54,8 @@ 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.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.OperationResultParcel; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout; @@ -303,8 +305,10 @@ public class ViewKeyActivity extends ActionBarActivity implements Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { - setResult(RESULT_CANCELED); - finish(); + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + setResult(RESULT_CANCELED); + finish(); + } } }; @@ -313,6 +317,7 @@ public class ViewKeyActivity extends ActionBarActivity implements @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // if a result has been returned, display a notify if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) { OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT); result.createNotify(this).show(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 5a55b0dad..786537b25 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 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 08243f06b..c37cb014e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 1f809cc51..1fed58721 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -155,8 +155,8 @@ public class ImportKeysAdapter extends ArrayAdapter { // don't show full fingerprint on key import holder.fingerprint.setVisibility(View.GONE); - if (entry.getBitStrength() != 0 && entry.getAlgorithm() != null) { - holder.algorithm.setText("" + entry.getBitStrength() + "/" + entry.getAlgorithm()); + if (entry.getAlgorithm() != null) { + holder.algorithm.setText(entry.getAlgorithm()); holder.algorithm.setVisibility(View.VISIBLE); } else { holder.algorithm.setVisibility(View.INVISIBLE); 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 d457e75bd..489cbcefb 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 @@ -35,7 +35,9 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import java.util.Calendar; import java.util.Date; +import java.util.TimeZone; public class SubkeysAdapter extends CursorAdapter { private LayoutInflater mInflater; @@ -50,6 +52,7 @@ public class SubkeysAdapter extends CursorAdapter { Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, + Keys.KEY_CURVE_OID, Keys.HAS_SECRET, Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, @@ -64,14 +67,15 @@ public class SubkeysAdapter extends CursorAdapter { private static final int INDEX_RANK = 2; private static final int INDEX_ALGORITHM = 3; private static final int INDEX_KEY_SIZE = 4; - private static final int INDEX_HAS_SECRET = 5; - private static final int INDEX_CAN_CERTIFY = 6; - private static final int INDEX_CAN_ENCRYPT = 7; - private static final int INDEX_CAN_SIGN = 8; - private static final int INDEX_IS_REVOKED = 9; - private static final int INDEX_CREATION = 10; - private static final int INDEX_EXPIRY = 11; - private static final int INDEX_FINGERPRINT = 12; + private static final int INDEX_KEY_CURVE_OID = 5; + private static final int INDEX_HAS_SECRET = 6; + private static final int INDEX_CAN_CERTIFY = 7; + private static final int INDEX_CAN_ENCRYPT = 8; + private static final int INDEX_CAN_SIGN = 9; + private static final int INDEX_IS_REVOKED = 10; + private static final int INDEX_CREATION = 11; + private static final int INDEX_EXPIRY = 12; + private static final int INDEX_FINGERPRINT = 13; public SubkeysAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { @@ -139,7 +143,8 @@ public class SubkeysAdapter extends CursorAdapter { String algorithmStr = PgpKeyHelper.getAlgorithmInfo( context, cursor.getInt(INDEX_ALGORITHM), - cursor.getInt(INDEX_KEY_SIZE) + cursor.getInt(INDEX_KEY_SIZE), + cursor.getString(INDEX_KEY_CURVE_OID) ); vKeyId.setText(keyIdStr); @@ -183,7 +188,7 @@ public class SubkeysAdapter extends CursorAdapter { SaveKeyringParcel.SubkeyChange subkeyChange = mSaveKeyringParcel.getSubkeyChange(keyId); if (subkeyChange != null) { - if (subkeyChange.mExpiry == null) { + if (subkeyChange.mExpiry == null || subkeyChange.mExpiry == 0L) { expiryDate = null; } else { expiryDate = new Date(subkeyChange.mExpiry * 1000); @@ -198,9 +203,13 @@ public class SubkeysAdapter extends CursorAdapter { boolean isExpired; if (expiryDate != null) { isExpired = expiryDate.before(new Date()); + Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + expiryCal.setTime(expiryDate); + // convert from UTC to time zone of device + expiryCal.setTimeZone(TimeZone.getDefault()); vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": " - + DateFormat.getDateFormat(context).format(expiryDate)); + + DateFormat.getDateFormat(context).format(expiryCal.getTime())); } else { isExpired = false; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java index be2e17c63..d50318fb4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java @@ -33,21 +33,19 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.TimeZone; public class SubkeysAddedAdapter extends ArrayAdapter { private LayoutInflater mInflater; private Activity mActivity; - // hold a private reference to the underlying data List - private List mData; - public SubkeysAddedAdapter(Activity activity, List data) { super(activity, -1, data); mActivity = activity; mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mData = data; } static class ViewHolder { @@ -101,16 +99,21 @@ public class SubkeysAddedAdapter extends ArrayAdapter= android.os.Build.VERSION_CODES.HONEYCOMB) { - mExpiryDatePicker.setMinDate(new Date().getTime() + DateUtils.DAY_IN_MILLIS); + // date picker works based on default time zone + Calendar minDateCal = Calendar.getInstance(TimeZone.getDefault()); + minDateCal.add(Calendar.DAY_OF_YEAR, 1); // at least one day after creation (today) + mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime()); } - ArrayList choices = new ArrayList(); - choices.add(new Choice(PublicKeyAlgorithmTags.DSA, getResources().getString( - R.string.dsa))); - if (!willBeMasterKey) { - choices.add(new Choice(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, getResources().getString( - R.string.elgamal))); - } - choices.add(new Choice(PublicKeyAlgorithmTags.RSA_GENERAL, getResources().getString( - R.string.rsa))); - ArrayAdapter adapter = new ArrayAdapter(context, - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mAlgorithmSpinner.setAdapter(adapter); - // make RSA the default - for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == PublicKeyAlgorithmTags.RSA_GENERAL) { - mAlgorithmSpinner.setSelection(i); - break; + { + ArrayList> choices = new ArrayList>(); + choices.add(new Choice(Algorithm.DSA, getResources().getString( + R.string.dsa))); + if (!mWillBeMasterKey) { + choices.add(new Choice(Algorithm.ELGAMAL, getResources().getString( + R.string.elgamal))); + } + choices.add(new Choice(Algorithm.RSA, getResources().getString( + R.string.rsa))); + choices.add(new Choice(Algorithm.ECDSA, getResources().getString( + R.string.ecdsa))); + choices.add(new Choice(Algorithm.ECDH, getResources().getString( + R.string.ecdh))); + ArrayAdapter> adapter = new ArrayAdapter>(context, + android.R.layout.simple_spinner_item, choices); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mAlgorithmSpinner.setAdapter(adapter); + // make RSA the default + for (int i = 0; i < choices.size(); ++i) { + if (choices.get(i).getId() == Algorithm.RSA) { + mAlgorithmSpinner.setSelection(i); + break; + } } } @@ -161,58 +178,42 @@ public class AddSubkeyDialogFragment extends DialogFragment { mKeySizeSpinner.setAdapter(keySizeAdapter); mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length + { + ArrayList> choices = new ArrayList>(); - dialog.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface di, int id) { - di.dismiss(); - Choice newKeyAlgorithmChoice = (Choice) mAlgorithmSpinner.getSelectedItem(); - int newKeySize = getProperKeyLength(newKeyAlgorithmChoice.getId(), getSelectedKeyLength()); + choices.add(new Choice(Curve.NIST_P256, getResources().getString( + R.string.key_curve_nist_p256))); + choices.add(new Choice(Curve.NIST_P384, getResources().getString( + R.string.key_curve_nist_p384))); + choices.add(new Choice(Curve.NIST_P521, getResources().getString( + R.string.key_curve_nist_p521))); - int flags = 0; - if (mFlagCertify.isChecked()) { - flags |= KeyFlags.CERTIFY_OTHER; - } - if (mFlagSign.isChecked()) { - flags |= KeyFlags.SIGN_DATA; - } - if (mFlagEncrypt.isChecked()) { - flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - if (mFlagAuthenticate.isChecked()) { - flags |= KeyFlags.AUTHENTICATION; - } + /* @see SaveKeyringParcel + choices.add(new Choice(Curve.BRAINPOOL_P256, getResources().getString( + R.string.key_curve_bp_p256))); + choices.add(new Choice(Curve.BRAINPOOL_P384, getResources().getString( + R.string.key_curve_bp_p384))); + choices.add(new Choice(Curve.BRAINPOOL_P512, getResources().getString( + R.string.key_curve_bp_p512))); + */ - Long expiry; - if (mNoExpiryCheckBox.isChecked()) { - expiry = null; - } else { - Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - //noinspection ResourceType - selectedCal.set(mExpiryDatePicker.getYear(), - mExpiryDatePicker.getMonth(), mExpiryDatePicker.getDayOfMonth()); - expiry = selectedCal.getTime().getTime() / 1000; - } - - SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd( - newKeyAlgorithmChoice.getId(), - newKeySize, - flags, - expiry - ); - mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey); - } + ArrayAdapter> adapter = new ArrayAdapter>(context, + android.R.layout.simple_spinner_item, choices); + mCurveSpinner.setAdapter(adapter); + // make NIST P-256 the default + for (int i = 0; i < choices.size(); ++i) { + if (choices.get(i).getId() == Curve.NIST_P256) { + mCurveSpinner.setSelection(i); + break; } - ); + } + } dialog.setCancelable(true); - dialog.setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface di, int id) { - di.dismiss(); - } - } - ); + + // onClickListener are set in onStart() to override default dismiss behaviour + dialog.setPositiveButton(android.R.string.ok, null); + dialog.setNegativeButton(android.R.string.cancel, null); final AlertDialog alertDialog = dialog.show(); @@ -246,7 +247,7 @@ public class AddSubkeyDialogFragment extends DialogFragment { mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - setKeyLengthSpinnerValuesForAlgorithm(((Choice) parent.getSelectedItem()).getId()); + updateUiForAlgorithm(((Choice) parent.getSelectedItem()).getId()); setCustomKeyVisibility(); setOkButtonAvailability(alertDialog); @@ -260,6 +261,79 @@ public class AddSubkeyDialogFragment extends DialogFragment { return alertDialog; } + @Override + public void onStart() { + super.onStart(); //super.onStart() is where dialog.show() is actually called on the underlying dialog, so we have to do it after this point + AlertDialog d = (AlertDialog) getDialog(); + if (d != null) { + Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE); + Button negativeButton = d.getButton(Dialog.BUTTON_NEGATIVE); + positiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!mFlagCertify.isChecked() && !mFlagSign.isChecked() + && !mFlagEncrypt.isChecked() && !mFlagAuthenticate.isChecked()) { + Toast.makeText(getActivity(), R.string.edit_key_select_flag, Toast.LENGTH_LONG).show(); + return; + } + + Algorithm algorithm = ((Choice) mAlgorithmSpinner.getSelectedItem()).getId(); + Curve curve = null; + Integer keySize = null; + // For EC keys, add a curve + if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) { + curve = ((Choice) mCurveSpinner.getSelectedItem()).getId(); + // Otherwise, get a keysize + } else { + keySize = getProperKeyLength(algorithm, getSelectedKeyLength()); + } + + int flags = 0; + if (mFlagCertify.isChecked()) { + flags |= KeyFlags.CERTIFY_OTHER; + } + if (mFlagSign.isChecked()) { + flags |= KeyFlags.SIGN_DATA; + } + if (mFlagEncrypt.isChecked()) { + flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + } + if (mFlagAuthenticate.isChecked()) { + flags |= KeyFlags.AUTHENTICATION; + } + + long expiry; + if (mNoExpiryCheckBox.isChecked()) { + expiry = 0L; + } else { + Calendar selectedCal = Calendar.getInstance(TimeZone.getDefault()); + //noinspection ResourceType + selectedCal.set(mExpiryDatePicker.getYear(), + mExpiryDatePicker.getMonth(), mExpiryDatePicker.getDayOfMonth()); + // date picker uses default time zone, we need to convert to UTC + selectedCal.setTimeZone(TimeZone.getTimeZone("UTC")); + + expiry = selectedCal.getTime().getTime() / 1000; + } + + SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd( + algorithm, keySize, curve, flags, expiry + ); + mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey); + + // finally, dismiss the dialogue + dismiss(); + } + }); + negativeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + } + } + private int getSelectedKeyLength() { final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem(); final String customLengthString = getResources().getString(R.string.key_size_custom); @@ -290,16 +364,16 @@ public class AddSubkeyDialogFragment extends DialogFragment { * @return correct key length, according to SpongyCastle specification. Returns -1, if key length is * inappropriate. */ - private int getProperKeyLength(int algorithmId, int currentKeyLength) { + private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) { final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192}; int properKeyLength = -1; - switch (algorithmId) { - case PublicKeyAlgorithmTags.RSA_GENERAL: + switch (algorithm) { + case RSA: if (currentKeyLength > 1024 && currentKeyLength <= 16384) { properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8); } break; - case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case ELGAMAL: int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length]; for (int i = 0; i < elGamalSupportedLengths.length; i++) { elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength); @@ -314,7 +388,7 @@ public class AddSubkeyDialogFragment extends DialogFragment { } properKeyLength = elGamalSupportedLengths[minimalIndex]; break; - case PublicKeyAlgorithmTags.DSA: + case DSA: if (currentKeyLength >= 512 && currentKeyLength <= 1024) { properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64); } @@ -324,10 +398,10 @@ public class AddSubkeyDialogFragment extends DialogFragment { } private void setOkButtonAvailability(AlertDialog alertDialog) { - final Choice selectedAlgorithm = (Choice) mAlgorithmSpinner.getSelectedItem(); - final int selectedKeySize = getSelectedKeyLength(); //Integer.parseInt((String) mKeySizeSpinner.getSelectedItem()); - final int properKeyLength = getProperKeyLength(selectedAlgorithm.getId(), selectedKeySize); - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(properKeyLength > 0); + Algorithm algorithm = ((Choice) mAlgorithmSpinner.getSelectedItem()).getId(); + boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH + || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0; + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled); } private void setCustomKeyVisibility() { @@ -343,38 +417,104 @@ public class AddSubkeyDialogFragment extends DialogFragment { // hide keyboard after setting visibility to gone if (visibility == View.GONE) { InputMethodManager imm = (InputMethodManager) - getActivity().getSystemService(getActivity().INPUT_METHOD_SERVICE); + getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0); } } - private void setKeyLengthSpinnerValuesForAlgorithm(int algorithmId) { + private void updateUiForAlgorithm(Algorithm algorithm) { final ArrayAdapter keySizeAdapter = (ArrayAdapter) mKeySizeSpinner.getAdapter(); - final Object selectedItem = mKeySizeSpinner.getSelectedItem(); keySizeAdapter.clear(); - switch (algorithmId) { - case PublicKeyAlgorithmTags.RSA_GENERAL: + switch (algorithm) { + case RSA: replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values); + mKeySizeSpinner.setSelection(1); + mKeySizeRow.setVisibility(View.VISIBLE); + mCurveRow.setVisibility(View.GONE); mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa)); + // allowed flags: + mFlagSign.setEnabled(true); + mFlagEncrypt.setEnabled(true); + mFlagAuthenticate.setEnabled(true); + + if (mWillBeMasterKey) { + mFlagCertify.setEnabled(true); + + mFlagCertify.setChecked(true); + mFlagSign.setChecked(false); + mFlagEncrypt.setChecked(false); + } else { + mFlagCertify.setEnabled(false); + + mFlagCertify.setChecked(false); + mFlagSign.setChecked(true); + mFlagEncrypt.setChecked(true); + } + mFlagAuthenticate.setChecked(false); break; - case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case ELGAMAL: replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values); + mKeySizeSpinner.setSelection(3); + mKeySizeRow.setVisibility(View.VISIBLE); + mCurveRow.setVisibility(View.GONE); mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length + // allowed flags: + mFlagCertify.setChecked(false); + mFlagCertify.setEnabled(false); + mFlagSign.setChecked(false); + mFlagSign.setEnabled(false); + mFlagEncrypt.setChecked(true); + mFlagEncrypt.setEnabled(true); + mFlagAuthenticate.setChecked(false); + mFlagAuthenticate.setEnabled(false); break; - case PublicKeyAlgorithmTags.DSA: + case DSA: replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values); + mKeySizeSpinner.setSelection(2); + mKeySizeRow.setVisibility(View.VISIBLE); + mCurveRow.setVisibility(View.GONE); mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa)); + // allowed flags: + mFlagCertify.setChecked(false); + mFlagCertify.setEnabled(false); + mFlagSign.setChecked(true); + mFlagSign.setEnabled(true); + mFlagEncrypt.setChecked(false); + mFlagEncrypt.setEnabled(false); + mFlagAuthenticate.setChecked(false); + mFlagAuthenticate.setEnabled(false); + break; + case ECDSA: + mKeySizeRow.setVisibility(View.GONE); + mCurveRow.setVisibility(View.VISIBLE); + mCustomKeyInfoTextView.setText(""); + // allowed flags: + mFlagCertify.setEnabled(mWillBeMasterKey); + mFlagCertify.setChecked(mWillBeMasterKey); + mFlagSign.setEnabled(true); + mFlagSign.setChecked(!mWillBeMasterKey); + mFlagEncrypt.setEnabled(false); + mFlagEncrypt.setChecked(false); + mFlagAuthenticate.setEnabled(true); + mFlagAuthenticate.setChecked(false); + break; + case ECDH: + mKeySizeRow.setVisibility(View.GONE); + mCurveRow.setVisibility(View.VISIBLE); + mCustomKeyInfoTextView.setText(""); + // allowed flags: + mFlagCertify.setChecked(false); + mFlagCertify.setEnabled(false); + mFlagSign.setChecked(false); + mFlagSign.setEnabled(false); + mFlagEncrypt.setChecked(true); + mFlagEncrypt.setEnabled(true); + mFlagAuthenticate.setChecked(false); + mFlagAuthenticate.setEnabled(false); break; } keySizeAdapter.notifyDataSetChanged(); - // when switching algorithm, try to select same key length as before - for (int i = 0; i < keySizeAdapter.getCount(); i++) { - if (selectedItem.equals(keySizeAdapter.getItem(i))) { - mKeySizeSpinner.setSelection(i); - break; - } - } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 4927a4d24..d0c9cea5b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -18,7 +18,9 @@ package org.sufficientlysecure.keychain.ui.dialog; import android.app.Dialog; +import android.app.ProgressDialog; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.os.Message; import android.os.Messenger; @@ -31,9 +33,10 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.util.Log; import java.util.HashMap; @@ -48,8 +51,6 @@ public class DeleteKeyDialogFragment extends DialogFragment { private TextView mMainMessage; private View mInflateView; - private Messenger mMessenger; - /** * Creates new instance of this delete file dialog fragment */ @@ -68,7 +69,7 @@ public class DeleteKeyDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final FragmentActivity activity = getActivity(); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); + final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER); final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS); @@ -83,6 +84,8 @@ public class DeleteKeyDialogFragment extends DialogFragment { builder.setTitle(R.string.warning); + final boolean hasSecret; + // If only a single key has been selected if (masterKeyIds.length == 1) { long masterKeyId = masterKeyIds[0]; @@ -98,7 +101,7 @@ public class DeleteKeyDialogFragment extends DialogFragment { } ); String userId = (String) data.get(KeyRings.USER_ID); - boolean hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1; + hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1; // Set message depending on which key it is. mMainMessage.setText(getString( @@ -107,12 +110,12 @@ public class DeleteKeyDialogFragment extends DialogFragment { userId )); } catch (ProviderHelper.NotFoundException e) { - sendMessageToHandler(MESSAGE_ERROR, null); dismiss(); return null; } } else { mMainMessage.setText(R.string.key_deletion_confirmation_multi); + hasSecret = false; } builder.setIcon(R.drawable.ic_dialog_alert_holo_light); @@ -120,18 +123,45 @@ public class DeleteKeyDialogFragment extends DialogFragment { @Override public void onClick(DialogInterface dialog, int which) { - boolean success = false; - for (long masterKeyId : masterKeyIds) { - int count = activity.getContentResolver().delete( - KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null - ); - success = count > 0; - } - if (success) { - sendMessageToHandler(MESSAGE_OKAY, null); - } else { - sendMessageToHandler(MESSAGE_ERROR, null); - } + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + + intent.setAction(KeychainIntentService.ACTION_DELETE); + + // Message is received after importing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_deleting), + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + try { + Message msg = Message.obtain(); + msg.copyFrom(message); + messenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "messenger error", e); + } + } + }; + + // fill values for this action + Bundle data = new Bundle(); + data.putLongArray(KeychainIntentService.DELETE_KEY_LIST, masterKeyIds); + data.putBoolean(KeychainIntentService.DELETE_IS_SECRET, hasSecret); + 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); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + dismiss(); } }); @@ -146,23 +176,4 @@ public class DeleteKeyDialogFragment extends DialogFragment { 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/EditSubkeyExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java index aa63f9944..f46e253c0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java @@ -25,9 +25,10 @@ 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.view.LayoutInflater; import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.DatePicker; import org.sufficientlysecure.keychain.Constants; @@ -40,17 +41,14 @@ import java.util.TimeZone; public class EditSubkeyExpiryDialogFragment 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"; + private static final String ARG_CREATION = "creation"; + private static final String ARG_EXPIRY = "expiry"; - public static final int MESSAGE_NEW_EXPIRY_DATE = 1; + public static final int MESSAGE_NEW_EXPIRY = 1; public static final int MESSAGE_CANCEL = 2; - public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date"; + public static final String MESSAGE_DATA_EXPIRY = "expiry"; private Messenger mMessenger; - private Calendar mExpiryCal; - - private DatePicker mDatePicker; /** * Creates new instance of this dialog fragment @@ -60,8 +58,8 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment { EditSubkeyExpiryDialogFragment frag = new EditSubkeyExpiryDialogFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_MESSENGER, messenger); - args.putSerializable(ARG_CREATION_DATE, creationDate); - args.putSerializable(ARG_EXPIRY_DATE, expiryDate); + args.putSerializable(ARG_CREATION, creationDate); + args.putSerializable(ARG_EXPIRY, expiryDate); frag.setArguments(args); @@ -75,15 +73,17 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); mMessenger = getArguments().getParcelable(ARG_MESSENGER); - Date creationDate = new Date(getArguments().getLong(ARG_CREATION_DATE) * 1000); - Date expiryDate = new Date(getArguments().getLong(ARG_EXPIRY_DATE) * 1000); + long creation = getArguments().getLong(ARG_CREATION); + long expiry = getArguments().getLong(ARG_EXPIRY); Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationCal.setTime(creationDate); - mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - mExpiryCal.setTime(expiryDate); + creationCal.setTime(new Date(creation * 1000)); + final Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + expiryCal.setTime(new Date(expiry * 1000)); - Log.d(Constants.TAG, "onCreateDialog"); + // date picker works with default time zone, we need to convert from UTC to default timezone + creationCal.setTimeZone(TimeZone.getDefault()); + expiryCal.setTimeZone(TimeZone.getDefault()); // Explicitly not using DatePickerDialog here! // DatePickerDialog is difficult to customize and has many problems (see old git versions) @@ -95,19 +95,64 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment { View view = inflater.inflate(R.layout.edit_subkey_expiry_dialog, null); alert.setView(view); - mDatePicker = (DatePicker) view.findViewById(R.id.edit_subkey_expiry_date_picker); + final CheckBox noExpiry = (CheckBox) view.findViewById(R.id.edit_subkey_expiry_no_expiry); + final DatePicker datePicker = (DatePicker) view.findViewById(R.id.edit_subkey_expiry_date_picker); + + noExpiry.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + datePicker.setVisibility(View.GONE); + } else { + datePicker.setVisibility(View.VISIBLE); + } + } + }); + + // init date picker with default selected date + if (expiry == 0L) { + noExpiry.setChecked(true); + datePicker.setVisibility(View.GONE); + + Calendar todayCal = Calendar.getInstance(TimeZone.getDefault()); + if (creationCal.after(todayCal)) { + // Note: This is just for the rare cases where creation is _after_ today + + // set it to creation date +1 day (don't set it to creationCal, it would break crash + // datePicker.setMinDate() execution with IllegalArgumentException + Calendar creationCalPlusOne = (Calendar) creationCal.clone(); + creationCalPlusOne.add(Calendar.DAY_OF_YEAR, 1); + datePicker.init( + creationCalPlusOne.get(Calendar.YEAR), + creationCalPlusOne.get(Calendar.MONTH), + creationCalPlusOne.get(Calendar.DAY_OF_MONTH), + null + ); + + } else { + // normally, just init with today + datePicker.init( + todayCal.get(Calendar.YEAR), + todayCal.get(Calendar.MONTH), + todayCal.get(Calendar.DAY_OF_MONTH), + null + ); + } + } else { + noExpiry.setChecked(false); + datePicker.setVisibility(View.VISIBLE); + + // set date picker to current expiry + datePicker.init( + expiryCal.get(Calendar.YEAR), + expiryCal.get(Calendar.MONTH), + expiryCal.get(Calendar.DAY_OF_MONTH), + null + ); + } 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 (creationCal.before(mExpiryCal)) { - mDatePicker.setMinDate(creationCal.getTime().getTime() - + DateUtils.DAY_IN_MILLIS); - } else { - // when creation date isn't available - mDatePicker.setMinDate(mExpiryCal.getTime().getTime() - + DateUtils.DAY_IN_MILLIS); - } + datePicker.setMinDate(creationCal.getTime().getTime()); } alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -115,34 +160,28 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment { public void onClick(DialogInterface dialog, int id) { dismiss(); - Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - //noinspection ResourceType - selectedCal.set(mDatePicker.getYear(), mDatePicker.getMonth(), mDatePicker.getDayOfMonth()); - - 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().getTime() / 1000); - sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data); - } + long expiry; + if (noExpiry.isChecked()) { + expiry = 0L; } else { - Bundle data = new Bundle(); - data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime().getTime() / 1000); - sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data); - } - } - }); + Calendar selectedCal = Calendar.getInstance(TimeZone.getDefault()); + //noinspection ResourceType + selectedCal.set(datePicker.getYear(), datePicker.getMonth(), datePicker.getDayOfMonth()); + // date picker uses default time zone, we need to convert to UTC + selectedCal.setTimeZone(TimeZone.getTimeZone("UTC")); - alert.setNeutralButton(R.string.btn_no_date, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); + long numDays = (selectedCal.getTimeInMillis() / 86400000) + - (expiryCal.getTimeInMillis() / 86400000); + if (numDays <= 0) { + Log.e(Constants.TAG, "Should not happen! Expiry num of days <= 0!"); + throw new RuntimeException(); + } + expiry = selectedCal.getTime().getTime() / 1000; + } Bundle data = new Bundle(); - data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null); - sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data); + data.putSerializable(MESSAGE_DATA_EXPIRY, expiry); + sendMessageToHandler(MESSAGE_NEW_EXPIRY, data); } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java index 093a04aff..0324dd3b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java @@ -25,6 +25,7 @@ import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnKeyListener; import android.os.Bundle; import android.support.v4.app.DialogFragment; +import android.view.ContextThemeWrapper; import android.view.KeyEvent; import org.sufficientlysecure.keychain.R; @@ -113,7 +114,12 @@ public class ProgressDialogFragment extends DialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); - ProgressDialog dialog = new ProgressDialog(activity); + // if the progress dialog is displayed from the application class, design is missing + // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay + ContextThemeWrapper context = new ContextThemeWrapper(activity, + R.style.Theme_AppCompat_Light); + + ProgressDialog dialog = new ProgressDialog(context); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java new file mode 100644 index 000000000..e3c9804bb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.AttributeSet; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; + +public class CertifyKeySpinner extends KeySpinner { + private long mHiddenMasterKeyId = Constants.key.none; + + public CertifyKeySpinner(Context context) { + super(context); + } + + public CertifyKeySpinner(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CertifyKeySpinner(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setHiddenMasterKeyId(long hiddenMasterKeyId) { + this.mHiddenMasterKeyId = hiddenMasterKeyId; + reload(); + } + + @Override + public Loader onCreateLoader() { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); + + // These are the rows that we will retrieve. + String[] projection = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_EXPIRED, + KeychainContract.KeyRings.HAS_CERTIFY, + KeychainContract.KeyRings.HAS_ANY_SECRET + }; + + String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + + KeychainContract.KeyRings.HAS_CERTIFY + " NOT NULL AND " + + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + + KeychainContract.KeyRings.IS_EXPIRED + " = 0 AND " + KeychainDatabase.Tables.KEYS + "." + + KeychainContract.KeyRings.MASTER_KEY_ID + " != " + mHiddenMasterKeyId; + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getContext(), baseUri, projection, where, null, null); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index 20b9570bb..ceb3f665f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -111,7 +111,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { protected void onAttachedToWindow() { super.onAttachedToWindow(); if (getContext() instanceof FragmentActivity) { - ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks() { + ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { @Override public Loader onCreateLoader(int id, Bundle args) { // These are the rows that we will retrieve. @@ -143,6 +143,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { swapCursor(null); } }); + } else { + Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java index 31e01a7fb..b456b61ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java @@ -24,7 +24,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; -import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -38,9 +38,7 @@ import org.sufficientlysecure.keychain.R; android:layout_width="wrap_content" android:layout_height="wrap_content" custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED" - custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED" - custom:foldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_FOLDED" - custom:unFoldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_UNFOLDED"> + custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"> @@ -49,7 +47,7 @@ import org.sufficientlysecure.keychain.R; */ public class FoldableLinearLayout extends LinearLayout { - private ImageButton mFoldableIcon; + private ImageView mFoldableIcon; private boolean mFolded; private boolean mHasMigrated = false; private Integer mShortAnimationDuration = null; @@ -139,7 +137,7 @@ public class FoldableLinearLayout extends LinearLayout { } private void initialiseInnerViews() { - mFoldableIcon = (ImageButton) mFoldableLayout.findViewById(R.id.foldableIcon); + mFoldableIcon = (ImageView) mFoldableLayout.findViewById(R.id.foldableIcon); mFoldableIcon.setImageResource(R.drawable.ic_action_expand); mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText); mFoldableTextView.setText(mFoldedLabel); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java new file mode 100644 index 000000000..3a31fc9f4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; +import android.support.v4.widget.CursorAdapter; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.util.Log; + +public abstract class KeySpinner extends Spinner { + public interface OnKeyChangedListener { + public void onKeyChanged(long masterKeyId); + } + + private long mSelectedKeyId; + private SelectKeyAdapter mAdapter = new SelectKeyAdapter(); + private OnKeyChangedListener mListener; + + public KeySpinner(Context context) { + super(context); + initView(); + } + + public KeySpinner(Context context, AttributeSet attrs) { + super(context, attrs); + initView(); + } + + public KeySpinner(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initView(); + } + + private void initView() { + setAdapter(mAdapter); + super.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (mListener != null) { + mListener.onKeyChanged(id); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + if (mListener != null) { + mListener.onKeyChanged(Constants.key.none); + } + } + }); + } + + public abstract Loader onCreateLoader(); + + @Override + public void setOnItemSelectedListener(OnItemSelectedListener listener) { + throw new UnsupportedOperationException(); + } + + public void setOnKeyChangedListener(OnKeyChangedListener listener) { + mListener = listener; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + reload(); + } + + public void reload() { + if (getContext() instanceof FragmentActivity) { + ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return KeySpinner.this.onCreateLoader(); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + mAdapter.swapCursor(data); + } + + @Override + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } + }); + } else { + Log.e(Constants.TAG, "KeySpinner must be attached to FragmentActivity, this is " + getContext().getClass()); + } + } + + public long getSelectedKeyId() { + return mSelectedKeyId; + } + + public void setSelectedKeyId(long selectedKeyId) { + this.mSelectedKeyId = selectedKeyId; + } + + private class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter { + private CursorAdapter inner; + private int mIndexUserId; + private int mIndexKeyId; + private int mIndexMasterKeyId; + + public SelectKeyAdapter() { + inner = new CursorAdapter(null, null, 0) { + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return View.inflate(getContext(), R.layout.keyspinner_key, null); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId)); + ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")")); + ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]); + ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId))); + } + + @Override + public long getItemId(int position) { + try { + return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId); + } catch (Exception e) { + // This can happen on concurrent modification :( + return Constants.key.none; + } + } + }; + } + + public Cursor swapCursor(Cursor newCursor) { + if (newCursor == null) return inner.swapCursor(null); + + mIndexKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.KEY_ID); + mIndexUserId = newCursor.getColumnIndex(KeychainContract.KeyRings.USER_ID); + mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID); + if (newCursor.moveToFirst()) { + do { + if (newCursor.getLong(mIndexMasterKeyId) == mSelectedKeyId) { + setSelection(newCursor.getPosition() + 1); + } + } while (newCursor.moveToNext()); + } + return inner.swapCursor(newCursor); + } + + @Override + public int getCount() { + return inner.getCount() + 1; + } + + @Override + public Object getItem(int position) { + if (position == 0) return null; + return inner.getItem(position - 1); + } + + @Override + public long getItemId(int position) { + if (position == 0) return Constants.key.none; + return inner.getItemId(position - 1); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + try { + View v = getDropDownView(position, convertView, parent); + v.findViewById(android.R.id.text1).setVisibility(View.GONE); + return v; + } catch (NullPointerException e) { + // This is for the preview... + return View.inflate(getContext(), android.R.layout.simple_list_item_1, null); + } + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + View v; + if (position == 0) { + if (convertView == null) { + v = inner.newView(null, null, parent); + } else { + v = convertView; + } + ((TextView) v.findViewById(android.R.id.title)).setText("None"); + v.findViewById(android.R.id.text1).setVisibility(View.GONE); + v.findViewById(android.R.id.text2).setVisibility(View.GONE); + } else { + v = inner.getView(position - 1, convertView, parent); + v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE); + v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE); + } + return v; + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java deleted file mode 100644 index a48d2a026..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -public class NoSwipeWrapContentViewPager extends android.support.v4.view.ViewPager { - public NoSwipeWrapContentViewPager(Context context) { - super(context); - } - - public NoSwipeWrapContentViewPager(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - - int height; - View child = getChildAt(getCurrentItem()); - if (child != null) { - child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - height = child.getMeasuredHeight(); - } else { - height = 0; - } - - heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent arg0) { - // Never allow swiping to switch between pages - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // Never allow swiping to switch between pages - return false; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java new file mode 100644 index 000000000..a1ed2c065 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.AttributeSet; +import org.sufficientlysecure.keychain.provider.KeychainContract; + +public class SignKeySpinner extends KeySpinner { + public SignKeySpinner(Context context) { + super(context); + } + + public SignKeySpinner(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public SignKeySpinner(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public Loader onCreateLoader() { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); + + // These are the rows that we will retrieve. + String[] projection = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_EXPIRED, + KeychainContract.KeyRings.HAS_SIGN, + KeychainContract.KeyRings.HAS_ANY_SECRET + }; + + String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeychainContract.KeyRings.HAS_SIGN + " NOT NULL AND " + + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0"; + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getContext(), baseUri, projection, where, null, null); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java index 9acc7a73b..99db634ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java @@ -50,7 +50,6 @@ public class AlgorithmNames { mEncryptionNames.put(PGPEncryptedData.TRIPLE_DES, "Triple DES"); mEncryptionNames.put(PGPEncryptedData.IDEA, "IDEA"); - mHashNames.put(HashAlgorithmTags.MD5, "MD5"); mHashNames.put(HashAlgorithmTags.RIPEMD160, "RIPEMD-160"); mHashNames.put(HashAlgorithmTags.SHA1, "SHA-1"); mHashNames.put(HashAlgorithmTags.SHA224, "SHA-224"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java index 70c7d80fe..48f10d4b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java @@ -17,21 +17,16 @@ package org.sufficientlysecure.keychain.util; -public class Choice { +public class Choice { private String mName; - private int mId; + private E mId; - public Choice() { - mId = -1; - mName = ""; - } - - public Choice(int id, String name) { + public Choice(E id, String name) { mId = id; mName = name; } - public int getId() { + public E getId() { return mId; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java index 5a4bf5311..09275fc95 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java @@ -46,10 +46,11 @@ public class FileImportCache { private Context mContext; - private static final String FILENAME = "key_import.pcl"; + private final String mFilename; - public FileImportCache(Context context) { - this.mContext = context; + public FileImportCache(Context context, String filename) { + mContext = context; + mFilename = filename; } public void writeCache(ArrayList selectedEntries) throws IOException { @@ -64,7 +65,7 @@ public class FileImportCache { throw new IOException("cache dir is null!"); } - File tempFile = new File(mContext.getCacheDir(), FILENAME); + File tempFile = new File(mContext.getCacheDir(), mFilename); DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile)); @@ -91,6 +92,10 @@ public class FileImportCache { } public Iterator readCache() throws IOException { + return readCache(true); + } + + public Iterator readCache(final boolean deleteAfterRead) throws IOException { File cacheDir = mContext.getCacheDir(); if (cacheDir == null) { @@ -98,7 +103,7 @@ public class FileImportCache { throw new IOException("cache dir is null!"); } - final File tempFile = new File(cacheDir, FILENAME); + final File tempFile = new File(cacheDir, mFilename); final DataInputStream ois = new DataInputStream(new FileInputStream(tempFile)); return new Iterator() { @@ -165,7 +170,10 @@ public class FileImportCache { if (!closed) { try { ois.close(); - tempFile.delete(); + if (deleteAfterRead) { + //noinspection ResultOfMethodCallIgnored + tempFile.delete(); + } } catch (IOException e) { // nvm } @@ -176,4 +184,17 @@ public class FileImportCache { }; } + + public boolean delete() throws IOException { + + File cacheDir = mContext.getCacheDir(); + if (cacheDir == null) { + // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU + throw new IOException("cache dir is null!"); + } + + final File tempFile = new File(cacheDir, mFilename); + return tempFile.delete(); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java new file mode 100644 index 000000000..4bb4ca5de --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java @@ -0,0 +1,29 @@ +package org.sufficientlysecure.keychain.util; + +import org.sufficientlysecure.keychain.pgp.Progressable; + +/** This is a simple variant of ProgressScaler which shows a fixed progress message, ignoring + * the provided ones. + */ +public class ProgressFixedScaler extends ProgressScaler { + + final int mResId; + + public ProgressFixedScaler(Progressable wrapped, int from, int to, int max, int resId) { + super(wrapped, from, to, max); + mResId = resId; + } + + public void setProgress(int resourceId, int progress, int max) { + if (mWrapped != null) { + mWrapped.setProgress(mResId, mFrom + progress * (mTo - mFrom) / max, mMax); + } + } + + public void setProgress(String message, int progress, int max) { + if (mWrapped != null) { + mWrapped.setProgress(mResId, mFrom + progress * (mTo - mFrom) / max, mMax); + } + } + +} diff --git a/OpenKeychain/src/main/res/layout-large/api_apps_list_activity.xml b/OpenKeychain/src/main/res/layout-large/api_apps_list_activity.xml index 88686bbbc..af06614b9 100644 --- a/OpenKeychain/src/main/res/layout-large/api_apps_list_activity.xml +++ b/OpenKeychain/src/main/res/layout-large/api_apps_list_activity.xml @@ -1,20 +1,18 @@ - + + - android:layout_width="match_parent" - android:layout_height="match_parent"> - - - + - + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml b/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml index 9e9f37391..06487a982 100644 --- a/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml +++ b/OpenKeychain/src/main/res/layout-large/decrypt_activity.xml @@ -1,19 +1,18 @@ - + + - - + - + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout-large/encrypt_activity.xml b/OpenKeychain/src/main/res/layout-large/encrypt_activity.xml index 355580fad..84b2891e3 100644 --- a/OpenKeychain/src/main/res/layout-large/encrypt_activity.xml +++ b/OpenKeychain/src/main/res/layout-large/encrypt_activity.xml @@ -1,19 +1,17 @@ - + + - + - + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout-large/key_list_activity.xml b/OpenKeychain/src/main/res/layout-large/key_list_activity.xml index 6abbea13f..039081cd7 100644 --- a/OpenKeychain/src/main/res/layout-large/key_list_activity.xml +++ b/OpenKeychain/src/main/res/layout-large/key_list_activity.xml @@ -1,18 +1,18 @@ - + + - + - + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml index e1ccfee1f..7af73be09 100644 --- a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml +++ b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml @@ -34,7 +34,7 @@ android:padding="4dp" /> - + + + + + + + + + custom:unFoldedLabel="@string/api_settings_hide_advanced"> + custom:unFoldedLabel="@string/api_settings_hide_info"> diff --git a/OpenKeychain/src/main/res/layout/api_remote_select_pub_keys.xml b/OpenKeychain/src/main/res/layout/api_remote_select_pub_keys.xml index a10592607..bf4d0a70d 100644 --- a/OpenKeychain/src/main/res/layout/api_remote_select_pub_keys.xml +++ b/OpenKeychain/src/main/res/layout/api_remote_select_pub_keys.xml @@ -4,13 +4,13 @@ android:layout_height="fill_parent" android:orientation="vertical" > - @@ -8,6 +7,7 @@ @@ -26,14 +26,10 @@ android:layout_marginTop="14dp" android:text="@string/section_certification_key" /> - + android:layout_height="wrap_content" /> + android:layout_height="wrap_content" /> - diff --git a/OpenKeychain/src/main/res/layout/edit_subkey_expiry_dialog.xml b/OpenKeychain/src/main/res/layout/edit_subkey_expiry_dialog.xml index 062f07863..8a713115e 100644 --- a/OpenKeychain/src/main/res/layout/edit_subkey_expiry_dialog.xml +++ b/OpenKeychain/src/main/res/layout/edit_subkey_expiry_dialog.xml @@ -4,6 +4,16 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + - - - - + android:layout_height="wrap_content" + android:orientation="vertical" /> - + android:src="@drawable/ic_action_expand"/> - - - - - - - - + diff --git a/OpenKeychain/src/main/res/layout/key_list_content.xml b/OpenKeychain/src/main/res/layout/key_list_content.xml index e58e42961..66b009c78 100644 --- a/OpenKeychain/src/main/res/layout/key_list_content.xml +++ b/OpenKeychain/src/main/res/layout/key_list_content.xml @@ -1,14 +1,22 @@ - + + + + - - \ No newline at end of file + android:layout_height="match_parent" /> + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/encrypt_asymmetric_signkey.xml b/OpenKeychain/src/main/res/layout/keyspinner_key.xml similarity index 100% rename from OpenKeychain/src/main/res/layout/encrypt_asymmetric_signkey.xml rename to OpenKeychain/src/main/res/layout/keyspinner_key.xml diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml index b15a73c0e..451eb30ee 100644 --- a/OpenKeychain/src/main/res/layout/view_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml @@ -4,7 +4,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - + + android:layout_height="wrap_content" /> + android:layout_marginLeft="8dp" /> diff --git a/OpenKeychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_certs_fragment.xml index 00b3f85c5..1cf826b30 100644 --- a/OpenKeychain/src/main/res/layout/view_key_certs_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_certs_fragment.xml @@ -1,24 +1,47 @@ - - - + android:layout_height="match_parent" + android:orientation="vertical"> + android:background="@color/holo_gray_bright" + android:padding="8dp" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@string/certs_text" + android:gravity="center_horizontal" /> + + + + + + + + + + + + - diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml index 056dd5986..6e571243d 100644 --- a/OpenKeychain/src/main/res/menu/key_list.xml +++ b/OpenKeychain/src/main/res/menu/key_list.xml @@ -31,6 +31,12 @@ app:showAsAction="never" android:title="@string/menu_import_existing_key" /> + +

http://www.openkeychain.org

OpenKeychain is an OpenPGP implementation for Android.

-

License: GPLv3+

+

Licence: GPLv3+

-

Developers

+

Vývojáři

    -
  • Dominik Schürmann (Maintainer)
  • +
  • Dominik Schürmann (Hlavní vývojář)
  • Art O Cathain
  • Ash Hughes
  • Brian C. Barnes
  • @@ -27,7 +27,7 @@
  • Tim Bray
  • Vincent Breitmoser
-

Libraries

+

Knihovny

  • Android Support Library v4 (Apache License v2)
  • diff --git a/OpenKeychain/src/main/res/raw-cs/help_changelog.html b/OpenKeychain/src/main/res/raw-cs/help_changelog.html index e27ac7475..41ec2cad6 100644 --- a/OpenKeychain/src/main/res/raw-cs/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-cs/help_changelog.html @@ -7,11 +7,12 @@
  • Key edit: awesome new design, key revocation
  • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
  • New first time screen
  • -
  • New create key screen: autocompletion of name and email based on your personal Android accounts
  • +
  • New key creation screen: autocompletion of name and email based on your personal Android accounts
  • File encryption: awesome new design, support for encrypting multiple files
  • New icons to show status of key (by Brennan Novak)
  • Important bug fix: Importing of large key collections from a file is now possible
  • Notification showing cached passphrases
  • +
  • Keys are connected to Android's contacts

This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

@@ -38,7 +39,7 @@

2.5

  • Fix decryption of symmetric pgp messages/files
  • -
  • Refactored edit key screen (thanks to Ash Hughes)
  • +
  • Refactored key edit screen (thanks to Ash Hughes)
  • New modern design for encrypt/decrypt screens
  • OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
@@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
  • Basic keyserver support
  • App2sd
  • -
  • More choices for pass phrase cache: 1, 2, 4, 8, hours
  • +
  • More choices for passphrase cache: 1, 2, 4, 8, hours
  • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
  • Bugfixes
  • Optimizations
  • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

    1.0.7

    • Fixed problem with signature verification of texts with trailing newline
    • -
    • More options for pass phrase cache time to live (20, 40, 60 mins)
    • +
    • More options for passphrase cache time to live (20, 40, 60 mins)

    1.0.6

      diff --git a/OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html index 88492731c..d691df871 100644 --- a/OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html +++ b/OpenKeychain/src/main/res/raw-cs/help_nfc_beam.html @@ -1,12 +1,12 @@ -

      How to receive keys

      +

      Jak přijmout klíče

        -
      1. Go to your partners contacts and open the contact you want to share.
      2. -
      3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
      4. -
      5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
      6. -
      7. Tap the card and the content will then load on the your device.
      8. +
      9. Navigujte na kontakt vašeho partnera a otevřete ten který chcete sdílet.
      10. +
      11. Podržte dvě zařízení zády k sobě (musí se téměř dotýkat) a ucítíte vibraci.
      12. +
      13. Po tom co ucítíte vibraci, uvidíte že se obsah na displeji zařízení vašeho partnera změní na objekt typu karta s animací na pozadí připomínající rychlost warpu ze Star Treku.
      14. +
      15. Tapněte na kartu a obsach se nahraje do zařízení.
      diff --git a/OpenKeychain/src/main/res/raw-cs/help_start.html b/OpenKeychain/src/main/res/raw-cs/help_start.html index 51a76c01e..eaffc2389 100644 --- a/OpenKeychain/src/main/res/raw-cs/help_start.html +++ b/OpenKeychain/src/main/res/raw-cs/help_start.html @@ -1,22 +1,22 @@ -

      Getting started

      -

      First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

      +

      Začínáme

      +

      Nejprve potřebujete osobní tajný klíč. Vytvořte si ho pomocí volby v menu v sekci "Klíče" nebo importujte existující tajné klíče. Následně můžete stáhnout klíče vašich známých nebo si je vyměnit pomocí QR kódu případně NFC.

      -

      It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

      +

      Doporučujeme instalaci OI File Manager pro zlepšení práce se soubory a Barcode Scanner pro skenování generovaných QR kódů. Kliknutí na odkazy otevře Google Play Store nebo F-Droid odkud můžete začít installovat.

      -

      Applications

      -

      Several applications support OpenKeychain to encrypt/sign your private communication:

      K-9 Mail: OpenKeychain support available in current alpha build!

      Conversations
      : Jabber/XMPP client

      PGPAuth
      : App to send a PGP-signed request to a server to open or close something, e.g. a door

      +

      Aplikace

      +

      Několik aplikací umožňuje šifrovat/podepsat privátní komunikaci pomocí OpenKeychain:

      K-9 Mail: podpora OpenKeychain v aktualní verzi alpha build!

      Konverzace
      : Jabber/XMPP client

      PGPAuth
      : Appka sloužící k odesílání požadavků podepsaných PGP na servery pro otevření nebo zavření něčeho, třeba dveří

      -

      I found a bug in OpenKeychain!

      -

      Please report the bug using the issue tracker of OpenKeychain.

      +

      Našel jsem bug v OpenChain!

      +

      Reportujte chyby pomocí issue trackeru OpenKeychain.

      -

      Contribute

      -

      If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

      +

      Přispět

      +

      Pokud chcete pomoci vyvýjet OpenKeychain tak, že přispějete svými kodérskými schopnostmi přečtěte si naší malou příručku na Githubu.

      -

      Translations

      -

      Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

      +

      Překlady

      +

      Pomozte s překladem OpenKeychain! Každý může participovat na OpenKeychain na Transifexu

      diff --git a/OpenKeychain/src/main/res/raw-cs/help_wot.html b/OpenKeychain/src/main/res/raw-cs/help_wot.html index 29790139b..30288f2bb 100644 --- a/OpenKeychain/src/main/res/raw-cs/help_wot.html +++ b/OpenKeychain/src/main/res/raw-cs/help_wot.html @@ -1,7 +1,7 @@ -

      Web of Trust

      +

      Síť důvěry

      The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.

      Support in OpenKeychain

      diff --git a/OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html index 083e055c7..09128be62 100644 --- a/OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html +++ b/OpenKeychain/src/main/res/raw-cs/nfc_beam_share.html @@ -2,10 +2,10 @@
        -
      1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
      2. -
      3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
      4. -
      5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
      6. -
      7. Tap the card and the content will then load on the other person’s device.
      8. +
      9. Ujistěte se, že NFC je zapnuto v Nastavení > Další > NFC a ujistěte se, že Android Beam ve stejné sekci je také zapnutý.
      10. +
      11. Podržte dvě zařízení zády k sobě (musí se téměř dotýkat) a ucítíte vibraci.
      12. +
      13. Po tom co ucítíte vibraci, uvidíte že se obsah na displeji zařízení vašeho partnera změní na objekt typu karta s animací na pozadí připomínající rychlost warpu ze Star Treku.
      14. +
      15. Tapněte na kartu a obsach se nahraje do zařízení.
      diff --git a/OpenKeychain/src/main/res/raw-de/help_about.html b/OpenKeychain/src/main/res/raw-de/help_about.html index f5622d54a..934caa832 100644 --- a/OpenKeychain/src/main/res/raw-de/help_about.html +++ b/OpenKeychain/src/main/res/raw-de/help_about.html @@ -5,7 +5,7 @@

      OpenKeychain ist eine OpenPGP-Implementierung für Android.

      Lizenz: GPLv3+

      -

      Developers

      +

      Engwickler

      • Dominik Schürmann (Maintainer)
      • Art O Cathain
      • diff --git a/OpenKeychain/src/main/res/raw-de/help_changelog.html b/OpenKeychain/src/main/res/raw-de/help_changelog.html index 674d9c93f..0a8f95e83 100644 --- a/OpenKeychain/src/main/res/raw-de/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-de/help_changelog.html @@ -3,17 +3,18 @@

        2.8

          -
        • So many bugs have been fixed in this release that we focus on the main new features
        • -
        • Key edit: awesome new design, key revocation
        • -
        • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
        • -
        • New first time screen
        • -
        • New create key screen: autocompletion of name and email based on your personal Android accounts
        • -
        • File encryption: awesome new design, support for encrypting multiple files
        • -
        • New icons to show status of key (by Brennan Novak)
        • -
        • Important bug fix: Importing of large key collections from a file is now possible
        • -
        • Notification showing cached passphrases
        • +
        • Es wurden in diesem Release so viele Bugs ausgebessert, sodass wir uns besser auf die neuen Funktionen konzentrieren
        • +
        • Schlüsselbearbeitung: tolles neues Design und Schlüsselrückruf
        • +
        • Schlüsselimport: tolles neues Design, gesicherte Verbindungen zum Schlüsselserver über HKPS, Schlüsselserver [?] über DNS SRV Einträge
        • +
        • Neuer Bildschirm bei der ersten Öffnung
        • +
        • New key creation screen: autocompletion of name and email based on your personal Android accounts
        • +
        • Dateiverschlüsselung: tolles neues Design, Unterstützung für mehrere Dateien
        • +
        • Neue Icons zum Anzeigen des Schlüsselstatus' (von Brennan Novak)
        • +
        • Wichtige Ausbesserung eines Bugs: Importieren größerer Schlüsselsammlungen aus einer Datei ist nun möglich
        • +
        • Benachrichtigung, die die Passphrasen im Cache anzeigt
        • +
        • Keys are connected to Android's contacts
        -

        This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

        +

        Dieser Release wäre ohne die Arbeit von Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray und Thialfihar nicht möglich

        2.7

          @@ -21,7 +22,7 @@
        • Neues Schlüsselansicht Design (Dominik, Vincent)
        • Neue flache Android Buttons (Dominik, Vincent)
        • API Fehler behoben (Dominik)
        • -
        • keybase.io importiert (Tim Bray)
        • +
        • Import aus keybase.io (Tim Bray)

        2.6.1

          @@ -29,17 +30,17 @@

        2.6

          -
        • Key certifications (thanks to Vincent Breitmoser)
        • -
        • Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
        • -
        • New design for signature verification
        • -
        • Custom key length (thanks to Greg Witczak)
        • -
        • Fix share-functionality from other apps
        • +
        • Schlüsselzertifizierungen (danke an Vincent Breitmoser)
        • +
        • Unterstützung für GnuPG-Teilschlüssel (danke an Vincent Breitmoser)
        • +
        • Neues Design für Signaturverifikation
        • +
        • Benutzerdefinierte Schlüssellänge (Dank an Greg Witczak)
        • +
        • Fehler behoben bei der Teilen-Funktion von anderen Apps

        2.5

          -
        • Fix decryption of symmetric pgp messages/files
        • -
        • Refactored edit key screen (thanks to Ash Hughes)
        • -
        • New modern design for encrypt/decrypt screens
        • +
        • Fehler bei der Entschlüsselung symmetrischer PGP Nachrichten/Dateien behoben
        • +
        • Refactored key edit screen (thanks to Ash Hughes)
        • +
        • Neues modernes Design für Verschlüsselungs-/Entschlüsselungs-Bildschirme
        • OpenPGP API Version 3 (mehrfache api accounts, interne fehlerbehebungen, schlüssel suche)

        2.4

        @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
        • Basic keyserver support
        • App2sd
        • -
        • More choices for pass phrase cache: 1, 2, 4, 8, hours
        • +
        • More choices for passphrase cache: 1, 2, 4, 8, hours
        • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • Bugfixes
        • Optimizations
        • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

          1.0.7

          • Fixed problem with signature verification of texts with trailing newline
          • -
          • More options for pass phrase cache time to live (20, 40, 60 mins)
          • +
          • More options for passphrase cache time to live (20, 40, 60 mins)

          1.0.6

            diff --git a/OpenKeychain/src/main/res/raw-de/help_start.html b/OpenKeychain/src/main/res/raw-de/help_start.html index dfff2bc03..0764c9ebb 100644 --- a/OpenKeychain/src/main/res/raw-de/help_start.html +++ b/OpenKeychain/src/main/res/raw-de/help_start.html @@ -1,7 +1,7 @@ -

            Los gehts

            +

            Los geht's

            Zuerst benötigen Sie einen privaten Schlüssel. Erstellen Sie einen Schlüssel über den Eintrag "Schlüssel" im Menu oder importieren Sie bestehende private Schlüssel. Anschließend können Sie die Schlüssel ihrer Freunde runterladen oder Sie über QR-Codes oder NFC austauschen.

            Es wird empfohlen, dass Sie den OI File Manager für ein verbessertes Dateihandling installieren, sowie den Barcode Scanner installieren um erstellte QR-Codes zu scannen. Die Links führen entweder zu Google Play oder zu F-Droid zur Installation.

            @@ -13,10 +13,10 @@

            Bitte berichten Sie Bugs mithilfe des Issue Trackers der OpenKeychain.

            Unterstützen

            -

            Wenn Sie uns bei der Entwickelung von OpenKeychain durch das Beisteuern von Code helfen wollt, schaut euch unsere kurze Anleitung auf Github an.

            +

            Wenn Sie uns bei der Entwickelung von OpenKeychain durch das Beisteuern von Code helfen wollen, schauen Sie unsere kurze Anleitung auf Github an.

            Übersetzungen

            -

            Hilf mit OpenKeychain zu übersetzten! Jeder kann auf OpenKeychain auf Transifex mitmachen.

            +

            Hilf mit, OpenKeychain zu übersetzten! Jeder kann auf OpenKeychain auf Transifex mitmachen.

            diff --git a/OpenKeychain/src/main/res/raw-es/help_changelog.html b/OpenKeychain/src/main/res/raw-es/help_changelog.html index a6b2d269d..cf778141a 100644 --- a/OpenKeychain/src/main/res/raw-es/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-es/help_changelog.html @@ -7,11 +7,12 @@
          • Edición de clave: Tremendo nuevo diseño, revocación de clave
          • Importación de clave: Impresionante nuevo diseño, conexiones seguras al servidor de claves vía hkps, el servidor de claves resuelve mediante registros DNS SRV
          • Nueva pantalla de primer inicio
          • -
          • Nueva pantalla de creación de clave: Autocompletado del nombre y correo electrónico basado en sus cuentas Android personales
          • +
          • Nueva pantalla de creación: autocompletado de nombre y correo electrónico basado en sus cuentas de Android personales
          • Cifrado de fichero: Pasmante nuevo diseño, soporte para cifrar múltiples ficheros.
          • Nuevos iconos para mostrar el estado de la clave (por Brennan Novak)
          • Importante reparacion de fallo: Ahora es posible la importación de grandes colecciones de claves desde un fichero
          • Notificación que muestra las frases contraseña almacenadas en caché
          • +
          • Las claves están conectadas con los contactos de Android

          Esta versión no sería posible sin el trabajo de Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

          @@ -33,13 +34,13 @@
        • Soporte para claves secretas parciales de GnuPG (gracias a Vincent Breitmoser)
        • Nuevo diseño para verificación de firma
        • Tamaño de clave personalizado (gracias a Greg Witczak)
        • -
        • Fix share-functionality from other apps
        • +
        • Repara la funcionalidad de compartido desde otras aplicaciones

        2.5

          -
        • Fix decryption of symmetric pgp messages/files
        • -
        • Refactored edit key screen (thanks to Ash Hughes)
        • -
        • New modern design for encrypt/decrypt screens
        • +
        • Repara el descifrado de mensajes/ficheros pgp simétricos
        • +
        • Pantalla de edición de clave refactorizada (gracias a Ash Hughes)
        • +
        • Nuevo diseño moderno para pantallas de cifrado/descifrado
        • OpenPGP API versión 3 (múltiples cuentas API, reparaciones internas, comprobación de claves)

        2.4

        @@ -47,45 +48,45 @@ Además de varios parches pequeños, un notable número de correcciones fueron hechas por las siguientes personas (en orden alfabético): Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.

          -
        • New unified key list
        • -
        • Colorized key fingerprint
        • -
        • Support for keyserver ports
        • -
        • Deactivate possibility to generate weak keys
        • -
        • Much more internal work on the API
        • -
        • Certify user ids
        • -
        • Keyserver query based on machine-readable output
        • -
        • Lock navigation drawer on tablets
        • -
        • Suggestions for emails on creation of keys
        • -
        • Search in public key lists
        • -
        • And much more improvements and fixes…
        • +
        • Nueva lista unificada de claves
        • +
        • Huella de validación de clave coloreada
        • +
        • Soporte para puertos de servidores de claves
        • +
        • Desactiva la posibilidad de generar claves débiles.
        • +
        • Mucho más trabajo interno en la API
        • +
        • Certifica las identidades de usuario
        • +
        • Petición al servidor de claves basada en salida legible-por-máquina
        • +
        • Cerrar panel lateral de navegación en tabletas
        • +
        • Sugerencias para correos electrónicos al crear claves
        • +
        • Buscar en listas de claves públicas
        • +
        • Añadir muchas más mejoras y reparaciones...

        2.3.1

          -
        • Hotfix for crash when upgrading from old versions
        • +
        • Reparación para fallo al actualizar desde versiones antiguas.

        2.3

          -
        • Remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
        • -
        • Fix setting expiry dates on keys (thanks to Ash Hughes)
        • -
        • More internal fixes when editing keys (thanks to Ash Hughes)
        • -
        • Querying keyservers directly from the import screen
        • -
        • Fix layout and dialog style on Android 2.2-3.0
        • -
        • Fix crash on keys with empty user ids
        • -
        • Fix crash and empty lists when coming back from signing screen
        • +
        • Elimina la exportación innecesaria de claves públicas al exportar claves secretas (privadas) (gracias a Ash Hughes)
        • +
        • Repara el establecimiento de fechas de expiración de claves (gracias a Ash Hughes)
        • +
        • Más reparaciones internas al editar claves (gracias a Ash Hughes)
        • +
        • Realización de peticiones a los servidores de claves desde la pantalla de importación
        • +
        • Repara la disposición y el estilo del cuadro de diálogo en Android 2.2-3.0
        • +
        • Repara fallos en claves con identidades de usuario vacías
        • +
        • Repara fallos y listas vacías al volver desde la pantalla de firmado
        • Bouncy Castle (librería criptográfica) actualizada de 1.47 a 1.50 y compilada desde la fuente
        • -
        • Fix upload of key from signing screen
        • +
        • Repara la subida de clave desde la pantalla de firmado

        2.2

          -
        • New design with navigation drawer
        • -
        • New public key list design
        • -
        • New public key view
        • -
        • Bug fixes for importing of keys
        • -
        • Key cross-certification (thanks to Ash Hughes)
        • -
        • Handle UTF-8 passwords properly (thanks to Ash Hughes)
        • -
        • First version with new languages (thanks to the contributors on Transifex)
        • -
        • Sharing of keys via QR Codes fixed and improved
        • -
        • Package signature verification for API
        • +
        • Nuevo diseño con panel lateral de navegación
        • +
        • Nuevo diseño de lista de claves públicas
        • +
        • Nueva vista de clave pública
        • +
        • Reparación de fallos para la importación de claves
        • +
        • Certificación-cruzada de clave (gracias a Ash Hughes)
        • +
        • Tratamiento adecuado de contraseñas UTF-8 (gracias a Ash Hughes)
        • +
        • Primera versión nuevos idiomas (gracias a los contribuidores en Transifex)
        • +
        • Compartición de claves mediante códigos QR reparada y mejorada
        • +
        • Verificación de firma de paquetes para la API

        2.1.1

          @@ -93,65 +94,65 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

        2.1

          -
        • Lots of bug fixes
        • -
        • New API for developers
        • +
        • Muchas reparaciones de fallos
        • +
        • Nueva API para desarrolladores
        • corrección del bug PRNG por Google

        2.0

          -
        • Complete redesign
        • -
        • Share public keys via qr codes, nfc beam
        • -
        • Sign keys
        • -
        • Upload keys to server
        • -
        • Fixes import issues
        • -
        • New AIDL API
        • +
        • Rediseño completo
        • +
        • Compartido de claves públicas mediante códigos qr, nfc beam
        • +
        • Firmar claves
        • +
        • Subir claves al servidor
        • +
        • Repara problemas de importación
        • +
        • Nueva API AIDL

        1.0.8

          -
        • Basic keyserver support
        • +
        • Soporte básico de servidor de claves
        • App2sd
        • -
        • More choices for pass phrase cache: 1, 2, 4, 8, hours
        • -
        • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • -
        • Bugfixes
        • -
        • Optimizations
        • +
        • Más opciones para caché de frase contraseña: 1, 2, 4, 8 horas
        • +
        • Traducciones: noruego (gracias, Sander Danielsen), chino (gracias, Zhang Fredrick)
        • +
        • Reparaciones de fallos
        • +
        • Optimizaciones

        1.0.7

          -
        • Fixed problem with signature verification of texts with trailing newline
        • -
        • More options for pass phrase cache time to live (20, 40, 60 mins)
        • +
        • Reparado un problema con la verificación de firma de textos con una nueva línea al principio
        • +
        • Más opciones para el periodo de vida en caché de la frase contraseña (20, 40, 60 mins)

        1.0.6

          -
        • Account adding crash on Froyo fixed
        • -
        • Secure file deletion
        • -
        • Option to delete key file after import
        • -
        • Stream encryption/decryption (gallery, etc.)
        • -
        • New options (language, force v3 signatures)
        • -
        • Interface changes
        • -
        • Bugfixes
        • +
        • Reparado el fallo de la adición de cuenta en Froyo
        • +
        • Borrado seguro de fichero
        • +
        • Opción para borrar fichero de clave después de importar
        • +
        • Cifrado/descifrado del stream (galería, etc.)
        • +
        • Nuevas opciones (idioma, forzar firmas v3)
        • +
        • Cambios en la interfaz
        • +
        • Reparaciones de fallos

        1.0.5

        • traducciones a alemán e italiano
        • -
        • Much smaller package, due to reduced BC sources
        • -
        • New preferences GUI
        • -
        • Layout adjustment for localization
        • -
        • Signature bugfix
        • +
        • Paquete mucho más pequeño, debido a fuentes de codec de bitrate (BC) reducido
        • +
        • Nueva interfaz gráfica (GUI) de prefencias
        • +
        • Ajuste de la disposición para localización
        • +
        • Reparación de firma

        1.0.4

          -
        • Fixed another crash caused by some SDK bug with query builder
        • +
        • Reparado otro fallo causado por algún fallo del kit de desarrollo de software (SDK) con el constructor de peticiones

        1.0.3

          -
        • Fixed crashes during encryption/signing and possibly key export
        • +
        • Reparados fallos durante el cifrado/firmado y posiblemente el exportado de clave

        1.0.2

          -
        • Filterable key lists
        • -
        • Smarter pre-selection of encryption keys
        • -
        • New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
        • -
        • Fixes and additional features (key preselection) for K-9 Mail, new beta build available
        • +
        • Listas de claves filtrables
        • +
        • Pre-selección de claves de cifrado más inteligente
        • +
        • Nuevo objeto Intent que maneja VIEW y SEND (ver y enviar), permite que los ficheros sean cifrados/descifrados fuera de los administradores de ficheros.
        • +
        • Reparaciones y características adicionales (preselección de clave) para K-9 Mail, nueva versión beta disponible

        1.0.1

          @@ -160,11 +161,11 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

          1.0.0

          • integración con K-9 Mail, APG compatible con la compilación beta de K-9 Mail
          • -
          • Support of more file managers (including ASTRO)
          • +
          • Soporte de más administradores de ficheros (incluyendo ASTRO)
          • traducción al esloveno
          • -
          • New database, much faster, less memory usage
          • -
          • Defined Intents and content provider for other apps
          • -
          • Bugfixes
          • +
          • Nueva base de datos, mucho más rápida, menos uso de memoria
          • +
          • Definidos objetos Intent y proveedores de contenido para otras aplicaciones
          • +
          • Reparaciones de fallos
          diff --git a/OpenKeychain/src/main/res/raw-et/help_changelog.html b/OpenKeychain/src/main/res/raw-et/help_changelog.html index e27ac7475..41ec2cad6 100644 --- a/OpenKeychain/src/main/res/raw-et/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-et/help_changelog.html @@ -7,11 +7,12 @@
        • Key edit: awesome new design, key revocation
        • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
        • New first time screen
        • -
        • New create key screen: autocompletion of name and email based on your personal Android accounts
        • +
        • New key creation screen: autocompletion of name and email based on your personal Android accounts
        • File encryption: awesome new design, support for encrypting multiple files
        • New icons to show status of key (by Brennan Novak)
        • Important bug fix: Importing of large key collections from a file is now possible
        • Notification showing cached passphrases
        • +
        • Keys are connected to Android's contacts

        This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

        @@ -38,7 +39,7 @@

        2.5

        • Fix decryption of symmetric pgp messages/files
        • -
        • Refactored edit key screen (thanks to Ash Hughes)
        • +
        • Refactored key edit screen (thanks to Ash Hughes)
        • New modern design for encrypt/decrypt screens
        • OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
        @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
        • Basic keyserver support
        • App2sd
        • -
        • More choices for pass phrase cache: 1, 2, 4, 8, hours
        • +
        • More choices for passphrase cache: 1, 2, 4, 8, hours
        • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • Bugfixes
        • Optimizations
        • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

          1.0.7

          • Fixed problem with signature verification of texts with trailing newline
          • -
          • More options for pass phrase cache time to live (20, 40, 60 mins)
          • +
          • More options for passphrase cache time to live (20, 40, 60 mins)

          1.0.6

            diff --git a/OpenKeychain/src/main/res/raw-fr/help_changelog.html b/OpenKeychain/src/main/res/raw-fr/help_changelog.html index 43618398d..9d58256a7 100644 --- a/OpenKeychain/src/main/res/raw-fr/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-fr/help_changelog.html @@ -12,6 +12,7 @@
          • Nouvelles icônes d'état des clefs (par Brennan Novak)
          • Correctif important de bogue : l'importation de grandes collections de clefs à partir d'un fichier est maintenant possible
          • Notification montrant les phrases de passe en cache
          • +
          • Les clefs sont connectées aux contacts d'Android

          Cette version ne serait pas possible sans le travail de Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

          @@ -38,7 +39,7 @@

          2.5

          • Correctif - déchiffrement de messages/fichiers pgp symétriques
          • -
          • Nouvel mouture de l'écran de modification des clefs (merci à Ash Hughes)
          • +
          • Écran de modification des clefs remanié (merci à Ash Hughes)
          • Nouvelle conception moderne pour les écrans de chiffrement/déchiffrement
          • API OpenPGP version 3 (comptes multiples d'api, correctifs internes, recherche de clefs)
          @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
          • Prise en charge de base du serveur de clefs
          • App2sd
          • -
          • Plus de choix pour le cache des phrases de passe : 1, 2, 4, 8 heures
          • +
          • Plus de choix pour le cache de la phrase de passe : 1, 2, 4, 8 heures
          • Traductions : norvégien (merci Sander Danielsen), chinois (merci Zhang Fredrick)
          • Correctifs de bogues
          • Optimisations
          • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

            1.0.7

            • Problème corrigé avec la vérification de la signature des textes se terminant par un retour à la ligne
            • -
            • Plus de choix pour la durée de vie de la phrase de passe : (20, 40, 60 min)
            • +
            • Plus de choix pour la durée de vie de la phrase de passe (20, 40, 60 min)

            1.0.6

              diff --git a/OpenKeychain/src/main/res/raw-it/help_about.html b/OpenKeychain/src/main/res/raw-it/help_about.html index e26c8e246..7b586cbd3 100644 --- a/OpenKeychain/src/main/res/raw-it/help_about.html +++ b/OpenKeychain/src/main/res/raw-it/help_about.html @@ -5,9 +5,9 @@

              OpenKeychain un implementazione OpenPGP per Android.

              Licenza: GPLv3+

              -

              Developers

              +

              Sviluppatori

                -
              • Dominik Schürmann (Maintainer)
              • +
              • Dominik Schürmann (Manutentore)
              • Art O Cathain
              • Ash Hughes
              • Brian C. Barnes
              • @@ -34,13 +34,13 @@
              • Android Support Library v7 'appcompat' (Licenza Apache v2)
              • -KeybaseLib (Apache License v2)
              • +KeybaseLib (Licenza Apache v2)
              • -SuperToasts (Apache License v2)
              • +SuperToasts (Licenza Apache v2)
              • -TokenAutoComplete (Apache License v2)
              • +TokenAutoComplete (Licenza Apache v2)
              • -MiniDNS (Apache License v2)
              • +MiniDNS (Licenza Apache v2)
              • StickyListHeaders (Licenza Apache v2)
              • diff --git a/OpenKeychain/src/main/res/raw-it/help_changelog.html b/OpenKeychain/src/main/res/raw-it/help_changelog.html index 6b39c43c0..c4cae4d47 100644 --- a/OpenKeychain/src/main/res/raw-it/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-it/help_changelog.html @@ -3,17 +3,18 @@

                2.8

                  -
                • So many bugs have been fixed in this release that we focus on the main new features
                • -
                • Key edit: awesome new design, key revocation
                • -
                • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                • -
                • New first time screen
                • -
                • New create key screen: autocompletion of name and email based on your personal Android accounts
                • -
                • File encryption: awesome new design, support for encrypting multiple files
                • -
                • New icons to show status of key (by Brennan Novak)
                • -
                • Important bug fix: Importing of large key collections from a file is now possible
                • -
                • Notification showing cached passphrases
                • +
                • Così tanti bug sono stati corretti in questa versione che ci concentriamo sulle principali novità
                • +
                • Modifica chiave: nuovo eccezionale design, revoca chiave
                • +
                • Importa chiave: nuovo eccezionale design, connessioni sicure via keyserver HKPS, risoluzione keyserver tramite record SRV DNS
                • +
                • Nuova schermata di introduzione
                • +
                • Nuova schermata di creazione chiave: autocompletamento di nomi e email basato sui tuoi account personali di Android
                • +
                • Codifica File: nuovo eccezionale design, supporto per codifica di più documenti
                • +
                • Nuove icone per mostrare lo stato delle chiavi (di Brennan Novak)
                • +
                • Correzione bug importante: Importazione di grandi collezioni di chiavi da un file ora è possibile
                • +
                • Notifiche mostrando la frase di accesso nella cache
                • +
                • Le chiavi sono connesse ai contatti di Android
                -

                This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                +

                Questo rilascio non sarebbe stato possibile senza il lavoro di Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                2.7

                  @@ -25,21 +26,21 @@

                2.6.1

                  -
                • Some fixes for regression bugs
                • +
                • alcune correzioni per i bug di regressione

                2.6

                  -
                • Key certifications (thanks to Vincent Breitmoser)
                • -
                • Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
                • -
                • New design for signature verification
                • -
                • Custom key length (thanks to Greg Witczak)
                • -
                • Fix share-functionality from other apps
                • +
                • Certificazioni chiave (grazie a Vincent Breitmoser)
                • +
                • supporto per chiavi segrete parziali GnuPG (grazie a Vincent Breitmoser)
                • +
                • nuovo design per la verifica della firma
                • +
                • lunghezza chiave personalizzata (grazie a Greg Witczak)
                • +
                • fix funzionalità di condivisione da altre app

                2.5

                  -
                • Fix decryption of symmetric pgp messages/files
                • -
                • Refactored edit key screen (thanks to Ash Hughes)
                • -
                • New modern design for encrypt/decrypt screens
                • +
                • Corretta la decodifica di messaggi PGP / file simmetrici
                • +
                • Refactoring della schermata di modifica chiave (grazie a Ash Hughes)
                • +
                • Nuovo design moderno per le schermate di codifica / decodifica
                • OpenPGP API versione 3 (api account multipli, correzioni interne, ricerca chiavi)

                2.4

                @@ -47,45 +48,45 @@ Oltre a numerose piccole correzioni, un notevole numero di patch sono state fatte dalle seguenti persone (in ordine alfabetico): Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paolo Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.

                  -
                • New unified key list
                • -
                • Colorized key fingerprint
                • -
                • Support for keyserver ports
                • -
                • Deactivate possibility to generate weak keys
                • -
                • Much more internal work on the API
                • -
                • Certify user ids
                • -
                • Keyserver query based on machine-readable output
                • -
                • Lock navigation drawer on tablets
                • -
                • Suggestions for emails on creation of keys
                • -
                • Search in public key lists
                • -
                • And much more improvements and fixes…
                • +
                • Nuova lista chiave unificata
                • +
                • Impronta chiave colorata
                • +
                • Supporto per le porte keyserver
                • +
                • disattivata la possibilità di generare chiavi deboli
                • +
                • Molto più lavoro interno sulle API
                • +
                • Certificazione ID utente
                • +
                • Interrogazione keyserver basate su output leggibile a livello macchina
                • +
                • Blocco del menu di navigazione sui tablet
                • +
                • Suggerimenti per e-mail sulla creazione di chiavi
                • +
                • Ricerca nelle liste di chiavi pubbliche
                • +
                • E molti altri miglioramenti e correzioni ...

                2.3.1

                  -
                • Hotfix for crash when upgrading from old versions
                • +
                • Correzione del crash quando si aggiorna da versioni precedenti

                2.3

                  -
                • Remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
                • -
                • Fix setting expiry dates on keys (thanks to Ash Hughes)
                • -
                • More internal fixes when editing keys (thanks to Ash Hughes)
                • -
                • Querying keyservers directly from the import screen
                • -
                • Fix layout and dialog style on Android 2.2-3.0
                • -
                • Fix crash on keys with empty user ids
                • -
                • Fix crash and empty lists when coming back from signing screen
                • +
                • Rimossa esportazione non necessaria delle chiavi pubbliche quando si esportano le chiavi private (grazie a Ash Hughes)
                • +
                • Corretto impostazione data di scadenza delle chiavi (grazie a Ash Hughes)
                • +
                • Molte altre correzioni interne quando si modificano le chiavi (grazie a Ash Hughes)
                • +
                • Interrogazione server delle chiavi direttamente dalla schermata di importazione
                • +
                • Corretta impaginazione e stile di dialogo su Android 2.2-3.0
                • +
                • Corretto crash su chiavi con id utente vuoto
                • +
                • Corretto crash e liste vuote quando si torna dalla schermata di firma
                • Bouncy Castle (libreria crittografica) aggiornata da 1.47 a 1.50 e compilata da sorgente
                • -
                • Fix upload of key from signing screen
                • +
                • Corretto caricamento delle chiavi dalla schermata di firma

                2.2

                  -
                • New design with navigation drawer
                • -
                • New public key list design
                • -
                • New public key view
                • -
                • Bug fixes for importing of keys
                • -
                • Key cross-certification (thanks to Ash Hughes)
                • -
                • Handle UTF-8 passwords properly (thanks to Ash Hughes)
                • -
                • First version with new languages (thanks to the contributors on Transifex)
                • -
                • Sharing of keys via QR Codes fixed and improved
                • -
                • Package signature verification for API
                • +
                • Nuovo design con barra di navigazione
                • +
                • Nuovo design per la lista chiavi pubbliche
                • +
                • Nuova visuale chiavi pubbliche
                • +
                • Correzione bug per importazione chiavi
                • +
                • Chiave certificazione incrociata (grazie a Ash Hughes)
                • +
                • Password UTF-8 gestite correttamente (grazie a Ash Hughes)
                • +
                • Prima versione con nuove lingue (grazie ai contributori su Transifex)
                • +
                • Condivisione di chiavi via Codici QR corretta e migliorata
                • +
                • Verifica firma pacchetto per API

                2.1.1

                  @@ -93,65 +94,65 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                2.1

                  -
                • Lots of bug fixes
                • -
                • New API for developers
                • +
                • Molte correzioni di bug
                • +
                • Nuove API per sviluppatori
                • PRNG bug fix by Google

                2.0

                  -
                • Complete redesign
                • -
                • Share public keys via qr codes, nfc beam
                • -
                • Sign keys
                • -
                • Upload keys to server
                • -
                • Fixes import issues
                • -
                • New AIDL API
                • +
                • Completo restyle
                • +
                • Condivisione chiavi pubbliche via codici qr, nfc beam
                • +
                • Firma chiavi
                • +
                • Caricamento chiavi sul server
                • +
                • Corrette caratteristiche di importazione
                • +
                • Nuova API AIDL

                1.0.8

                  -
                • Basic keyserver support
                • +
                • Supporto base per server delle chiavi
                • App2sd
                • -
                • More choices for pass phrase cache: 1, 2, 4, 8, hours
                • -
                • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                • -
                • Bugfixes
                • -
                • Optimizations
                • +
                • Aggiunte opzioni per la cache della frase di accesso: 1, 2, 4, 8 ore
                • +
                • Traduzioni: Norvegese (grazie, Sander Danielsen), Cinese (grazie, Zhang Fredrick)
                • +
                • Correzione bug
                • +
                • Ottimizzazioni

                1.0.7

                  -
                • Fixed problem with signature verification of texts with trailing newline
                • -
                • More options for pass phrase cache time to live (20, 40, 60 mins)
                • +
                • Corretto problema con la verifica firma di testi con capo finale
                • +
                • Maggiori opzioni per il tempo di mantenimento della cache della frase di accesso (20, 40, 60 minuti)

                1.0.6

                  -
                • Account adding crash on Froyo fixed
                • -
                • Secure file deletion
                • -
                • Option to delete key file after import
                • -
                • Stream encryption/decryption (gallery, etc.)
                • -
                • New options (language, force v3 signatures)
                • -
                • Interface changes
                • -
                • Bugfixes
                • +
                • Crash della aggiunta degli account risolto su Froyo
                • +
                • Cancellazione sicura dei file
                • +
                • Opzione per cancellare file delle chiavi dopo l'importazione
                • +
                • Flusso codifica/decodifica (galleria, ecc.)
                • +
                • Nuove opzioni (lingua, forza firme v3)
                • +
                • Cambiamenti interfaccia
                • +
                • Correzione bug

                1.0.5

                • Traduzione Italiana e Tedesca
                • -
                • Much smaller package, due to reduced BC sources
                • -
                • New preferences GUI
                • -
                • Layout adjustment for localization
                • -
                • Signature bugfix
                • +
                • Dimensioni pacchetto ridotte, a causa della riduzione dei sorgenti BC
                • +
                • Nuove preferenze GUI
                • +
                • Regolazione layout per la localizzazione
                • +
                • Correzione bug firma

                1.0.4

                  -
                • Fixed another crash caused by some SDK bug with query builder
                • +
                • Corretto altro crash causato da alcuni bug SDK con query builder

                1.0.3

                  -
                • Fixed crashes during encryption/signing and possibly key export
                • +
                • Corretto crash durante codifica/firma e possibilita' di esportare chiave

                1.0.2

                  -
                • Filterable key lists
                • -
                • Smarter pre-selection of encryption keys
                • -
                • New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
                • -
                • Fixes and additional features (key preselection) for K-9 Mail, new beta build available
                • +
                • Liste chiavi filtrabili
                • +
                • Preselezione di chiavi di codifica intelligente
                • +
                • Nuovo gestore intent per VIEW e SEND, permette la codifica/decodifica file all'infuori di file manager
                • +
                • Caratteristiche corrette e aggiunte (preselezione chiavi) per K-9 Mail. nuova build beta disponibile

                1.0.1

                  @@ -160,11 +161,11 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                  1.0.0

                  • integrazione K-9 Mail, APG supporto beta build di K-9 Mail
                  • -
                  • Support of more file managers (including ASTRO)
                  • +
                  • Supporto per altri file manager (incluso ASTRO)
                  • traduzione Slovena
                  • -
                  • New database, much faster, less memory usage
                  • -
                  • Defined Intents and content provider for other apps
                  • -
                  • Bugfixes
                  • +
                  • Nuovo database, piu' veloce, utilizzo memoria ridotto
                  • +
                  • Definiti Intent e ContentProvider per le altre app
                  • +
                  • Correzione bug
                  diff --git a/OpenKeychain/src/main/res/raw-ja/help_changelog.html b/OpenKeychain/src/main/res/raw-ja/help_changelog.html index 21f0fa230..0f467c407 100644 --- a/OpenKeychain/src/main/res/raw-ja/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-ja/help_changelog.html @@ -12,6 +12,7 @@
                • 鍵のステータス表示の新しいアイコン(Brennan Novak提供)
                • 重要なバグ修正: 巨大な鍵コレクションをファイルからインポートするのが可能になりました
                • キャッシュしたパスフレーズの通知表示
                • +
                • 鍵のアドレスをAndroidの連絡先と連携するようにした

                Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfiharの働きなくしてはこのリリースはありませんでした

                @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                • 鍵サーバの基本サポート
                • App2SD
                • -
                • パスフレーズのキャッシュ時間について1,2,4,8時間の選択肢追加
                • +
                • パスフレーズのキャッシュ時間についての選択肢追加: 1,2,4,8時間
                • 翻訳: ノルウェー語 (ありがとう、Sander Danielsen)、中国語 (ありがとう、Zhang Fredrick) 追加
                • バグ修正
                • 最適化
                • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                  1.0.7

                  • 改行を含まないテキストの署名検証問題の修正
                  • -
                  • パスフレーズのキャッシュ時間 (20,40,60分) のオプション追加
                  • +
                  • パスフレーズのキャッシュ生存時間 (20,40,60分) のオプション追加

                  1.0.6

                    diff --git a/OpenKeychain/src/main/res/raw-nl/help_changelog.html b/OpenKeychain/src/main/res/raw-nl/help_changelog.html index 3bc039642..9edbb718e 100644 --- a/OpenKeychain/src/main/res/raw-nl/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-nl/help_changelog.html @@ -7,11 +7,12 @@
                  • Key edit: awesome new design, key revocation
                  • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                  • New first time screen
                  • -
                  • New create key screen: autocompletion of name and email based on your personal Android accounts
                  • +
                  • New key creation screen: autocompletion of name and email based on your personal Android accounts
                  • File encryption: awesome new design, support for encrypting multiple files
                  • New icons to show status of key (by Brennan Novak)
                  • Important bug fix: Importing of large key collections from a file is now possible
                  • Notification showing cached passphrases
                  • +
                  • Keys are connected to Android's contacts

                  This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                  @@ -38,7 +39,7 @@

                  2.5

                  • Fix decryption of symmetric pgp messages/files
                  • -
                  • Refactored edit key screen (thanks to Ash Hughes)
                  • +
                  • Refactored key edit screen (thanks to Ash Hughes)
                  • New modern design for encrypt/decrypt screens
                  • OpenPGP API versie 3 (meerdere api accounts, interne fixes, sleutel lookup)
                  @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                  • Basic keyserver support
                  • App2sd
                  • -
                  • More choices for pass phrase cache: 1, 2, 4, 8, hours
                  • +
                  • More choices for passphrase cache: 1, 2, 4, 8, hours
                  • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                  • Bugfixes
                  • Optimizations
                  • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                    1.0.7

                    • Fixed problem with signature verification of texts with trailing newline
                    • -
                    • More options for pass phrase cache time to live (20, 40, 60 mins)
                    • +
                    • More options for passphrase cache time to live (20, 40, 60 mins)

                    1.0.6

                      diff --git a/OpenKeychain/src/main/res/raw-pl/help_changelog.html b/OpenKeychain/src/main/res/raw-pl/help_changelog.html index eded2c8c4..2fb953cd2 100644 --- a/OpenKeychain/src/main/res/raw-pl/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-pl/help_changelog.html @@ -7,11 +7,12 @@
                    • Key edit: awesome new design, key revocation
                    • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                    • New first time screen
                    • -
                    • New create key screen: autocompletion of name and email based on your personal Android accounts
                    • +
                    • New key creation screen: autocompletion of name and email based on your personal Android accounts
                    • File encryption: awesome new design, support for encrypting multiple files
                    • New icons to show status of key (by Brennan Novak)
                    • Important bug fix: Importing of large key collections from a file is now possible
                    • Notification showing cached passphrases
                    • +
                    • Keys are connected to Android's contacts

                    This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                    @@ -38,7 +39,7 @@

                    2.5

                    • Fix decryption of symmetric pgp messages/files
                    • -
                    • Refactored edit key screen (thanks to Ash Hughes)
                    • +
                    • Refactored key edit screen (thanks to Ash Hughes)
                    • New modern design for encrypt/decrypt screens
                    • OpenPGP API wersja 3 (wiele kont API, wewnętrzne poprawki, wyszukiwanie kluczy)
                    @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                    • Basic keyserver support
                    • App2sd
                    • -
                    • More choices for pass phrase cache: 1, 2, 4, 8, hours
                    • +
                    • More choices for passphrase cache: 1, 2, 4, 8, hours
                    • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                    • Bugfixes
                    • Optimizations
                    • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                      1.0.7

                      • Fixed problem with signature verification of texts with trailing newline
                      • -
                      • More options for pass phrase cache time to live (20, 40, 60 mins)
                      • +
                      • More options for passphrase cache time to live (20, 40, 60 mins)

                      1.0.6

                        diff --git a/OpenKeychain/src/main/res/raw-pt/help_about.html b/OpenKeychain/src/main/res/raw-pt/help_about.html new file mode 100644 index 000000000..f536fecbd --- /dev/null +++ b/OpenKeychain/src/main/res/raw-pt/help_about.html @@ -0,0 +1,54 @@ + + + +

                        http://www.openkeychain.org

                        +

                        OpenKeychain is an OpenPGP implementation for Android.

                        +

                        License: GPLv3+

                        + +

                        Developers

                        +
                          +
                        • Dominik Schürmann (Maintainer)
                        • +
                        • Art O Cathain
                        • +
                        • Ash Hughes
                        • +
                        • Brian C. Barnes
                        • +
                        • Bahtiar 'kalkin' Gadimov
                        • +
                        • Daniel Albert
                        • +
                        • Daniel Hammann
                        • +
                        • Daniel Haß
                        • +
                        • Greg Witczak
                        • +
                        • 'mar-v-in'
                        • +
                        • Markus Doits
                        • +
                        • Miroojin Bakshi
                        • +
                        • Nikhil Peter Raj
                        • +
                        • Paul Sarbinowski
                        • +
                        • 'Senecaso'
                        • +
                        • Sreeram Boyapati
                        • +
                        • Thialfihar (APG 1.x)
                        • +
                        • Tim Bray
                        • +
                        • Vincent Breitmoser
                        • +
                        +

                        Libraries

                        + + + diff --git a/OpenKeychain/src/main/res/raw-pt/help_changelog.html b/OpenKeychain/src/main/res/raw-pt/help_changelog.html new file mode 100644 index 000000000..41ec2cad6 --- /dev/null +++ b/OpenKeychain/src/main/res/raw-pt/help_changelog.html @@ -0,0 +1,171 @@ + + + +

                        2.8

                        +
                          +
                        • So many bugs have been fixed in this release that we focus on the main new features
                        • +
                        • Key edit: awesome new design, key revocation
                        • +
                        • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                        • +
                        • New first time screen
                        • +
                        • New key creation screen: autocompletion of name and email based on your personal Android accounts
                        • +
                        • File encryption: awesome new design, support for encrypting multiple files
                        • +
                        • New icons to show status of key (by Brennan Novak)
                        • +
                        • Important bug fix: Importing of large key collections from a file is now possible
                        • +
                        • Notification showing cached passphrases
                        • +
                        • Keys are connected to Android's contacts
                        • +
                        +

                        This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                        + +

                        2.7

                        +
                          +
                        • Purple! (Dominik, Vincent)
                        • +
                        • New key view design (Dominik, Vincent)
                        • +
                        • New flat Android buttons (Dominik, Vincent)
                        • +
                        • API fixes (Dominik)
                        • +
                        • Keybase.io import (Tim Bray)
                        • +
                        +

                        2.6.1

                        +
                          +
                        • Some fixes for regression bugs
                        • +
                        +

                        2.6

                        +
                          +
                        • Key certifications (thanks to Vincent Breitmoser)
                        • +
                        • Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
                        • +
                        • New design for signature verification
                        • +
                        • Custom key length (thanks to Greg Witczak)
                        • +
                        • Fix share-functionality from other apps
                        • +
                        +

                        2.5

                        +
                          +
                        • Fix decryption of symmetric pgp messages/files
                        • +
                        • Refactored key edit screen (thanks to Ash Hughes)
                        • +
                        • New modern design for encrypt/decrypt screens
                        • +
                        • OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
                        • +
                        +

                        2.4

                        +

                        Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free! +Besides several small patches, a notable number of patches are made by the following people (in alphabetical order): +Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.

                        +
                          +
                        • New unified key list
                        • +
                        • Colorized key fingerprint
                        • +
                        • Support for keyserver ports
                        • +
                        • Deactivate possibility to generate weak keys
                        • +
                        • Much more internal work on the API
                        • +
                        • Certify user ids
                        • +
                        • Keyserver query based on machine-readable output
                        • +
                        • Lock navigation drawer on tablets
                        • +
                        • Suggestions for emails on creation of keys
                        • +
                        • Search in public key lists
                        • +
                        • And much more improvements and fixes…
                        • +
                        +

                        2.3.1

                        +
                          +
                        • Hotfix for crash when upgrading from old versions
                        • +
                        +

                        2.3

                        +
                          +
                        • Remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
                        • +
                        • Fix setting expiry dates on keys (thanks to Ash Hughes)
                        • +
                        • More internal fixes when editing keys (thanks to Ash Hughes)
                        • +
                        • Querying keyservers directly from the import screen
                        • +
                        • Fix layout and dialog style on Android 2.2-3.0
                        • +
                        • Fix crash on keys with empty user ids
                        • +
                        • Fix crash and empty lists when coming back from signing screen
                        • +
                        • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
                        • +
                        • Fix upload of key from signing screen
                        • +
                        +

                        2.2

                        +
                          +
                        • New design with navigation drawer
                        • +
                        • New public key list design
                        • +
                        • New public key view
                        • +
                        • Bug fixes for importing of keys
                        • +
                        • Key cross-certification (thanks to Ash Hughes)
                        • +
                        • Handle UTF-8 passwords properly (thanks to Ash Hughes)
                        • +
                        • First version with new languages (thanks to the contributors on Transifex)
                        • +
                        • Sharing of keys via QR Codes fixed and improved
                        • +
                        • Package signature verification for API
                        • +
                        +

                        2.1.1

                        +
                          +
                        • API Updates, preparation for K-9 Mail integration
                        • +
                        +

                        2.1

                        +
                          +
                        • Lots of bug fixes
                        • +
                        • New API for developers
                        • +
                        • PRNG bug fix by Google
                        • +
                        +

                        2.0

                        +
                          +
                        • Complete redesign
                        • +
                        • Share public keys via qr codes, nfc beam
                        • +
                        • Sign keys
                        • +
                        • Upload keys to server
                        • +
                        • Fixes import issues
                        • +
                        • New AIDL API
                        • +
                        +

                        1.0.8

                        +
                          +
                        • Basic keyserver support
                        • +
                        • App2sd
                        • +
                        • More choices for passphrase cache: 1, 2, 4, 8, hours
                        • +
                        • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                        • +
                        • Bugfixes
                        • +
                        • Optimizations
                        • +
                        +

                        1.0.7

                        +
                          +
                        • Fixed problem with signature verification of texts with trailing newline
                        • +
                        • More options for passphrase cache time to live (20, 40, 60 mins)
                        • +
                        +

                        1.0.6

                        +
                          +
                        • Account adding crash on Froyo fixed
                        • +
                        • Secure file deletion
                        • +
                        • Option to delete key file after import
                        • +
                        • Stream encryption/decryption (gallery, etc.)
                        • +
                        • New options (language, force v3 signatures)
                        • +
                        • Interface changes
                        • +
                        • Bugfixes
                        • +
                        +

                        1.0.5

                        +
                          +
                        • German and Italian translation
                        • +
                        • Much smaller package, due to reduced BC sources
                        • +
                        • New preferences GUI
                        • +
                        • Layout adjustment for localization
                        • +
                        • Signature bugfix
                        • +
                        +

                        1.0.4

                        +
                          +
                        • Fixed another crash caused by some SDK bug with query builder
                        • +
                        +

                        1.0.3

                        +
                          +
                        • Fixed crashes during encryption/signing and possibly key export
                        • +
                        +

                        1.0.2

                        +
                          +
                        • Filterable key lists
                        • +
                        • Smarter pre-selection of encryption keys
                        • +
                        • New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
                        • +
                        • Fixes and additional features (key preselection) for K-9 Mail, new beta build available
                        • +
                        +

                        1.0.1

                        +
                          +
                        • GMail account listing was broken in 1.0.0, fixed again
                        • +
                        +

                        1.0.0

                        +
                          +
                        • K-9 Mail integration, APG supporting beta build of K-9 Mail
                        • +
                        • Support of more file managers (including ASTRO)
                        • +
                        • Slovenian translation
                        • +
                        • New database, much faster, less memory usage
                        • +
                        • Defined Intents and content provider for other apps
                        • +
                        • Bugfixes
                        • +
                        + + diff --git a/OpenKeychain/src/main/res/raw-pt/help_nfc_beam.html b/OpenKeychain/src/main/res/raw-pt/help_nfc_beam.html new file mode 100644 index 000000000..88492731c --- /dev/null +++ b/OpenKeychain/src/main/res/raw-pt/help_nfc_beam.html @@ -0,0 +1,12 @@ + + + +

                        How to receive keys

                        +
                          +
                        1. Go to your partners contacts and open the contact you want to share.
                        2. +
                        3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
                        4. +
                        5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
                        6. +
                        7. Tap the card and the content will then load on the your device.
                        8. +
                        + + diff --git a/OpenKeychain/src/main/res/raw-pt/help_start.html b/OpenKeychain/src/main/res/raw-pt/help_start.html new file mode 100644 index 000000000..51a76c01e --- /dev/null +++ b/OpenKeychain/src/main/res/raw-pt/help_start.html @@ -0,0 +1,22 @@ + + + +

                        Getting started

                        +

                        First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

                        + +

                        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

                        + +

                        Applications

                        +

                        Several applications support OpenKeychain to encrypt/sign your private communication:

                        K-9 Mail: OpenKeychain support available in current alpha build!

                        Conversations
                        : Jabber/XMPP client

                        PGPAuth
                        : App to send a PGP-signed request to a server to open or close something, e.g. a door

                        + +

                        I found a bug in OpenKeychain!

                        +

                        Please report the bug using the issue tracker of OpenKeychain.

                        + +

                        Contribute

                        +

                        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

                        + +

                        Translations

                        +

                        Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

                        + + + diff --git a/OpenKeychain/src/main/res/raw-pt/help_wot.html b/OpenKeychain/src/main/res/raw-pt/help_wot.html new file mode 100644 index 000000000..29790139b --- /dev/null +++ b/OpenKeychain/src/main/res/raw-pt/help_wot.html @@ -0,0 +1,17 @@ + + + +

                        Web of Trust

                        +

                        The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.

                        + +

                        Support in OpenKeychain

                        +

                        There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.

                        + +

                        Trust Model

                        +

                        Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.

                        + +

                        Certifying keys

                        +

                        Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.

                        + + + diff --git a/OpenKeychain/src/main/res/raw-pt/nfc_beam_share.html b/OpenKeychain/src/main/res/raw-pt/nfc_beam_share.html new file mode 100644 index 000000000..083e055c7 --- /dev/null +++ b/OpenKeychain/src/main/res/raw-pt/nfc_beam_share.html @@ -0,0 +1,11 @@ + + + +
                          +
                        1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
                        2. +
                        3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
                        4. +
                        5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
                        6. +
                        7. Tap the card and the content will then load on the other person’s device.
                        8. +
                        + + diff --git a/OpenKeychain/src/main/res/raw-ru/help_changelog.html b/OpenKeychain/src/main/res/raw-ru/help_changelog.html index f74a54762..6f33e8e01 100644 --- a/OpenKeychain/src/main/res/raw-ru/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-ru/help_changelog.html @@ -7,11 +7,12 @@
                      • Key edit: awesome new design, key revocation
                      • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                      • New first time screen
                      • -
                      • New create key screen: autocompletion of name and email based on your personal Android accounts
                      • +
                      • New key creation screen: autocompletion of name and email based on your personal Android accounts
                      • File encryption: awesome new design, support for encrypting multiple files
                      • New icons to show status of key (by Brennan Novak)
                      • Important bug fix: Importing of large key collections from a file is now possible
                      • Notification showing cached passphrases
                      • +
                      • Keys are connected to Android's contacts

                      This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                      @@ -38,7 +39,7 @@

                      2.5

                      • Fix decryption of symmetric pgp messages/files
                      • -
                      • Refactored edit key screen (thanks to Ash Hughes)
                      • +
                      • Refactored key edit screen (thanks to Ash Hughes)
                      • New modern design for encrypt/decrypt screens
                      • OpenPGP API версии 3 (множественные аккаунты, внутренние исправления, поиск ключей)
                      @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                      • Basic keyserver support
                      • App2sd
                      • -
                      • More choices for pass phrase cache: 1, 2, 4, 8, hours
                      • +
                      • More choices for passphrase cache: 1, 2, 4, 8, hours
                      • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                      • Bugfixes
                      • Optimizations
                      • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                        1.0.7

                        • Fixed problem with signature verification of texts with trailing newline
                        • -
                        • More options for pass phrase cache time to live (20, 40, 60 mins)
                        • +
                        • More options for passphrase cache time to live (20, 40, 60 mins)

                        1.0.6

                          diff --git a/OpenKeychain/src/main/res/raw-sl/help_changelog.html b/OpenKeychain/src/main/res/raw-sl/help_changelog.html index e5ff10005..d9da74e00 100644 --- a/OpenKeychain/src/main/res/raw-sl/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-sl/help_changelog.html @@ -7,11 +7,12 @@
                        • Key edit: awesome new design, key revocation
                        • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                        • New first time screen
                        • -
                        • New create key screen: autocompletion of name and email based on your personal Android accounts
                        • +
                        • New key creation screen: autocompletion of name and email based on your personal Android accounts
                        • File encryption: awesome new design, support for encrypting multiple files
                        • New icons to show status of key (by Brennan Novak)
                        • Important bug fix: Importing of large key collections from a file is now possible
                        • Notification showing cached passphrases
                        • +
                        • Keys are connected to Android's contacts

                        This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                        @@ -38,7 +39,7 @@

                        2.5

                        • Fix decryption of symmetric pgp messages/files
                        • -
                        • Refactored edit key screen (thanks to Ash Hughes)
                        • +
                        • Refactored key edit screen (thanks to Ash Hughes)
                        • New modern design for encrypt/decrypt screens
                        • API OpenPGP, verzija 3 (podpora za več API računov, interni popravki, iskanje ključev)
                        @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                        • Basic keyserver support
                        • App2sd
                        • -
                        • More choices for pass phrase cache: 1, 2, 4, 8, hours
                        • +
                        • More choices for passphrase cache: 1, 2, 4, 8, hours
                        • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                        • Bugfixes
                        • Optimizations
                        • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                          1.0.7

                          • Fixed problem with signature verification of texts with trailing newline
                          • -
                          • More options for pass phrase cache time to live (20, 40, 60 mins)
                          • +
                          • More options for passphrase cache time to live (20, 40, 60 mins)

                          1.0.6

                            diff --git a/OpenKeychain/src/main/res/raw-tr/help_changelog.html b/OpenKeychain/src/main/res/raw-tr/help_changelog.html index e27ac7475..41ec2cad6 100644 --- a/OpenKeychain/src/main/res/raw-tr/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-tr/help_changelog.html @@ -7,11 +7,12 @@
                          • Key edit: awesome new design, key revocation
                          • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                          • New first time screen
                          • -
                          • New create key screen: autocompletion of name and email based on your personal Android accounts
                          • +
                          • New key creation screen: autocompletion of name and email based on your personal Android accounts
                          • File encryption: awesome new design, support for encrypting multiple files
                          • New icons to show status of key (by Brennan Novak)
                          • Important bug fix: Importing of large key collections from a file is now possible
                          • Notification showing cached passphrases
                          • +
                          • Keys are connected to Android's contacts

                          This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                          @@ -38,7 +39,7 @@

                          2.5

                          • Fix decryption of symmetric pgp messages/files
                          • -
                          • Refactored edit key screen (thanks to Ash Hughes)
                          • +
                          • Refactored key edit screen (thanks to Ash Hughes)
                          • New modern design for encrypt/decrypt screens
                          • OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
                          @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                          • Basic keyserver support
                          • App2sd
                          • -
                          • More choices for pass phrase cache: 1, 2, 4, 8, hours
                          • +
                          • More choices for passphrase cache: 1, 2, 4, 8, hours
                          • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                          • Bugfixes
                          • Optimizations
                          • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                            1.0.7

                            • Fixed problem with signature verification of texts with trailing newline
                            • -
                            • More options for pass phrase cache time to live (20, 40, 60 mins)
                            • +
                            • More options for passphrase cache time to live (20, 40, 60 mins)

                            1.0.6

                              diff --git a/OpenKeychain/src/main/res/raw-uk/help_about.html b/OpenKeychain/src/main/res/raw-uk/help_about.html index 92fd7db13..106e7cabc 100644 --- a/OpenKeychain/src/main/res/raw-uk/help_about.html +++ b/OpenKeychain/src/main/res/raw-uk/help_about.html @@ -5,14 +5,14 @@

                              OpenKeychain імплементація OpenPGP для Андроїду.

                              Ліцензія: GPLv3+

                              -

                              Developers

                              +

                              Розробники

                                -
                              • Dominik Schürmann (Maintainer)
                              • -
                              • Art O Cathain
                              • -
                              • Ash Hughes
                              • +
                              • Домінік Шурман (супроводжувач)
                              • +
                              • Арт О'Катен
                              • +
                              • Еш Гуджес
                              • Браян С. Барнс
                              • -
                              • Bahtiar 'kalkin' Gadimov
                              • -
                              • Daniel Albert
                              • +
                              • Бахтіяр 'kalkin' Ґадімов
                              • +
                              • Даніель Альберт
                              • Даніель Гаман
                              • Даніель Габ
                              • Ґреґ Вітчак
                              • @@ -34,13 +34,13 @@
                              • Бібліотека підтримки Android в.7 'appcompat' (Ліцензія Apache в.2)
                              • -KeybaseLib (Apache License v2)
                              • +KeybaseLib (ліцензія Apache в2)
                              • -SuperToasts (Apache License v2)
                              • +SuperToasts (ліцензія Apache в2)
                              • -TokenAutoComplete (Apache License v2)
                              • +TokenAutoComplete (ліцензія Apache в2)
                              • -MiniDNS (Apache License v2)
                              • +MiniDNS (ліцензія Apache в2)
                              • StickyListHeaders (ліцензія Apache в. 2)
                              • diff --git a/OpenKeychain/src/main/res/raw-uk/help_changelog.html b/OpenKeychain/src/main/res/raw-uk/help_changelog.html index 7ecb25c2b..1837a243e 100644 --- a/OpenKeychain/src/main/res/raw-uk/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-uk/help_changelog.html @@ -3,17 +3,18 @@

                                2.8

                                  -
                                • So many bugs have been fixed in this release that we focus on the main new features
                                • -
                                • Key edit: awesome new design, key revocation
                                • -
                                • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                                • -
                                • New first time screen
                                • -
                                • New create key screen: autocompletion of name and email based on your personal Android accounts
                                • -
                                • File encryption: awesome new design, support for encrypting multiple files
                                • -
                                • New icons to show status of key (by Brennan Novak)
                                • -
                                • Important bug fix: Importing of large key collections from a file is now possible
                                • -
                                • Notification showing cached passphrases
                                • +
                                • У цій версії виправлено надто багато вад, тому ви зосередилися на основних нових функціях
                                • +
                                • Редагування ключа: новий вражаючий дизайн, відкликання ключа
                                • +
                                • Імпорт ключа: новий вражаючий дизайн, безпечні з'єднання із сервером ключів через hkps, вирішення сервера ключів через записи DNS SRV
                                • +
                                • Новий екран першого запуску
                                • +
                                • Новий екран створення ключа: автозавершення імені та ел. пошти на основі вашого особистого профілю на Андроїді
                                • +
                                • Шифрування файлу: новий вражаючий дизайн, підтримка шифрування кількох файлів
                                • +
                                • Нові піктограми для показу стану ключа (від Бренана Новака)
                                • +
                                • Виправлення важливої вади: зараз став можливим імпорт великих збірок ключів із файлу
                                • +
                                • Сповіщення для показу кешованих парольних фраз
                                • +
                                • Keys are connected to Android's contacts
                                -

                                This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                                +

                                Ця версія була б неможливою без роботи Вінсента Брейтмозера (GSoC 2014), mar-v-in (GSoC 2014), Даніеля Альберта, Арт О'Катен, Даніеля Хасс, Тіма Брея, Thialfihar

                                2.7

                                  @@ -25,21 +26,21 @@

                                2.6.1

                                  -
                                • Some fixes for regression bugs
                                • +
                                • Деякі виправлення для накопичених вад

                                2.6

                                  -
                                • Key certifications (thanks to Vincent Breitmoser)
                                • -
                                • Support for GnuPG partial secret keys (thanks to Vincent Breitmoser)
                                • -
                                • New design for signature verification
                                • -
                                • Custom key length (thanks to Greg Witczak)
                                • -
                                • Fix share-functionality from other apps
                                • +
                                • Сертифікації ключів (завдяки Вінсенту Бреймозеру)
                                • +
                                • Підтримка часткових секретних ключів для GnuPG (завдяки Вінсенту Брейтмозеру)
                                • +
                                • Новий дизайн для перевірки підпису
                                • +
                                • Власна довжина ключа (завдяки Ґреґу Вітчаку)
                                • +
                                • Виправлено функцію поширення з інших програм

                                2.5

                                  -
                                • Fix decryption of symmetric pgp messages/files
                                • -
                                • Refactored edit key screen (thanks to Ash Hughes)
                                • -
                                • New modern design for encrypt/decrypt screens
                                • +
                                • Виправлено опис симетричних повідомлень/файлів pgp
                                • +
                                • Перероблено екран редагування ключа (завдяки Ash Hughes)
                                • +
                                • Новий сучасний дизайн для екранів шифрування/розшифрування
                                • OpenPGP API версія 3 (підтримка кількох профілів, внутрішні зміни, пошук ключа)

                                2.4

                                @@ -47,45 +48,45 @@ Крім окремих незначних латок, значне число латок зробили наступні люди (у алфавітному порядку): Даніель Гаман, Даніель Габ, Ґреґ Вітчак, Міроджін Бакші, Ніхіл Петер Радж, Пауль Сарбіновський, Срірам Бояпаті, Вінсент Брейтмосер.

                                  -
                                • New unified key list
                                • -
                                • Colorized key fingerprint
                                • -
                                • Support for keyserver ports
                                • -
                                • Deactivate possibility to generate weak keys
                                • -
                                • Much more internal work on the API
                                • -
                                • Certify user ids
                                • -
                                • Keyserver query based on machine-readable output
                                • -
                                • Lock navigation drawer on tablets
                                • -
                                • Suggestions for emails on creation of keys
                                • -
                                • Search in public key lists
                                • -
                                • And much more improvements and fixes…
                                • +
                                • Новий єдиний перелік ключів
                                • +
                                • Кольоровий відбиток ключа
                                • +
                                • Підтримка для портів сервера ключів
                                • +
                                • Деактивувати можливість генерувати слабкі ключі
                                • +
                                • Набагато більше внутрішньої роботи на API
                                • +
                                • Сертифікувано ідентифікатори користувача
                                • +
                                • Запит сервера ключів на основі машиночитабельного виводу
                                • +
                                • Блокування панелі навігації на планшетах
                                • +
                                • Пропозиції для листів при створенні ключів
                                • +
                                • Пошук у списках відкритих ключів
                                • +
                                • І багато інших покращень та виправлень…

                                2.3.1

                                  -
                                • Hotfix for crash when upgrading from old versions
                                • +
                                • Свіже виправлення збою при оновленні із старих версій

                                2.3

                                  -
                                • Remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
                                • -
                                • Fix setting expiry dates on keys (thanks to Ash Hughes)
                                • -
                                • More internal fixes when editing keys (thanks to Ash Hughes)
                                • -
                                • Querying keyservers directly from the import screen
                                • -
                                • Fix layout and dialog style on Android 2.2-3.0
                                • -
                                • Fix crash on keys with empty user ids
                                • -
                                • Fix crash and empty lists when coming back from signing screen
                                • +
                                • Видалений непотрібний експорт публічного ключа при експорті секретного ключа (завдяки Ash Hughes)
                                • +
                                • Виправлено налаштування дат дії ключів (завдяки Ash Hughes)
                                • +
                                • Більше внутрішніх виправлень при редагуванні ключів (завдяки Ash Hughes)
                                • +
                                • Сервери запитаного ключа безпосередньо з екрану імпорту
                                • +
                                • Виправлено стиль розмітки і діалогу у Андроїд 2.2-3.0
                                • +
                                • Виправлено збої, коли ключ мав порожній ідентифікатор користувача
                                • +
                                • Виправлено збої та порожні списки при поверненні з екрану реєстрації
                                • Bouncy Castle (криптографічна бібліотека) оновлена з версії 1.47 до 1.50 та зібрана з коду
                                • -
                                • Fix upload of key from signing screen
                                • +
                                • Виправлено завантаження ключа з вікна реєстрації

                                2.2

                                  -
                                • New design with navigation drawer
                                • -
                                • New public key list design
                                • -
                                • New public key view
                                • -
                                • Bug fixes for importing of keys
                                • -
                                • Key cross-certification (thanks to Ash Hughes)
                                • -
                                • Handle UTF-8 passwords properly (thanks to Ash Hughes)
                                • -
                                • First version with new languages (thanks to the contributors on Transifex)
                                • -
                                • Sharing of keys via QR Codes fixed and improved
                                • -
                                • Package signature verification for API
                                • +
                                • Новий дизайн з бічною панеллю
                                • +
                                • Новий дизайн списку ключів
                                • +
                                • Новий вид перегляду ключа
                                • +
                                • Виправлення помилок імпорту ключів
                                • +
                                • Крос-сертифікація ключів (завдяки Ash Hughes)
                                • +
                                • Правильна обробка паролів в UTF-8 (завдяки Ash Hughes)
                                • +
                                • Перша версія з новими мовами (завдяки перекладачам на Transifex)
                                • +
                                • Виправлення і поліпшення передачі ключів через QR коди
                                • +
                                • Перевірка підписів пакетів для API

                                2.1.1

                                  @@ -93,65 +94,65 @@

                                2.1

                                  -
                                • Lots of bug fixes
                                • -
                                • New API for developers
                                • +
                                • Безліч виправлень помилок
                                • +
                                • Новий API для розробників
                                • Виправлення вади генератора випадкових чисел від Google

                                2.0

                                  -
                                • Complete redesign
                                • -
                                • Share public keys via qr codes, nfc beam
                                • -
                                • Sign keys
                                • -
                                • Upload keys to server
                                • -
                                • Fixes import issues
                                • -
                                • New AIDL API
                                • +
                                • Переробка дизайну
                                • +
                                • Передача ключів через QR-коди і NFC
                                • +
                                • Підписати ключ
                                • +
                                • Завантаження на сервер ключів
                                • +
                                • Виправлення проблем імпорту
                                • +
                                • Новий AIDL API

                                1.0.8

                                  -
                                • Basic keyserver support
                                • +
                                • Підтримка сервера основних ключів
                                • App2sd
                                • -
                                • More choices for pass phrase cache: 1, 2, 4, 8, hours
                                • -
                                • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                                • -
                                • Bugfixes
                                • -
                                • Optimizations
                                • +
                                • Більше варіантів збереження кешу пароля: 1, 2, 4, 8 годин
                                • +
                                • Переклади: норвезькою (завдяки Сандер Даніельсен), китайською (завдяки Чжан Фредріку)
                                • +
                                • Виправлення вад
                                • +
                                • Оптимізації

                                1.0.7

                                  -
                                • Fixed problem with signature verification of texts with trailing newline
                                • -
                                • More options for pass phrase cache time to live (20, 40, 60 mins)
                                • +
                                • Виправлення помилки при перевірці підпису тексту з переведенням рядка
                                • +
                                • Більше варіантів збереження кешу пароля (20, 40, 60 хвилин)

                                1.0.6

                                  -
                                • Account adding crash on Froyo fixed
                                • -
                                • Secure file deletion
                                • -
                                • Option to delete key file after import
                                • -
                                • Stream encryption/decryption (gallery, etc.)
                                • -
                                • New options (language, force v3 signatures)
                                • -
                                • Interface changes
                                • -
                                • Bugfixes
                                • +
                                • Виправлення помилки створення запису на Froyo
                                • +
                                • Вилучення безпечного файлу
                                • +
                                • Вилучення файлу ключа після імпорту
                                • +
                                • Передача шифрування (галерея і т. д.)
                                • +
                                • Нові можливості (мова, примусові v3 підписи)
                                • +
                                • Зміни інтерфейсу
                                • +
                                • Виправлення вад

                                1.0.5

                                • Німецький та італійський переклад
                                • -
                                • Much smaller package, due to reduced BC sources
                                • -
                                • New preferences GUI
                                • -
                                • Layout adjustment for localization
                                • -
                                • Signature bugfix
                                • +
                                • Істотно менший пакунок програми завдяки зменшенню джерел
                                • +
                                • Нові налаштунки інтерфейсу
                                • +
                                • Зміна розмітки для локалізації
                                • +
                                • Виправлення помилки підпису

                                1.0.4

                                  -
                                • Fixed another crash caused by some SDK bug with query builder
                                • +
                                • Виправлення ще однієї помилки, що виникає в SDK

                                1.0.3

                                  -
                                • Fixed crashes during encryption/signing and possibly key export
                                • +
                                • Виправлення помилок при шифруванні/підписанні та експорті ключів

                                1.0.2

                                  -
                                • Filterable key lists
                                • -
                                • Smarter pre-selection of encryption keys
                                • -
                                • New Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
                                • -
                                • Fixes and additional features (key preselection) for K-9 Mail, new beta build available
                                • +
                                • Фільтр списку ключів
                                • +
                                • Поліпшення вибору ключів шифрування
                                • +
                                • Додана можливість шифрувати файли прямо з файлового менеджера
                                • +
                                • Виправлення помилок і нові можливості (попередній вибір ключа) для інтеграції з K-9 Mail, нова бета-збірка доступна

                                1.0.1

                                  @@ -160,11 +161,11 @@

                                  1.0.0

                                  • інтеграція з K-9 Mail, APG підтримує бета-збірку K-9 Mail
                                  • -
                                  • Support of more file managers (including ASTRO)
                                  • +
                                  • Підтримка сторонніх файлових менеджерів (в т.ч. ASTRO)
                                  • Словенський переклад
                                  • -
                                  • New database, much faster, less memory usage
                                  • -
                                  • Defined Intents and content provider for other apps
                                  • -
                                  • Bugfixes
                                  • +
                                  • Нова база даних, швидша робота, менше використання пам'яті
                                  • +
                                  • Додано обробники для взаємодії з іншими програмами
                                  • +
                                  • Виправлення вад
                                  diff --git a/OpenKeychain/src/main/res/raw-zh/help_changelog.html b/OpenKeychain/src/main/res/raw-zh/help_changelog.html index e27ac7475..41ec2cad6 100644 --- a/OpenKeychain/src/main/res/raw-zh/help_changelog.html +++ b/OpenKeychain/src/main/res/raw-zh/help_changelog.html @@ -7,11 +7,12 @@
                                • Key edit: awesome new design, key revocation
                                • Key import: awesome new design, secure keyserver connections via hkps, keyserver resolving via DNS SRV records
                                • New first time screen
                                • -
                                • New create key screen: autocompletion of name and email based on your personal Android accounts
                                • +
                                • New key creation screen: autocompletion of name and email based on your personal Android accounts
                                • File encryption: awesome new design, support for encrypting multiple files
                                • New icons to show status of key (by Brennan Novak)
                                • Important bug fix: Importing of large key collections from a file is now possible
                                • Notification showing cached passphrases
                                • +
                                • Keys are connected to Android's contacts

                                This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                                @@ -38,7 +39,7 @@

                                2.5

                                • Fix decryption of symmetric pgp messages/files
                                • -
                                • Refactored edit key screen (thanks to Ash Hughes)
                                • +
                                • Refactored key edit screen (thanks to Ash Hughes)
                                • New modern design for encrypt/decrypt screens
                                • OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)
                                @@ -110,7 +111,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
                                • Basic keyserver support
                                • App2sd
                                • -
                                • More choices for pass phrase cache: 1, 2, 4, 8, hours
                                • +
                                • More choices for passphrase cache: 1, 2, 4, 8, hours
                                • Translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
                                • Bugfixes
                                • Optimizations
                                • @@ -118,7 +119,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa

                                  1.0.7

                                  • Fixed problem with signature verification of texts with trailing newline
                                  • -
                                  • More options for pass phrase cache time to live (20, 40, 60 mins)
                                  • +
                                  • More options for passphrase cache time to live (20, 40, 60 mins)

                                  1.0.6

                                    diff --git a/OpenKeychain/src/main/res/raw/help_changelog.html b/OpenKeychain/src/main/res/raw/help_changelog.html index 5f1798896..c8992ddab 100644 --- a/OpenKeychain/src/main/res/raw/help_changelog.html +++ b/OpenKeychain/src/main/res/raw/help_changelog.html @@ -16,6 +16,7 @@ And don't add newlines before or after p tags because of transifex -->
                                  • New icons to show status of key (by Brennan Novak)
                                  • Important bug fix: Importing of large key collections from a file is now possible
                                  • Notification showing cached passphrases
                                  • +
                                  • Keys are connected to Android's contacts

                                  This release wouldn't be possible without the work of Vincent Breitmoser (GSoC 2014), mar-v-in (GSoC 2014), Daniel Albert, Art O Cathain, Daniel Haß, Tim Bray, Thialfihar

                                  diff --git a/OpenKeychain/src/main/res/values-cs/strings.xml b/OpenKeychain/src/main/res/values-cs/strings.xml index 19e94b4b8..5f62b9036 100644 --- a/OpenKeychain/src/main/res/values-cs/strings.xml +++ b/OpenKeychain/src/main/res/values-cs/strings.xml @@ -1,14 +1,21 @@ + Vybrat klíče + Vybrat svůj klíč Zašifrovat Rozšifrovat Heslo + Přidat podklíč Editovat klíč Možnosti Appky Nastavení keyservrů Změnit heslo + Sdílet otisk s... + Sdílet klíč s... + Sdílet soubor s... + Sdílet zprávu s... Zašifrovat do souboru Rozšifrovat do souboru Importovat klíče @@ -19,6 +26,8 @@ Certifikovat identity Detaily klíče Nápověda + Log + Vytvořit klíč Identity Podklíče @@ -26,43 +35,68 @@ Výchozí hodnoty Pokročilé Akce + Celý klíč Klíč použitý pro certifikaci Uploadovat klíč Keyserver + Otisk + Klíče k ověření + Dešifrovat, ověřit a uložit soubor + Dešifrovat a ověřit zpávu Zaširovat a uložit soubor + Zašifrovat a sdílet soubor Uložit Zrušit Smazat + Žádná expirace OK Nahrát na keyserver Další Zpět Vyhledat klíč + Zašifrovat a sdílet zprávu + Zobrazit klíč certifikátu + Vytvořit klíč + Přidat soubor(y) Nastavení Nápověda Exportovat do souboru Smazat klíč + Vytvořit moje klíče + Importovat existující klíč Hledat Beam settings Zrušit Zašifrovat do... Vybrat vše Přidat klíče + Exportovat všechny klíče + Zobrazit pokročilé info Zpráva Soubor + Soubor(y) + Soubor: Bez hesla Heslo + Opakovat heslo Algoritmus + Soubor ASCII armor + Soubory: ASCII armor + Od: + Komu: + Soubory: smazat po zašifrování Smazat po rozšifrování Šifrovací algoritmus Hashovací algoritmus + Veřejným klíčem + Heslem Cache hesel Komprimovat zprávu Komprimovat soubor - Vynutit staré OpenPGPv3 podpisy + Keyservery ID klíče Vytvořeno Expirace @@ -80,6 +114,8 @@ <žádný klíč> slouží k šifrovní slouží k podpisu + může ověřit + nemůže ověřit po epiraci revokovat Tajný klíč: @@ -119,6 +155,8 @@ Určitě smazat\n%s? Úspěšně smazáno. Nejprve vyberte soubor. + Úspěšně podepsáno a/nebo zašifrováno. + Úspěšně podepsání a/nebo zašifrováno do schránky. Heslo zadejte dvakrát. Vyberte alespoň jeden šifrovací klíč. Vyberte alespoň jeden šifrovací nebo podpisový klíč. @@ -128,14 +166,19 @@ Opravdu si přejete smazat všechny vybrané veřejné klíče?\nToto nebude možné vzít zpět! Opravdu chcete smazat TAJNÝ klíč \'%s\'?\nToto je nevratná operace! Opravdu chcete smazat veřejný klíč \'%s\'?\nToto nebude možné vrátit zpět! + Zárověň exportovat tajný klíč Úspěšně exportován 1 klíč. Úspěšně exportován %d klíč. Žádný kláč pro export. Žádný: pouze podklíče podporují ElGamal. Nemohu najít klíč %08X. Úspěšně nahráno na keyserver. + Úspěšně ověřené identity Seznam je prázný! + Úspěšně odeslaný klíč pomocí NFC Beam! Klíč byl zkopírován do schránky! + Fingerprint byl zkopírován do schránky! + Prosím vyberte klíč pro ověření! Klíč je příliš velký aby byl sdílen pomocí této metody! + Špatný podpis! + Neznámý veřejný klíč + Validní podpis (neověřen) + Validní podpis (ověřen) + Úspěšně rozšifrováno + Úspěšně rozšifrováno ale neznámý veřejný klíč + Úspěšně rozšifrováno a validní podpis (neověřen) + Úspěšně rozšifrováno a validní podpis (ověřen) Hotovo. Zrušit ukládám... importuji... exportuji... + uloaduji... vytvářím klíč... vytvářím hlavní klíč... + generuji nový RSA klíč... + generuji nový DSA klíč... + generuji nový ElGamal klíč... + modifikuji klíčenku... + odemykám klíčenku... + přidávám uživatelské IDčka... + revokuji uživatelská IDčka... + měním primární uživatelské ID... + modifikuji podklíče... + revokuji podklíče... + přidávám podklíče... + mněním heslo... extrahuji podpisový klíč... extrahuji klíč... připravuji streamy... @@ -178,13 +247,13 @@ zpracovávám podpis... verifikuji podpis... podepisuji... + ověřuji... načítám data... hledám klíč... rozbaluji data... verifikuji integritu... mažu \'%s\' bezpečně... - Jméno/Email/ID klíče 512 768 @@ -196,6 +265,7 @@ 8192 Vlastní velikost klíče Napište vlastní délku klíče (v bitech): + Délka RSA klíče musí být větší než 1024 a alespoň 16384. Zároveň musí být dělitelná 8mi. Délka DSA klíče musí být alespoň 512 a nejvýše 1024. Zároveň musí být dělitelná 64. rychle @@ -209,10 +279,19 @@ O aplikaci Verze: + Keyserver + Soubor/schránka + QR kód/NFC + Keybase.io Importovat vybrané klíče QR kód nerozpoznán! Zkuste to znovu prosím! Fingerprint je příliš krátký (< 16 znaků) + Skenovat QR kód... + Zobrazit log + Nic k importu + Chyba importu klíčů! + , s varováními Dešifrovat soubor pomocí OpenKeychain Importovat kíč pomocí OpenKeychain @@ -230,17 +309,17 @@ Uložit Zrušit Zneplatnit přístup + Spustit aplikaci Smazat účet Jméno balíčku SHA-256 z podpisu balíčku Účty Žádné účty nejsou specifikovány pro tuto appku. + Appka žádá o vytvoření nového účtu. Prosím vyberte nějaký existující klíč nebo vytvořte nový.\nAppky jsou omezeny na použití klíčů, které zde vyberete! Zobrazená appka chce zašifrovat/rozšifrovat zprávy a podepsat je vačím jménem.\nPovolit přístup?\n\nVAROVÁNÍ: Pokud nevíte proč se tato obrazovka objevila, zamítněte přístup! Také můžete zneplatnit přístup jindy v obrazovce \'Appky\'. Povolit přístup Zamítnout přístup Prosím vyberte klíč! - Nebyl nalezen žádný veřejný klíč pro tyto identity: - Existuje více jak jeden veřejný klíč pro tyto identity: Zkontrolujte prosím seznam příjemců! Selhala kontrola podpisu! Instalovali jste tuto appku z jiného zdroje? Pokud jste si jistí, že toto není útok, zneplatněte registraci této appky v OpenKeychain a poté ji znovu zaregistrujte. @@ -251,16 +330,51 @@ Můžete začít pomocí nebo vytvářím váš vlastní klíč + Importuji existující klíč. Editovat klíč Šifruji pomocí těchto klíčů. Certifikovat identity + Aktualizuji z keyserveru + Sdílet pomocí... + Sdílet přes NFC tak, že podržíte zařízení zády k sobě + Aktualizovat na keyserver Hlavní info + Sdílet + Podklíče Certifikáty + Změnit heslo + Přidat identitu + Přidat podklíč + Vyberte akci! + + Změnit na hlavní identitu + Zneplatnit identitu + + + Zrušit zneplatnění + + Tato identity byla zneplatněna. Toto není možné vzít zpět. + Vyberte akci! + + Změnit expiraci + Zneplatnit podklíč + + nový + Aktualizovat klíč na keyserver + Toto pole je vyžadováno + Heslo nesouhlasí + Zadali jste následující identitu: + Vytvoření identity může chvíli tvrvat. Mezitím si dejte šálek kávy.\n(3 podklíče, RSA, 4096 bit) + Zadejte své celé jméno, emailovou adresu a zvolte heslo. + Celé jméno, např. Jan Novák + Tento klíč byl zneplatněn! + Tento klíč vyexpiroval! + Klíče Podepisuji a šifruji Dešifruji a kontroluji Appky @@ -268,6 +382,8 @@ Zavří navigační panel Moje Klíče + Zpráva zadaná sem bude podepsána za použití klíče vybraného v poli \'Od\' a zašifrovaná pro všecny příjemce vybrané v \'Pro\'. + Vložte zašifrovaný text pro jeho rozšifrování a/nebo ověření... výchozí žádný @@ -284,6 +400,7 @@ + Identita diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index a843a154a..6c5e12266 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -6,10 +6,11 @@ Verschlüsseln Entschlüsseln Passwort + Unterschlüssel hinzufügen Schlüssel bearbeiten Einstellungen Apps - Schlüsselserver Einstellungen + Schlüsselserver Passwort ändern Teile Fingerabdruck über… Teile Schlüssel über... @@ -26,6 +27,7 @@ Schlüsseldetails Hilfe Log + Schlüssel erzeugen Identitäten Unterschlüssel @@ -47,12 +49,13 @@ Speichern Abbrechen Löschen + Kein Ablaufdatum Okay Auf Schlüsselserver hochladen Weiter Zurück Schlüssel nachschlagen - Entschlüsseln und teilen + Verschlüsseln und teilen Beglaubigungsschlüssel anzeigen Schlüssel erzeugen Datei(en) hinzufügen @@ -70,7 +73,7 @@ Alles auswählen Schlüssel hinzufügen Alle Schlüssel exportieren - Erweiterte Info anzeigen + Erweiterte Infos anzeigen Nachricht Datei @@ -80,9 +83,10 @@ Passwort Passwort wiederholen Algorithmus + Datei: ASCII Armor Datei: ASCII Armor Lass andere wissen dass du OpenKeychain nutzt - Fügt \'OpenKeychain v2.7\' zu OpenPGP Signaturen, Daten und exportierten Schlüsseln hinzu + Fügt \'OpenKeychain v2.7\' zu OpenPGP Signaturen, DAten und exportierten Schlüsseln hinzu Von: An: Dateien: Nach Verschlüsselung löschen @@ -94,7 +98,6 @@ Passwort-Cache Nachrichten-Komprimierung Datei-Komprimierung - Erzwinge alte OpenPGPv3-Signaturen Schlüsselserver Schlüssel-ID Erstellungsdatum @@ -113,8 +116,8 @@ <kein Schlüssel> kann verschlüsseln kann signieren - kann bestätigen - kann nicht bestätigen + kann beglaubigen + kann nicht beglaubigen abgelaufen widerrufen @@ -167,9 +170,9 @@ Das Passwort bitte zweimal eingeben. Mindestens einen Schlüssel zum Verschlüsseln auswählen. Mindestens einen Schlüssel zum Verschlüsseln oder einen zum Signieren auswählen. - Bitte angeben, in welche Datei verschlüsselt werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert. - Bitte angeben, in welche Datei entschlüsselt werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert. - Bitte angeben, in welche Datei exportiert werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert. + Bitte angeben in welche Datei verschlüsselt werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert. + Bitte angeben,in welche Datei entschlüsselt werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert. + Bitte angeben in welche Datei exportiert werden soll.\nWARNUNG: Datei wird überschrieben, wenn sie bereits existiert. Möchtest du wirklich alle ausgewählten öffentlichen Schlüssel löschen?\nDies kann nicht rückgängig gemacht werden! Soll der PRIVATE Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! Soll der öffentliche Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! @@ -234,22 +237,24 @@ Fertig. Abbrechen - wird gespeichert… - wird importiert… - wird exportiert… - hochladen... + Wird gespeichert… + Wird importiert… + Wird exportiert… + Wird hochgeladen... Schlüssel wird erstellt… Hauptschlüssel wird erstellt… - erzeuge neuen RSA Schlüssel... - erzeuge neuen DSA Schlüssel... - erzeuge neuen ElGamal Schlüssel... - verändere Schlüsselbund... - entsperre Schlüsselbund... - füge User ID hinzu... - ändere Haupt User ID... - verändere Unterschlüssel... - füge Unterschlüssel hinzu... - ändere Passwort... + Erzeuge neuen RSA-Schlüssel... + Erzeuge neuen DSA-Schlüssel... + Erzeuge neuen ElGamal-Schlüssel... + Schlüsselbund wird verändert... + Schlüsselbund wird entsperrt... + User-ID wird hinzugefügt... + User-IDs werden widerrufen... + Primäre User-ID wird geändert... + Unterschlüssel werden verändert... + Unterschlüssel wird widerrufen... + Unterschlüssel wird hinzugefügt... + Passwort wird geändert... Schlüssel wird exportiert… Schlüssel werden exportiert… @@ -263,15 +268,14 @@ Signatur wird erstellt… Signatur wird verarbeitet… Signatur wird verifiziert… - wird signiert… + Wird signiert… + Wird beglaubige... Daten werden gelesen… Schlüssel wird gesucht… Daten werden entpackt… Integrität wird überprüft… \'%s\' wird sicher gelöscht… - Name/Email/Schlüssel ID... - Name/Keybase.io Benutzername... 512 768 @@ -283,6 +287,7 @@ 8192 Benutzerdefinierte Schlüssellänge Benutzerdefinierte Schlüssellänge (in Bit): + Die RSA-Schlüssellänge muss grösser als 1024 und höchstens 16384 sein. Sie muss auch ein Mehrfaches von 8 sein. Die DSA-Schlüssellänge muss mindestens 512 und höchstens 1024 sein. Auch muss sie ein Mehrfaches von 64 sein. schnell @@ -298,12 +303,12 @@ Schlüsselserver Datei/Zwischenablage - QR Kode/NFC + QR-Code/NFC Keybase.io Ausgewählte Schlüssel importieren Falsch formatierter QR-Code! Bitte erneut versuchen! Der Fingerabdruck ist zu kurz (< 16 Zeichen) - QR Kode Einlesen... + QR-Code Einlesen... 1 Schlüssel erfolgreich importiert @@ -344,12 +349,11 @@ SHA-256 der Paketsignatur Konten Keine Konten mit dieser Anwendung verknüpft. - Folgende Anwendung möchte Nachrichten ver-/entschlüsseln und in Ihrem Namen signieren\nZugriff erlauben?\n\nVORSICHT: Sollten Sie nicht wissen warum dieses Fenster erscheint, sollten Sie den Zugriff nicht gewähren! Sie können Zugriffe später über das Menü \'Anwendungen\' widerrufen. + Die App verlangt die Erstellung eines neuen Accounts. Bitte wähle einen deiner existierenden Schlüssel aus oder erstelle einen neuen.\nApps können nur hier ausgewählte Schlüssel nutzen. + Folgende Anwendung möchte Nachrichten ver-/entschlüsseln und in Ihrem Namen signieren.\nZugriff erlauben?\n\nVORSICHT: Sollten Sie nicht wissen warum dieses Fenster erscheint, sollten Sie den Zugriff verbieten! Sie können Zugriffe später über das Menü \'Apps\' widerrufen. Zugriff erlauben Zugriff verbieten Bitte einen Schlüssel auswählen! - Für diese Identität wurden keine öffentlichen Schlüssel gefunden: - Für diese Identität existieren mehrere öffentliche Schlüssel: Bitte die Liste der Empfänger überprüfen! Signaturüberprüfung fehlgeschlagen! Haben Sie diese Anwendung aus einer anderen Quelle installiert? Wenn Sie eine Attacke ausschliessen können, sollten Sie die Registrierung der App in OpenKeychain widerrufen und die Anwendung erneut registrieren. @@ -364,21 +368,33 @@ Du kannst anfangen OpenKeychain zu benutzen indem du oder deinen eigenen Schlüssel erstellst + Importiere einen existierenden Schlüssel. Schlüssel bearbeiten Mit diesem Schlüssel verschlüsseln Identitäten beglaubigen + Von Schlüsselserver aktualisieren Teilen über... Teilen über NFC (Geräte Rückseite an Rückseite halten) + Auf Schlüsselserver hochladen Informationen Teilen Unterschlüssel Beglaubigungen + Wiederrufen + Ungültig + Passwort ändern Identität hinzufügen Unterschlüssel hinzufügen + neu + Schlüssel auf Schlüsselserver hochladen + Passwörter stimmen nicht überein + Vollständiger Name, z.B. Max Mustermann + Dieser Schlüssel wurde widerrufen! + Dieser Schlüssel ist nicht mehr gültig! Schlüssel Signieren und Verschlüsseln @@ -396,25 +412,38 @@ einfach überprüft positiv widerrufen + OK fehlgeschlagen! Fehler! Schlüssel nicht verfügbar Alte Schlüssel aus der Datenbank löschen Die Anwendung ist wegen Kodierungsfehler fehlgeschlagen - Die Anwendung is wegen Eingabe/Ausgabe-Fehler fehlgeschlagen + Die Anwendung ist wegen eines Eingabe/Ausgabe-Fehlers fehlgeschlagen Die Anwendung ist wegen internen Fehler fehlgeschlagen + Importiere öffentlichen Schlüsselbund %s Schlüsselbund-daten werden kodiert Datenbank-Transaktionen werden vorbereitet + Hauptschlüssel %s wird verarbeitet Unterschlüssel %s wird bearbeitet + Benutzerkennung wurde widerrufen + Datenbankfehler! Geheime Unterschlüssel werden bearbeitet + Schlüsselbund enthält keine neuen Daten, es gibt nichts zu tun. + Geheimer Schlüsselbund erfolgreich importiert + Verarbeite Unterschlüssel %s + Ändere primäre Benutzerkenung auf %s + Widerrufe Benutzerkennung %s + Benutzerkennung darf nicht leer sein! + + Interner Fehler! Beglaubiger Zertifikatdetails Identität diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index 80c2060d1..16d60dc82 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -27,6 +27,7 @@ Detalles de la clave Ayuda Registro (log) + Crear clave Identidades Subclaves @@ -84,8 +85,8 @@ Algoritmo Armadura ASCII del fichero Ficheros: Armadura ASCII - Permitir a otros saber que está usando OpenKeychain - Escribe \'OpenKeychain v2.7\' en las firmas OpenPGP, el texto cifrado, y las claves exportadas + Permitir conocer a otros que usted está usando OpenKeychain + Escribe \'OpenKeychain v2.7\' en las firmas OpenPGP, texto cifrado, y claves exportadas Desde: Hacia: Ficheros: Borrar después del cifrado @@ -97,7 +98,6 @@ Caché de frase de contraseña Compresión de mensaje Compresión de archivo - Forzar firmas OpenPGPv3 antiguas Servidores de claves ID de clave Creación @@ -275,9 +275,12 @@ descomprimiendo los datos... verificando la integridad... borrando \'%s\' de forma segura… + borrando claves... + consolidación: guardando en caché... + consolidación: reimportando - Identidad de Nombre/Correo/Clave... - Nombre de usuario de Nombre/Keybase.io... + Nombre/Correo electrónico/Identidad de clave... + Nombre/Correo electrónico/Prueba/Clave... 512 768 @@ -356,13 +359,15 @@ Cuentas No hay cuentas adjuntas a esta aplicación La aplicación solicita la creación de una nueva cuenta. Por favor, seleccione una de sus claves existentes o cree una nueva.\n¡Las aplicaciones están restringidas al uso de las claves que usted seleccione aquí! + La clave guardada para esta cuenta ha sido borrada. ¡Por favor seleccione una diferente!\n¡Las aplicaciones están restringidas al uso de las claves que seleccione aquí! La aplicación mostrada quiere cifrar, descrifrar y firmar mensajes en su nombre.\n¿Permitir acceso?\n\nADVERTENCIA: Si no sabe por qué apareció esta pantalla, ¡no permita el acceso! Puede revocar el acceso más tarde usando la pantalla de \'Aplicaciones\'. Permitir el acceso Denegar el acceso ¡Por favor, selecciona una clave! - No se encontraron claves públicas para estas identidades: - Existe más de una clave pública para estas identidades: + No se encontraron claves para estas identidades: + Existe más de una clave para estas identidades: ¡Por favor, revisa la lista de destinatarios! + ¡Por favor seleccione los receptores! ¡La comprobación de la firma ha fallado! ¿Has instalado esta app desde una fuente distinta? Si estás seguro de que esto no es un ataque, revoca el registro de esta app en OpenKeychain y regístrala de nuevo. Compartir con código QR @@ -389,6 +394,14 @@ Compartir Subclaves Certificados + Revocada + Esta identidad ha sido revocada por el propietario de la clave. En adelante no es válida. + Verificada + Esta identidad ha sido verificada + No verificada + Esta identidad no se ha verificado aún. No puede estar seguro de si la identidad realmente corresponde a una persona en concreto. + No válido + ¡Algo está mal con esta identidad! Cambiar frase contraseña Añadir identidad @@ -408,6 +421,7 @@ Revocar subclave nueva + ¡Por favor, seleccione al menos un indicador! Subir clave al servidor de claves Este campo es obligatorio @@ -443,6 +457,7 @@ Aplicando operación de inserción por lote. Se intentó importar un juego de claves (keyring) secreto como público. Esto es un fallo, por favor ¡consigne un informe! + Esta clave es una clave OpenPGP versión 3 y por tanto insegura. No ha sido importada. No se borró ninguna clave antigua (¿crear una nueva?) Clave antigua borrada de la base de datos La operación falló debido a un error de codificación @@ -548,7 +563,7 @@ Eliminando identidad de usuario con marca de tiempo futura Eliminando certificado de identidad de usuario, de tipo desconocido (%s) Eliminando auto-certificado defectuoso para la identidad de usuario \"%s\" - Eliminando auto-certificado desactualizado para el identificador de usuario \"%s\" + Eliminando auto-certificado desactualizado para el identificador de usuario \"%s\" Eliminando certificado ajeno de identidad de usuario por %s Eliminando certificado redundate de revocación para la identidad de usuario \"%s\" Eliminando certificado desactualizado de revocación para la identidad de usuario \"%s\" @@ -557,8 +572,8 @@ Incorporándolas en el juego de claves públicas %s Incorporándolas en el juego de claves secretas (privadas) %s - Error fatal codificando la firma - Se intentaron consolidar juegos de claves heterogéneos + Error fatal codificando la firma + ¡Se intentaron fusionar juegos de claves con diferentes huellas de validación de claves! Añadiendo nueva subclave %s Se encontraron %s nuevos certificados en el juego de claves No hay nuevos certificados @@ -567,6 +582,7 @@ ¡No se especificaron opciones de clave! ¡Los juegos de claves tienen que estar creados con al menos una identidad de usuario! ¡La clave maestra debe tener el indicador de certificado! + El periodo hasta la expiración no puede ser \"el mismo que antes\" al crear la clave. Esto es un error de programación, por favor ¡rellene un informe de fallo! ¡El tamaño de la clave debe ser mayor o igual de 512! ¡Error PGP interno! ¡Seleccionado algoritmo defectuoso! @@ -577,18 +593,26 @@ ¡La actual huella de validación de clave no coincide con la esperada! No hay identidad de clave. Esto es un error interno, por favor ¡consigne un informe de error! Error interno, ¡fallo en la comprobación de integridad! + ¡No se encontró certificado maestro sobre el que operar! (¿Todos revocados?) ¡Especificada identidad de usuario primaria defectuosa! + ¡Se especificó una mala identidad de usuario para revocación! ¡Las identidades de usuario revocadas no pueden ser primarias! + El periodo hasta la expiración no puede ser \"el mismo que antes\" al crear subclave. Esto es un error de programación, por favor ¡rellene un informe de fallo! + ¡Error fatal descrifrando la clave maestra! Probablemente esto se daba a un error de programación, por favor ¡rellene un informe de fallo! ¡Excepción interna de PGP! ¡Excepción con la firma! - Cambiando frase contraseña + Modificando certificaciones maestras + Cambiando frase contraseña para el juego de claves (keyring)... + Re-cifrando subclave %s con nueva frase contraseña + Fallo al establecer nueva frase contraseña, intentándolo de nuevo con una antigua frase contraseña vacía + ¡La frase contraseña para la subclave no pudo cambiarse! (¿Tiene la subclave una diferente de la de las otras claves?) Reemplazando certificado de la anterior identidad de usuario primaria Generando nuevo certificado para nueva identidad de usuario primaria Modificando subclave %s - ¡Intentó operar sobre una subclave ausente %s! - Generando nueva subclave %2$s de %1$s bits + ¡Se intentó operar sobre una subclave desaparecida %s! + Añadiendo nueva subclave de tipo %2$s (%1$s bits) Nueva identidad de subclave: %s - ¡La fecha de expiración no puede ser del pasado! + ¡La fecha de expiración no puede ser del pasado! Revocando subclave %s Juego de claves modificado con éxito Añadiendo identidad de usuario %s @@ -596,7 +620,41 @@ Revocando identidad de usuario %s ¡La identidad de usuario no debe estar vacía! ¡Error desbloqueando juego de claves! - Desbloqueando juego de claves + Desbloqueando juego de claves (keyring) + + Consolidando base de datos + ¡La consolidación se inició cuando ninguna base de datos estaba cacheada! Probablemente esto es un error de programación, por favor consigne un informe de fallo. + ¡Consolidación abortada, ejecutándose ya en otro hilo! + Guardando juegos de claves secretas (privadas) + Guardando juegos de claves públicas + Limpiando base de datos + Base de datos consolidada con éxito + Entrando en fase crítica + Abandonando fase crítica + Borrando fichero de caché de juego de claves públicas + Borrando fichero de caché de juego de claves secretas (privadas) + ¡Error al abrir la base de datos! + ¡Error de E/S al escribir claves públicas en caché! + ¡Error de E/S al escribir claves secretas (privadas) en caché! + ¡Error al reimportar claves públicas! + ¡Error reimportando claves secretas (privadas)! + + Recuperando consolidación con %1$d clave secreta (privada) y %2$d pública + Recuperando consolidación con %1$d claves secretas (privadas) y %2$d públicas + + Recuperando desde un estado desconocido + + Reimportando una clave pública + Reimportando %d claves públicas + + No hay claves públicas a reimportar, omitiendo... + + Reimportando una clave secreta (privada) + Reimportando %d claves secretas (privadas) + + No hay claves públicas a reimportar, omitiendo... + Excepción borrando fichero de caché de claves públicas + Excepción al borrar fichero de caché de claves secretas (privadas) Haga clic para limpiar las frases contraseña almacenadas en caché OpenKeychain ha almacenado en caché %d frases contraseña @@ -610,6 +668,7 @@ Identidad <desconocido> No hay certificados para esta clave + Sólo los auto-certificados y los certificados creados con las claves de usted se muestran aquí. Identidades a certificar Razón de la revocación Estado de la verificación diff --git a/OpenKeychain/src/main/res/values-et/strings.xml b/OpenKeychain/src/main/res/values-et/strings.xml index fdd7f026a..6ea0d1236 100644 --- a/OpenKeychain/src/main/res/values-et/strings.xml +++ b/OpenKeychain/src/main/res/values-et/strings.xml @@ -97,6 +97,7 @@ + diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index e30ebb883..f383ac40b 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -27,6 +27,7 @@ Détails sur la clef Aide Journal + Créer une clef identités Sous-clefs @@ -84,8 +85,8 @@ Algorithme Fichier ASCII Armor Fichier : ASCII Armor - Informez les autres de votre utilisation d\'OpenKeychain - Ajoute « OpenKeychain v2.7 » aux signatures OpenPGP, aux cryptogrammes, et aux clefs exportées + Faire savoir aux autres que vous utilisez OpenKeychain + Ajoute « OpenKeychain v2.7 » aux signatures OpenPGP, aux cryptogrammes et aux clefs exportées De : À : Fichier : supprimer après chiffrement @@ -97,9 +98,8 @@ Cache de la phrase de passe Compression des messages Compression des fichiers - Forcer les anciennes signatures OpenPGP v3 Serveurs de clefs - ID de le clef + ID de clef Création Expiration Utilisation @@ -248,8 +248,8 @@ génération d\'une nouvelle clef ElGamal... modification du trousseau... déverrouillage du trousseau... - ajout des IDs d\'utilisateur... - révocation des IDs d\'utilisateur... + ajout des ID d\'utilisateur... + révocation des ID d\'utilisateur... changement de l\'ID d\'utilisateur principale... modification des sous-clefs... révocation des sous-clefs... @@ -275,9 +275,12 @@ décompression des données... vérification de l\'intégrité... suppression sûre de « %s »... + suppression des clefs... + consolider : enregistrement dans le cache... + consolider : réimportation... - Nom/courriel/ ID clef... - Nom/nom d\'utilisateur keybase.io... + Nom/courriel/ID de clef... + Nom/courriel/preuve/clef... 512 768 @@ -356,13 +359,15 @@ Comptes Aucun compte n\'est attaché à cette appli. L\'appli demande la création d\'un nouveau compte. Veuillez choisir une de vos clefs existantes ou en créer une nouvelle.\nLes applis ne peuvent utiliser que les clefs que vous choisissez ici ! + La clef enregistrée pour ce compte a été supprimée. Veuillez en choisir une autre !\nLes applis sont restreintes à l\'utilisation de clefs choisies ici. L\'application affichée veut chiffrer/déchiffrer des messages et les signer en votre nom.\nPermettre l\'accès ?\n\nAVERTISSEMENT : si vous ne savez pas pourquoi cet écran est apparu, refusez l\'accès ! Vous pourrez révoquer l\'accès plus tard en utilisant l\'écran « Applis ». Permettre l\'accès Enlever l\'accès Veuillez choisir une clef ! - Aucune clef publique n\'a été trouvée pour ces identités : - Plus d\'une clef publique existe pour ces identités : + Aucune clef n\'a été trouvée pour ces identités : + Plus d\'une clef existe pour ces identités : Veuillez revoir la liste des destinataires ! + Veuillez choisir les destinataires ! La vérification de la signature a échoué ! Avez-vous installé cette appli à partir d\'une source différente ? Si vous êtes sûr que ce n\'est pas une attaque, révoquez l\'enregistrement de cette appli dans OpenKeychain et enregistrez-la à nouveau. Partager par un code QR @@ -389,6 +394,14 @@ Partager Sous-clefs Certificats + Révoquée + Cette identité a été révoquée par le propriétaire de la clef. Elle n\'est plus valide. + Vérifiée + Cette identité a été vérifiée. + Non vérifiée + Cette identité n\'a pas encore été vérifiée. Vous ne pouvez pas être certain si l\'identité corresponds vraiment à une personne spécifique. + Invalide + Quelque chose ne va pas avec cette identité ! Changer la phrase de passe Ajouter une identité @@ -408,6 +421,7 @@ Révoquer la sous-clef nouvelle + Veuillez sélectionner au moins un drapeau ! Téléverser la clef vers le serveur de clefs Ce champ est exigé @@ -443,6 +457,7 @@ Application de l\'opération d\'insertion par lot. Tentative d\'importer le trousseau secret comme public. Ceci est un bogue, veuillez remplir un rapport ! + Cette clef est une clef d\'OpenPGP version 3 et n\'est, par conséquent, pas sécuritaire. Elle n\'a pas été importée. Aucune ancienne clef de supprimée (création d\'une nouvelle ?) L\'ancienne clef a été supprimée de la base de données Échec de l\'opération causé par une erreur d\'encodage @@ -548,7 +563,7 @@ Suppression de l\'ID d\'utilisateur ayant une estampille temporelle dans le futur Suppression du certificat d\'ID d\'utilisateur de type inconnu (%s) Suppression du mauvais auto-certificat pour l\'ID d\'utilisateur « %s » - Suppression de l\'auto-certificat périmé pour l\'ID d\'utilisateur « %s » + Suppression de l\'auto-certificat périmé pour l\'ID d\'utilisateur « %s » Suppression du certificat étranger d\'ID d\'utilisateur par %s Suppression du certificat de révocation redondant pour l\'ID d\'utilisateur « %s » Suppression du certificat de révocation périmé pour l\'ID d\'utilisateur « %s » @@ -557,8 +572,8 @@ Fusion vers le trousseau public %s Fusion vers le trousseau secret %s - Erreur fatale lors de l\'encodage de la signature - Il a été tenté de consolider les trousseaux hétérogènes + Erreur fatale lors de l\'encodage de la signature + Il a été tenté de fusionner des trousseaux avec des empreintes différentes ! Ajout de la nouvelle sous-clef %s %s nouveaux certificats trouvés dans le trousseau Aucun nouveau certificat @@ -567,6 +582,7 @@ Aucune option de clef maîtresse n\'a été spécifiée ! Les trousseaux doivent être créés avec au moins un ID d\'utilisateur ! La clef maîtresse doit avoir le drapeau « certifié » ! + L\'expiration ne peut pas être « pareille qu\'avant » lors de la création de clefs. Ceci est une erreur de programmation, veuillez remplir un rapport de bogue ! La taille de la clef doit être supérieure ou égale à 512 ! Erreur interne PGP ! Mauvais choix d’algorithme ! @@ -577,18 +593,26 @@ L\'empreinte de clef effective ne correspond pas à celle attendue ! Aucune ID de clef. Ceci est une erreur interne, veuillez remplir un rapport de bogue ! Erreur interne, le contrôle d\'intégrité a échoué ! - Mauvais ID principal spécifié ! - Les IDs d\'utilisateurs révoqués ne peuvent pas être principaux ! + Aucun certificat maître sur lequel se basé n\'a été trouvé ! (Tous révoqués ?) + Mauvais ID d\'utilisateur principal spécifié ! + Mauvais ID d\'utilisateur spécifié pour la révocation ! + Les ID d\'utilisateurs révoqués ne peuvent pas être principaux ! + L\'expiration ne peut pas être « pareille qu\'avant » lors de la création de sous-clefs. Ceci est une erreur de programmation, veuillez remplir un rapport de bogue ! + Erreur fatale lors du déchiffrement de la clef maîtresse ! Ceci est probablement une erreur de programmation, veuillez remplir un rapport de bogue ! Exception interne PGP ! Exception de signature ! - Changement de la phrase de passe + Modification des certifications maîtresses + Changement de la phrase de passe pour le trousseau... + Rechiffrement de la sous-clef %s avec la nouvelle phrase de passe + Échec lors de la définition de la nouvelle phrase de passe, nouvel essai avec une ancienne phrase de passe vide + La phrase de passe de la sous-clef n\'a pas pu être changée ! (Est-elle différente des autres clefs ?) Remplacement du certificat de l\'ID d\'utilisateur principal précédent Génération d\'un nouveau certificat pour le nouvel ID d\'utilisateur principal Modification de la sous-clef %s - Une action a été tentée sur la sous-clef manquante %s ! - Génération d\'une nouvelle sous-clef %2$s de %1$s bit - ID de la nouvelle sous-clef : %s - La date d\'expiration ne peut pas être dans le passé ! + Une action a été tentée sur la sous-clef manquante %s ! + Ajout de la nouvelle sous-clef de type %2$s (%1$s bit) + Nouvelle ID de sous-clef : %s + La date d\'expiration ne peut pas être dans le passé ! Révocation de la sous-clef %s Trousseau modifié avec succès Ajout de l\'ID d\'utilisateur %s @@ -597,6 +621,40 @@ L\'ID d\'utilisateur ne peut pas être vide ! Erreur lors du déverrouillage du trousseau ! Déverrouillage du trousseau + + Consolidation de la base de données + La consolidation a été commencée alors qu\'aucune base de données n\'était en cache ! Ceci est probablement une erreur de programmation, veuillez remplir un rapport de bogue ! + La consolidation a été abandonnée, elle est déjà en cours sur un autre exétron ! + Enregistrement des trousseaux secrets + Enregistrement des trousseaux publiques + Nettoyage de la base de données + Base de données consolidée avec succès + Début de la phase critique + Fin de la phase critique + Suppression du fichier de cache du trousseau public + Suppression du fichier de cache du trousseau secret + Erreur lors de l\'ouverture de la base de données ! + Erreur E/S lors de l\'écriture des clefs publiques vers le cache ! + Erreur E/S lors de l\'écriture des clefs secrètes vers le cache ! + Erreur lors de la réimportation des clefs publiques ! + Erreur lors de la réimportation des clefs secrètes ! + + Consolidation de récupération avec %1$d clef secrète et %2$d publique + Consolidation de récupération avec %1$d clefs secrètes et %2$d publiques + + Récupération à partir d\'un état inconnu + + Réimportation d\'une clef publique + Réimportation de %d clefs publiques + + Aucune clef publique à réimporter, étape ignorée... + + Réimportation d\'une clef secrète + Réimportation de %d clefs secrètes + + Aucune clef secrète à réimporter, étape ignorée... + Une exception a eu lieu lors de la suppression du fichier de cache public + Une exception a eu lieu lors de la suppression du fichier de cache secret Cliquer ici pour effacer les phrases de passe mises en cache OpenKeychain a mis en cache %d phrases de passe @@ -610,6 +668,7 @@ identité <inconnu> Aucun certificat pour cette clef + Seuls les auto-certificats et les certificats créés avec vos clefs sont affichés ici. Identités à certifier Raison de la révocation État de vérification diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index 89e09783b..e4e08ca78 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -1,9 +1,12 @@ + Seleziona chiavi + Seleziona la Tua Chiave Codifica Decodifica Frase di accesso + Aggiungi Sottochiave Modifica Chiave Preferenze Apps @@ -12,6 +15,7 @@ Condivi impronta con... Condividi chiave con... Condividi file con... + Condividi messaggio con... Codifica File Decodifica File Importa Chiavi @@ -23,6 +27,7 @@ Dettagli Chiave Aiuto Registro + Crea Chiave Identità Sottochiavi @@ -40,20 +45,27 @@ Decodifica, verifica e salva su file Decodifica e verifica messaggio Codifica e salva file + Codifica e condividi file Salva Annulla Elimina + Nessuna Scadenza OK Carica sul Server delle Chiavi Prossimo Precedente Chiave di ricerca + Codifica e condividi messaggio Mostra chiave di certificazione + Crea chiave + Aggiungi file(s) Impostazioni Aiuto Esporta su un file Cancella chiave + Crea mia chiave + Importa chiavi esistenti Cerca Impostazioni Beam Annulla @@ -65,19 +77,27 @@ Messaggio File + File(s) + File: Nessuna Frase di Accesso Frase di Accesso Ripeti Frase di Accesso Algortimo + Documenti con Armatura ASCII + Documenti: Armatura ASCII Fai sapere agli altri che utilizzi OpenKeychain Scrive \'OpenKeychain v2.7\' nelle firme OpenPGP, testi cifrati e chiavi esportate + Da: + A: + Documenti: Cancella Dopo Codifica Cancella Dopo Decodifica Algoritmo di Codifica Algoritmo di Hash + Con Chiave Pubblica + Con Frase di Accesso Cache Frase di Accesso Compressione Messaggio Compressione File - Forza vecchie Firme OpenPGPv3 Server Chiavi ID Chiave Creazione @@ -156,6 +176,7 @@ Vuoi veramente eliminare tutte le chiavi pubbliche selezionate?\nNon potrai annullare! Vuoi veramente eliminare la chiave PRIVATA \'%s\'?\nNon potrai annullare! Vuoi veramente eliminare la chiave pubblica \'%s\'?\nNon potrai annullare! + Esporta anche chiave segreta 1 chiave esportata correttamente. %d chiavi esportate correttamente. Nessuna chiave esportata. @@ -194,6 +215,11 @@ Devi avere Android 4.1 per usare Android NFC Beam! NFC non disponibile nel tuo dispositivo! Nessuna chiave trovata! + Query di ricerca troppo. Per favore ridefinisci la tua ricerca! + C\'è stato un errore durante la ricerca delle chiavi. + La query di ricerca della chiave ha generato troppi risultati. Per favore ridefinisci la tua ricerca! + In entrambi i casi sono state trovate troppe o nessuna chiave. Si prega di migliorare la vostra ricerca! + Nessuna chiave valida trovata nei File/Appunti! Si è verificato un errore generico, si prega di creare una nuova segnalazione di errore per OpenKeychain. parte del file caricato e\' un oggetto OpenPGP valido, ma non una chave OpenPGP @@ -214,8 +240,21 @@ salvataggio... importazione... esportazione... + caricamento... fabbricazione chiave... fabbricazione portachiavi principale... + generazione di una nuova chiave RSA... + generazione di una nuova chiave DSA... + generazione di una nuova chiave ElGamal... + modifica portachiavi... + Sblocco portachiavi... + Aggiunta ID utente... + Revoca ID utente... + Cambio ID utente primario... + Modifica sottochiave... + Revoca sottochiave... + Aggiunta sottochiave... + Cambio frase di accesso... esportazione chiave... esportazione chiavi... @@ -230,14 +269,18 @@ elaborazione firma... verifica firma... firma... + certificazione... lettura dati... ricerca chiave... decompressione dati... verifica integrita\'... eliminazione sicura di \'%s\'... + cancellazione chiavi... + consolidazione: salvataggio della cache... + consolidazione: reimportazione... - Nome/Email/ID Chiave... - Nome/Keybase.io nome utente... + Nome/Email/ID Chiave... + Nome/Email/Certificato/Chiave... 512 768 @@ -249,6 +292,7 @@ 8192 Lunghezza chiave peronalizzata Digita lunghezza chiave personalizzata (in bit): + La lunghezza della chiave RSA deve essere maggiore di 1024 e al massimo 16384. Inoltre, deve essere multipla di 8. La lunghezza della chiave DSA deve essere almeno 512 e al massimo 1024. Inoltre, deve essere multipla di 64. veloce @@ -275,6 +319,10 @@ Chiave importata correttamente. %1$d chiavi importate correttamente. + + e chiave%2$s aggiornata. + e aggiornate %1$d chiavi%2$s. + Chiave%2$s importata correttamente. %1$d chiavi%2$s importate correttamente. @@ -310,13 +358,16 @@ SHA-256 della Firma del Pacchetto Account Nessun account collegato a questa applicazione + L\'applicazione richiede la creazione di un nuovo account. Si prega di selezionare una chiave esistente o crearne una nuova.\nLe applicazioni sono limitate all\'utilizzo delle chiavi selezionate qui! + La chiave salvata per questo account è stata cancellata. Perfavore selezionane una diversa!\nLe applicazioni sono limitate all\'utilizzo delle chiavi qui selezionate! Le app visualizzate hanno richiesto l\'accesso a OpenKeychain.\nPermetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' apparsa, nega l\'accesso! Puoi revocare l\'accesso dopo, usando la schermata \'App Registrate\'. Permetti accesso Nega accesso Per favore selezionare una chiave! - Nessuna chiave pubblica trovata per queste identità: - Esistono piu\' di una chiave pubblica per queste identità: + Nessuna chiave trovata per queste identità: + Esistono piu\' di una chiave per queste identità: Per favore ricontrolla la lista destinatari! + Per favore seleziona i destinatari! Controllo della firma fallito! Hai installato questa app da una fonte diversa? Se sei sicuro che non sia un attacco, revoca la registrazione di questa app in OpenKeychain e dopo registra di nuovo l\'app. Condividi tramite Codice QR @@ -330,16 +381,27 @@ Puoi iniziare da o creazione della tua chiave + importazione di chiave esistente. Modifica chiave Codifica con questa chiave Certifica identità + Aggiorna dal server delle chiavi Condividi con... Condividi con NFC tenendo i dispositivi a stretto contatto + Carica nel server delle chiavi Info Principale Condividi Sottochiavi Certificati + Revocato + Questa identità è stata revocata dal suo proprietario. Non è più valida. + Verificato + Questa identità è stata verificata. + Non verificato + Questa identità non è stata ancora verificata. Non puoi esser sicuro che l\'identità corrisponda veramente ad una specifica persona. + Non valido + C\'è qualcosa che non va con questa identità! Cambia Frase Di Accesso Aggiungi Identità @@ -349,14 +411,25 @@ Cambia in Identita\' Primaria Revoca identita\' + + Ripristina revoca + + Questa identità è stata revocata. Ciò non può essere annullato. Seleziona un azione! Cambia Scadenza Revoca Sottochiave + nuovo + Per favore seleziona almeno una spunta! Carica chiave nel server delle chiavi Questo campo è necessario + Le frasi di accesso non coincidono. + Hai inserito la seguente identità: + La creazione di una chiave richiede un po\' di tempo, prendi un caffè nel frattempo...\n(3 sottochiavi, RSA, 4096 bit) + Inserisci Nome Completo, Email e scegli una Frase di Accesso. + Nome completo, es: Mario Rossi Questa chiave è stata revocata! Questa chiave è scaduta @@ -369,6 +442,7 @@ Chiudi drawer di navigazione Le Mie Chiavi + Il messaggio inserito in questo campo verrà firmato con la chiave selezionata in \'Da\' e crittografato per tutti i destinatari selezionati in \'A\'. Inserisci il testo cifrato qui per la decodifica e/o verifica... predefiniti @@ -383,6 +457,7 @@ Applicazione inserimento operazioni in batch. Ho cercato di importare portachiavi privato come pubblico. Questo è un bug, per cortesia inviateci un rapporto! + Questa chiave è una chiave OpenPGP versione 3 e quindi non sicura. Non è stata importata. Nessuna vecchia chiave cancellata (stai creando una nuova?) Cancellate vecchie chiavi dal database Operazione fallita a causa di un errore di codifica @@ -488,7 +563,7 @@ Rimozione ID utente con marca temporale futura Rimozione certificato ID utente di tipo sconosciuto (%s) Rimozione autocertificazione corrotta per ID utente \"%s\" - Rimozione autocertificazione scaduta per ID utente \"%s\" + Rimozione autocertificazione scaduta per ID utente \"%s\" Rimozione certificato ID utente estraneo di %s Rimozione certificato di revoca ridondante per ID utente \"%s\" Rimozione certificato di revoca scaduto per ID utente \"%s\" @@ -497,8 +572,8 @@ Fusione nel portachiavi pubblico %s Fusione nel portachiavi privato %s - Errore fatale nella codifica della firma - Tentativo di consolidare portachiavi eterogenei + Errore fatale nella codifica della firma + Tentativo di unire portachiavi con impronte digitali diverse! Aggiunta nuova sottochiave %s Trovati %s nuovi certificati nel portachiavi Nessun nuovo certificato @@ -507,6 +582,7 @@ Nessuna opzione della chiave principale specificata! I portachiavi devono essere creati con almeno un ID utente! La chiave principale deve avere la caratteristica di certificazione! + La data di scadenza non può essere \"come prima\" sulla creazione di chiavi. Questo è un errore di programmazione, si prega di inviare una segnalazione di bug! La grandezza della chiave deve essere di 512bit o maggiore Errore PGP interno! Pessima opzione di algortimo! @@ -517,18 +593,26 @@ Impronta chiave attuale non corrispondente! Nessun ID chiave. Questo è un errore di programmazione, per favore invia una segnalazione! Errore interno, controllo di integrità fallito! + Nessun certificato principale trovato su cui operare! (Tutti revocati?) ID utente primario specificato non valido! + ID utente specificato non valido per la revoca! ID utente revocato non può essere primario! + La data di scadenza non può essere \"come prima\" sulla creazione di sottochiavi. Questo è un errore di programmazione, si prega di inviare una segnalazione di bug! + Errore irreversibile nella decodifica della chiave principale! Questo è probabilmente un errore di programmazione, si prega di inviare una segnalazione di bug! Eccezione interna di PGP! Eccezione di firma! - Cambio frase di accesso + Modifica delle certificazioni principali + Cambio frase di accesso del portachiavi... + Ri-codifica sottochiave %s con nuova frase di accesso + Impostazione nuova frase di accesso fallita, provo di nuovo con frase precedente di accesso vuota + La frase di accesso per la sottochiave non può essere modificata! (Ne ha una diversa dalle altre chiavi?) Sostituzione certificato del ID utente primario precedente Generazione di un nuovo certificato per il nuovo ID utente primario Modifica sottochiave %s - Tentativo di operare su sottochiave mancante %s! - Generazione nuovi %1$s bit %2$s sottochiave + Tentativo di operare su sottochiave %s mancante! + Aggiunta nuova sottochiave di tipo %2$s (%1$s bit) Nuovo ID sottochiave: %s - La data di scadenza non può essere passata! + La data di scadenza non può essere nel passato! Revoca sottochiave %s Portachiavi modificato con successo Aggiunta id utente %s @@ -537,6 +621,40 @@ ID Utente non può essere vuoto! Errore di apertura portachiavi! Apertura portachiavi + + Consolidazione database + Il consolidamento è stato avviato mentre nessun database è stato memorizzato nella cache! Questo è probabilmente un errore di programmazione, si prega di aprire un bug report. + Consolidamento interrotto, già in esecuzione su un altro processo! + Salvataggio portachiavi privati + Salvataggio portachiavi pubblico + Pulizia database + Consolidamento database con successo + Ingresso fase critica + Uscita fase critica + Eliminazione del file di cache del portachiavi pubblico + Eliminazione del file di cache del portachiavi privato + Errore nell\'apertura del database + Errore di IO durante la scrittura delle chiavi pubbliche nella cache! + Errore di IO durante la scrittura delle chiavi private nella cache! + Errore nella re-importazione delle chiavi pubbliche! + Errore nella re-importazione delle chiavi private! + + Recupero consolidamento con %1$d chiavi segrete e %2$d chiavi pubbliche + Recupero consolidamento con %1$d chiavi segrete e %2$d chiavi pubbliche + + Recupero da situazione ignota + + Reimportazione di una chiave pubblica + Reimportazione di %d chiavi pubbliche + + Nessuna chiave pubblica da reimportare, proseguo... + + Reimportazione di una chiave privata + Reimportazione di %d chiavi private + + Nessuna chiave privata da reimportare, proseguo... + Eccezione durante la eliminazione del file di cache pubblico + Eccezione durante la eliminazione del file di cache privato Clicca per rimuovere la frase di accesso nella cache OpenKeychain ha memorizzato nella cache %d frasi di accesso @@ -550,6 +668,7 @@ Identit <sconosciuto> Nessun certificato per questa chiave + Solo le autocertificazioni e i certificati creati con le tue chiavi vengono visualizzati qui. Identità da certificare Ragione della Revoca Stato Verifica @@ -557,6 +676,7 @@ Chiave non trovata! Errore di elaborazione chiave! ripulito + Le vostre chiavi possono essere eliminate solo singolarmente! Visualizza Dettagli Certificati sconosciuto non può firmare @@ -565,5 +685,7 @@ Mostra chiave (%s) Riappropriati della tua riservatezza con OpenKeychain! + Crea mia chiave + Importa Chiave esistente Salta Installazione diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index dbb52d208..36cbe64b5 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -27,6 +27,7 @@ 鍵の概要 ヘルプ ログ + 鍵の生成 ユーザID 副鍵 @@ -97,7 +98,6 @@ パスフレーズキャッシュ メッセージの圧縮 ファイルの圧縮 - 強制的に古いOpenPGPV3形式の署名にする 鍵サーバ 鍵ID 生成 @@ -271,8 +271,8 @@ 完全性の検証中... \'%s\' を完全に削除中… - 名前/メール/鍵ID... - 名前/Keybase.io名... + 名前/メール/鍵ID... + 名前/メール/証明/鍵... 512 768 @@ -347,13 +347,15 @@ アカウント このアプリに接続されてるアカウントはありません。 このアプリは新しいアカウントの生成を要求しています。すでにある鍵1つを選択するか、新たに生成してください。\nここであなたが選択する鍵の使い道についてアプリケーションには制約があります! + このアカウントの削除された鍵を保存しました。異るものを選択してください!\nここであなたが選択する鍵の使い道についてはアプリには制約があります! 表示されているアプリはメッセージの暗号/復号化とあなたの名前での署名付けを要求しています。\nアクセスを許可しますか?\n\n注意: もしなぜスクリーンに表れたかわからないなら、アクセスを許可しないでください! あなたは後で\'アプリ\'スクリーンを使い、アクセスを破棄するこもできます。 許可されたアクセス 許可されないアクセス 鍵を選択してください! - このユーザIDについて公開鍵が見付かりません: - このユーザIDについて1つ以上の公開鍵が存在します: + このユーザIDについて鍵が見付かりません: + このユーザIDについて1つ以上の鍵が存在します: 受信者リストを確認してください! + 受信者を選択してください! 署名チェックが失敗! 違うところからこのアプリをインストールしましたか? もし攻撃されてでなくそうであるなら、OpenKeychainにあるこのアプリの登録を破棄し、再度アプリを登録してください。 QRコードで共有 @@ -379,6 +381,14 @@ 共有 副鍵 証明 + 破棄 + このIDは鍵の所有者により破棄されています。もう適正ではありません。 + 検証 + このIDは検証されています。 + 未検証 + このIDはまだ検証されていません。IDが本当に特定の人に対応している場合を、あなたは確認することができません。 + 不適正 + このIDではなにかしら問題があります! パスフレーズの変更 ユーザIDの追加 @@ -398,6 +408,7 @@ 副鍵を破棄 新規 + 最低1つフラグを選択してください! 鍵サーバへアップロード このフィールドは必須です @@ -433,6 +444,7 @@ 連続挿入処理を適用する。 秘密鍵の鍵輪を公開鍵としてインポートを試行しました。これはバグで、ファイルをレポートしてください! + この鍵はOpenPGP v3形式の鍵で安全ではありません。そのためインポートできません。 削除された古い鍵はありません (新しく作りますか?) データベースから古い鍵を削除しました エンコードエラーにより操作が失敗しました @@ -534,7 +546,7 @@ 未来にタイムスタンプがあるユーザIDを破棄中 不明な型 (%s) でのユーザID検証を破棄中 ユーザID \"%s\" による問題のある自己検証を破棄中 - 期限の切れたユーザID \"%s\" による自己検証を破棄中 + 期限の切れたユーザID \"%s\" による自己検証を破棄中 %s によって検証されている外部ユーザIDを破棄中 ユーザID \"%s\" による重複した破棄証明を破棄中 ユーザID \"%s\" による期限切れ破棄証明を破棄中 @@ -543,8 +555,8 @@ 公開鍵の鍵輪 %s にマージ中 秘密鍵の鍵輪 %s にマージ中 - 署名のエンコードでの致命的なエラー - 種類の異なる鍵輪を統合しようとした + 署名のエンコードでの致命的なエラー + 指紋が異なる鍵輪をマージしようとしています! 新しい副鍵 %s を追加中 鍵輪に新しい検証を %s 発見 新しい証明がない @@ -553,6 +565,7 @@ 主鍵オプション特有ではありません! 鍵輪は最低でも1つのユーザIDの生成が必要です! 主鍵は検証フラグが必須です! + 鍵の生成時に期限を\"過去\"とすることはできません。これはプログラムエラーで、バグレポートとしてファイルを送ってください! 鍵サイズは512かそれ以上が必須です! PGP内部エラー! 悪いアルゴリズムを選択しています! @@ -563,18 +576,24 @@ 現実の鍵指紋が想定の1つと合致しませんでした! 鍵IDがない。 これは内部エラーで、バグレポートの提出をお願いします! 内部エラー、完全性チェックが失敗! + マスター認証が操作で見付かりませんでした(すべて破棄しましたか?) 問題のある主ユーザIDが指定された! + 破棄において問題のあるユーザIDが指定された! 主ユーザIDの破棄はできません! + 副鍵の生成時に期限を\"過去\"とすることはできません。これはプログラムエラーで、バグレポートとしてファイルを送ってください! + 主鍵の復号で致命的な失敗! これはプログラミングのエラーの場合がありますので、バグレポートの提出をお願いします! PGP内部例外! 署名例外! - パスフレーズの変更中 + マスター認証を変更 + 鍵輪のパスフレーズの変更中... + 新しいパスフレーズの設定に失敗しました、空の古いパスフレーズで再度試してください + 副鍵のパスフレーズは変更されていません! (他の鍵とは異なるになっていませんか?) 以前の主ユーザIDで証明を入れ替え中 新しい主ユーザIDで新しい証明を生成中 副鍵 %s を変更中 - 遺失した副鍵 %s の操作をしようとした! - 新しい %1$s ビットの %2$s 副鍵の生成中 + 見付からない副鍵 %s の操作をしようとした! 新しい副鍵 ID: %s - 期限切れ日を過去にはできません! + 期限切れ日を過去にはできません! 副鍵 %s を破棄中 鍵輪の変更に成功 ユーザID %s を追加中 @@ -583,6 +602,34 @@ ユーザIDは空にすることはできません! 鍵輪のロック解除エラー! 鍵輪のロック解除中 + + データベースの統合中 + 秘密鍵の鍵輪を保存中 + 公開鍵の鍵輪を保存中 + データベースの消去中 + データベースの統合が成功しました + クリティカルなフェーズに入りました + クリティカルなフェーズを抜けました + 公開鍵輪のキャッシュファイルを削除中 + 秘密鍵輪のキャッシュファイルを削除中 + データベースオープン中のエラー! + キャッシュに公開鍵を書く際にI/Oエラー! + キャッシュに秘密鍵を書く際にI/Oエラー! + 公開鍵の再インポートのエラー! + 秘密鍵の再インポートのエラー! + + %1$d の秘密鍵と %2$d の公開鍵の統合のリカバリー中 + + 不明な状態からリカバリー中 + + %d の公開鍵を再インポート中 + + 再インポートで公開鍵がありません、スキップします... + + %d の秘密鍵を再インポート中 + + 再インポートで秘密鍵がありません、スキップします... + 公開鍵キャッシュファイルの削除例外 クリックしてパスフレーズのキャッシュをクリア OpenKeychainは %d のパスフレーズをキャッシュしています diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml index f225e204a..7f93d0d51 100644 --- a/OpenKeychain/src/main/res/values-nl/strings.xml +++ b/OpenKeychain/src/main/res/values-nl/strings.xml @@ -71,7 +71,6 @@ Wachtwoordcache Berichtcompressie Bestandscompressie - Forceer oude OpenPGPv3 Handtekeningen Sleutelservers Sleutel-id Aanmaak @@ -281,8 +280,6 @@ Toegang toestaan Toegang weigeren Selecteert u a.u.b. een sleutel - Geen publieke sleutels zijn gevonden voor deze identiteiten: - Meer dan een publieke sleutel bestaat voor deze identiteiten: Bekijkt u a.u.b. de ontvangers Handtekening check mislukt! Hebt u deze app van een andere bron geïnstalleerd? Als u zeker weet dat dit geen aanval is, haal dan de registratie van deze app in OpenKeychain weg en registreer de app opnieuw. @@ -334,6 +331,7 @@ + Certificeer diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml index a4a66e30c..f96753662 100644 --- a/OpenKeychain/src/main/res/values-pl/strings.xml +++ b/OpenKeychain/src/main/res/values-pl/strings.xml @@ -59,7 +59,6 @@ Bufor haseł Kompresja wiadomości Kompresja plików - Wymuś stare podpisy OpenPGPv3 Serwery kluczy Identyfikator klucza Utworzenia @@ -311,6 +310,7 @@ + Szczegóły certyfikatu diff --git a/OpenKeychain/src/main/res/values-pt/strings.xml b/OpenKeychain/src/main/res/values-pt/strings.xml new file mode 100644 index 000000000..9a8c94e31 --- /dev/null +++ b/OpenKeychain/src/main/res/values-pt/strings.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml index fd2c0312d..dba0aed12 100644 --- a/OpenKeychain/src/main/res/values-ru/strings.xml +++ b/OpenKeychain/src/main/res/values-ru/strings.xml @@ -74,7 +74,6 @@ Помнить пароль Сжатие сообщения Сжатие файла - Использовать OpenPGPv3 подписи (устар.) Серверы ключей ID ключа Создан @@ -238,7 +237,6 @@ проверка целостности... безопасное удаление \'%s\'... - Имя/Email/ID ключа… 512 768 @@ -304,8 +302,6 @@ Разрешить доступ Запретить доступ Пожалуйста, выберите ключ! - Для этих идентификаторов не найдены публичные ключи: - Для этих идентификаторов найдено более одного публичного ключа: Пожалуйста, проверьте получателей! Проверка подписи пакета не удалась! Если вы установили программу из другого источника, отзовите для неё доступ к этой программе или обновите право доступа. @@ -383,14 +379,13 @@ Аннулированные идентификаторы не могут быть основными! Внутренняя ошибка PGP! Ошибка подписи! - Изменение пароля - Срок годности не может быть в прошлом! Связка успешно изменена Добавление id %s Изменение основного uid на %s Аннулирование id %s Ошибка разблокирования связки! Разблокирование связки + Кем подписан diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml index 0a7b4713e..a24c1ddf1 100644 --- a/OpenKeychain/src/main/res/values-sl/strings.xml +++ b/OpenKeychain/src/main/res/values-sl/strings.xml @@ -74,7 +74,6 @@ Hranjenje gesla v spominu Stiskanje sporočil Stiskanje datotek - Vsili stare podpise OpenPGPv3 Strežniki ID ključa Ustvarjanje @@ -243,8 +242,6 @@ preverjam neokrnjenost... varno brišem \'%s\'… - Ime/e-pošta/ID ključa - Ime/Keybase.io uporabniško ime... 512 768 @@ -303,8 +300,6 @@ Dovoli dostop Zavrni dostop Izberite ključ! - Za te identitete ni bil najden noben javni ključ: - Za te identitete obstaja več ključev: Preverite seznam prejemnikov! Preverjanje podpisa ni uspelo! Ste namestili to aplikacijo iz drugega vira? Če ste prepričani, da to ni napad, prekličite registracijo te aplikacije v OpenKeychain in jo izvedite znova. @@ -406,6 +401,7 @@ + Overovitelj diff --git a/OpenKeychain/src/main/res/values-tr/strings.xml b/OpenKeychain/src/main/res/values-tr/strings.xml index f8c26f140..f8ef23467 100644 --- a/OpenKeychain/src/main/res/values-tr/strings.xml +++ b/OpenKeychain/src/main/res/values-tr/strings.xml @@ -162,6 +162,7 @@ + imzalanamadı diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml index 42ac94c32..f1533f4f2 100644 --- a/OpenKeychain/src/main/res/values-uk/strings.xml +++ b/OpenKeychain/src/main/res/values-uk/strings.xml @@ -1,9 +1,12 @@ + Вибрати ключі + Виберіть ваш ключ Зашифрувати Розшифрувати Парольна фраза + Додати підключ Редагувати ключ Налаштування Програми @@ -12,6 +15,7 @@ Поділитися відбитком із… Поділитися ключем з… Поширити файл з… + Поширити повідомлення із… Зашифрувати до файлу Розшифрувати до файлу Імпортувати ключі @@ -23,6 +27,7 @@ Подробиці про ключ Довідка Журнал + Створити ключ Сутності Підключі @@ -40,20 +45,27 @@ Розшифрувати, перевірити та зберегти файл Розшифрувати і перевірити повідомлення Шифрувати і зберегти файл + Зашифрувати та поширити файл Зберегти Скасувати Вилучити + Необмежено Гаразд Завантажити на сервер ключів Далі Назад Шукати ключ + Зашифрувати і поширити повідомлення Переглянути ключ сертифікації + Створити ключ + Додати файл(и) Параметри Довідка Експорт до файлу Вилучити ключ + Створити мій ключ + Імпортувати наявний ключ Пошук Налаштування променя Скасувати @@ -65,16 +77,27 @@ Повідомлення Файл + Файл(и) + Файл: Без парольної фрази Парольна фраза + Повторити пароль Алгоритм + Файл ASCII Armor + Файли: ASCII Armor + Нехай інші дізнаються, що ви користуєтеся OpenKeychain + Напишіть \'OpenKeychain v2.7\' для підписів, зашифрованого тексту та експортованих ключів OpenPGP + Від: + Кому: + Файли: вилучити після шифрування Вилучити після розшифрування Алгоритм шифрування Хеш алгоритм + З публічним ключем + З паролем Кеш парольної фрази Стиснення повідомлення Стиснення файлу - Примусово старі підписи OpenPGPv3 Сервери ключів ІД ключа Створення @@ -155,6 +178,7 @@ Ви справді хочете вилучити усі вибрані відкриті ключі?\nВи не зможете це відмінити! Ви справді хочете вилучити секретний ключ \'%s\'?\nВи не зможете це відмінити! Ви справді хочете вилучити відкритий ключ \'%s\'?\nВи не зможете це відмінити! + Також експортувати секретні ключі Успішно експортовано 1 ключ. Успішно експортовано %d ключів. Жодного ключа не експортовано. @@ -194,6 +218,11 @@ Вам потрібний Android 4.1 для використання функції Androids NFC промінь! NFC недоступний на вашому пристрої! Ключ не знайдено! + Надто короткий пошуковий запит. Поліпшіть його! + Трапилася помилка при пошуці за ключами. + Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит! + Нема жодного ключа або знайдено надто багато! Поліпшіть свій запит! + Жодного дійсного ключа не знайдено у File/Clipboard! Трапилася загальна помилка, будь ласка, створіть новий звіт про помилку для OpenKeychain. частина завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP @@ -215,8 +244,21 @@ збереження… імпортується… експортується… + відвантаження… будується ключ… побудова основного кільця… + генерується новий ключ RSA… + генерується новий ключ DSA… + генерується новий ключ ElGamal… + змінюється в\'язка ключів… + розблоковується в\'язка… + розблоковуються ID користувачів… + відхилення ІД користувачів… + змінюється ІД первинного користувача… + змінюються підключі… + відхилення підключів… + додаються підключі… + змінюється пароль… експортується ключ… експортуються ключі… @@ -232,13 +274,15 @@ обробляється підпис… перевірка підпису… підписання… + сертифікується… читання даних… пошук ключа… розпакування даних… перевірка цілісності… вилучення безпечно \'%s\'… - Назва/Ел. пошта/ІД ключа… + Назва/Ел. пошта/ІД ключа… + Назва/Ел.пошта/Доказ/Ключ… 512 768 @@ -250,6 +294,7 @@ 8192 Особливий розмір ключа Введіть інший розмір ключа (у бітах): + Довжина ключа RSA має бути більша за 1024 та менша за 16384. Також він має мати кратність 8. Довжина ключа DSA має бути більша за 512 та менша за 1024. Також він має мати кратність 64. швидке @@ -277,11 +322,22 @@ Успішно імпортовано %1$d ключі. Успішно імпортовано %1$d ключів. + + та оновлено ключ%2$s. + та оновлено %1$d ключі%2$s. + та оновлено %1$d ключів%2$s. + Успішно імпортований ключ%2$s. Успішно імпортовано %1$d ключі%2$s. Успішно імпортовано %1$d ключів%2$s. + + Успішно оновлено ключ%2$s. + Успішно оновлено %1$d ключі%2$s. + Успішно оновлено %1$d ключів%2$s. + + Переглянути журнал Нема що імпортувати. Помилка імпорту ключів! , із застереженнями @@ -291,6 +347,7 @@ Зашифрувати з OpenKeychain Розшифрувати з OpenKeychain + Незареєстровані програми!\n\nСписок підтримуваних сторонніх програм можна знайти у Довідці! Показати додаткову інформацію Приховати додаткову інформацію Показати додаткові налаштування @@ -306,12 +363,17 @@ Назва пакунку SHA-256 підписку пакунку Облікові записи + Немає облікового запису приєднаного до цієї програми. + Ця програма вимагає створення нового профілю. Будь ласка, виберіть наявний приватний ключ або створіть інший.\nПрограми обмежені використання ключів, які ви тут оберете! + Збережений для цього профілю ключ вже вилучений. Будь ласка, виберіть інший!\nПрограми обмежені використання ключів, які ви тут оберете! + Показана програма запитує доступ до OpenPGP Keychain.\nДозволити доступ?\n\nУВАГА: якщо ви не знаєте, чому цей екран появився, не дозволяйте доступ! Ви можете відкликати доступ пізніше, використовуючи екран \'Зареєстровані програми\'. Дозволити доступ Не дозволити доступ Будь ласка, виберіть ключ! - Немає публічних ключів для цих сутностей: - Наявно більше одного публічного ключа для цих сутностей: + Не знайдено жодного ключа для цих сутностей: + Наявно більше одного ключа для цих сутностей: Будь ласка, перевірте список одержувачів! + Будь ласка, виберіть одержувачів! Перевірка підпису пакету не вдалася! Може ви встановили програму з іншого джерела? Якщо ви впевнені, що це не атака, то відкличте реєстрацію програми у OpenKeychain та знову зареєструйте її. Відправити як штрих-код @@ -326,19 +388,57 @@ Ви можете розпочати за чи створюється ваш власний ключ + імпортується наявний ключ. Редагувати ключ Шифрувати з цим ключем Сертифікувати сутності + Оновити із сервера ключів Поділитися із… Поширити через NFC, тримаючи пристрої пліч-о-пліч + Відвантажити на сервер ключів Основна інформація Поділитися Підключі Сертифікати + Відхилено + Ця сутність вже відкликана власником ключа. Вона більше не дійсна. + Перевірено + Не перевірено + Не перевірено + Ця сутність ще не перевірена. Ви не можете переконатися, чи сутність справді відповідає вказаній особі. + Недійсна + Щось неправильне у цій сутності! + Змінити пароль + Додати сутність + Додати підключ + Виберіть дію! + + Змінити на первинну сутність + Відкликати сутність + + + Обернути відкликання + + Ця сутність вже відкликана. Це не можна скасувати. + Виберіть дію! + + Змінити термін дії + Відкликати підключ + + новий + Відвантажити ключ на сервер ключів + Це поле - обов\'язкове + Паролі фрази не збігаються + Ви ввели наступну сутність: + Створення ключа може зайняти деякий час, випийте чашечку кави за цей час ...\n(3 підключі, RSA, 4096 біт) + Введіть ваше повне ім\'я, електронну адреса та оберіть парольну фразу. + Повне ім\'я, наприклад Степан Бандера + Цей ключ вже відкликано! + Термін дії цього ключа вже минув! Ключі Підписати і зашифрувати @@ -348,6 +448,7 @@ Закрити панель навігації Мої ключі + Введене тут повідомлення буде підписане, використовуючи ключ, вибраний у Звідки та зашифрований для всіх одержувачів, вказаних у Кому. Уведіть зашифрований текст тут для його розшифрування та/або перевірки… типово @@ -355,48 +456,152 @@ випадковий додатний відкликано + Гаразд Невдача! Помилка! Недоступний ключ Застосовується пакетна операція вставки. + Спробували імпортувати секретну в\'язку як публічну. Це вада. Будь ласка, відправте звіт! + Цей ключ зроблений OpenPGP версії 3, а тому небезпечний. Його не можна імпортувати. Нема вилученого старого ключа (створюється новий?) Вилучений старий ключ з бази даних Операція не вдалася через помилку кодування Операція не вдалася через помилку вводу/виводу + Операція не вдалася через помилку бази даних Операція не вдалася через внутрішню помилку + Імпортується публічна в\'язка %s Шифруються дані із в\'язки Аналізуються ключі Підготовка операцій з базою даних Обробляється основний ключа %s + В\'язка закінчилася %s + В\'язка закінчується %s + Прапори основного ключа: certify, encrypt, sign + Прапори основного ключа: certify, encrypt + Прапори основного ключа: certify, sign + Прапори основного ключа: encrypt, sign + Прапори основного ключа: certify + Прапори основного ключа: encrypt + Прапори основного ключа: sign + Прапори основного ключа: немає Опрацьовується підключ %s + Підключ закінчився %s + Підключ закінчуєтья %s + Прапори підключів: certify, encrypt, sign + Прапори підключів: certify, encrypt + Прапори підключів: certify, sign + Прапори підключів: encrypt, sign + Прапори підключів: certify + Прапори підключів: encrypt + Прапори підключів: sign + Прапори підключів: жодний Успішно імпортована публічна в\'язка + В\'язка не містить нових даних. Нема що робити. Повторне вставлення секретного ключа Виявлено поганий сертифікат! Помилка опрацювання сертифікату! + ІД користувача сертифіковано %1$s + + Ігнорується один сертифікат виданий невідомим відкритим ключем + Ігнорується %s сертифікати, виданих невідомими відкритими ключами + Ігнорується %s сертифікатів, виданих невідомими відкритими ключами + + Класифікуються ІД користувача (нема доступних надійних ключів) + + Класифікуються ІД користувача (за допомогою одного надійного ключа) + Класифікуються ІД користувача (за допомогою %s надійних ключів) + Класифікуються ІД користувача (за допомогою %s надійних ключів) + + Перевпорядкування ІД користувачів Обробляється ІД користувача %s + ІД користувача відхилене + Спробували імпортувати публічну в\'язку як секретну. Це вада. Будь ласка, відправте звіт! + Спробували імпортувати в\'язку без канонізації. Це вада. Будь ласка, відправте звіт! Імпортується секретний ключ %s Помилка бази даних! Опрацьовуються секретні підключі Помилка шифрування в’язки + Генерується публічна в\'язка із секретної Підключ %s недоступний у публічному ключі Позначено %s як доступно + В\'язка не містить нових даних. Нема що робити. Успішно імпортована секретна в\'язка + Канонізація публічної в\'язки %s + Канонізація секретної в\'язки %s + Невдала канонізація в\'язки: в\'язка не має дійсних ІД користувача Обробляється основний ключ… Опрацьовується підключ %s + Тип сертифікату невідомого ключа: %s + Не знайдено дійсного сертифікату для %s, вилучення із кільця + Успішна канонізація в\'язки, без змін + + Успішна канонізація в\'язки, вилучено один хибний сертифікат + Успішна канонізація в\'язки, вилучено %d хибні сертифікати + Успішна канонізація в\'язки, вилучено %d хибних сертифікатів + + Успішна канонізація в\'язки, вилучено %1$s хибних та %2$s надлишкових сертифікатів + + Успішна канонізація в\'язки, вилучено один надлишковий сертифікат + Успішна канонізація в\'язки, вилучено %d надлишкові сертифікати + Успішна канонізація в\'язки, вилучено %d надлишкових сертифікатів + + Вилучення недійсного ІД користувача %s + Злиття у публічну в\'язку %s + Злиття у секретну в\'язку %s + Фатальна помилка шифрування підпису + Спробували злити в\'язки із різними відбитками! Додається новий підключ %s + Знайдено %s нових сертифікатів у в\'язці + Немає нових сертифікатів + Генерується новий основний ключ + Не вказано параметрів основного ключа! + В\'язка мусить бути створеною хоча б одним ІД користувача! + Основний ключ повинен мати прапорець certify! + Розмір ключа має бути більшим або рівним 512! + Внутрішня помилка PGP! + Поганий вибір алгоритму! + Основний ключ не може бути ключем ElGamal! + Змінюється в\'язка %s + Виняток шифрування! + Дійсний відбиток ключа не зібгається з очікуваним! + Немає ІД ключа. Це внутрішня помилка! Будь ласка, надішліть звіт про ваду! Внутрішня помилка - збій перевірки цілісності! + Не знайдено основного сертифікату для операцій! (Усе відкликано?) + Вказаний поганий ІД первинного користувача! + Вказаний поганий ІД первинного користувача! ІД відхилених користувачів не може бути первинним! Внутрішній виняток PGP! - Змінюється парольна фраза + Виняток підпису! + Змінюються основі сертифікації + Замінюється сертифікат ІД попереднього первинного користувача + Генерується новий сертифікат для ІД нового первинного користувача + Змінюється підключ %s + Спробували працювати із втраченим підключем %s! + Новий ід підключа: %s + Дата завершення дії не може бути у минулому! + Відкликається підключ %s + Успішно модифіковано в\'язку + Додається ід користувача %s + Змінюється первинне uid на %s + Відхилення ІД користувача %s + ІД користувача не повинно бути порожнім! + Помилка розблокування в\'язки! Розблоковується в\'язка + + Клацніть для очищення кешованих парольних фраз + OpenKeychain має %d кешованих парольних фраз + Кешовані парольні фрази: + Очистити кеш + Пароль + Внутрішня помилка! Ким підписаний Дані сертифікату Сутність @@ -409,6 +614,7 @@ Ключ не знайдено! Помилка опрацювання ключа! голий + Ваші власні ключі можна вилучити лише окремо! Переглянути дані сертифікату невідомий не можна підписати @@ -416,4 +622,8 @@ Вручну не створюються профілі OpenKeychain.\nЗа подробицями дивіться Довідку. Показати ключ (%s) + Заберіть вашу приватність із OpenKeychain! + Створити мій ключ + Імпортувати наявний ключ + Пропустити установку diff --git a/OpenKeychain/src/main/res/values-zh/strings.xml b/OpenKeychain/src/main/res/values-zh/strings.xml index 8e5d42bb7..9d0848c07 100644 --- a/OpenKeychain/src/main/res/values-zh/strings.xml +++ b/OpenKeychain/src/main/res/values-zh/strings.xml @@ -144,6 +144,7 @@ + diff --git a/OpenKeychain/src/main/res/values/attr.xml b/OpenKeychain/src/main/res/values/attr.xml index 86622b3e0..5dfa03987 100644 --- a/OpenKeychain/src/main/res/values/attr.xml +++ b/OpenKeychain/src/main/res/values/attr.xml @@ -4,8 +4,6 @@ - - \ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 09a434d5d..d31a08081 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -109,6 +109,7 @@ Expiry Usage Key Size + Elliptic Curve Primary identity Name Comment @@ -156,6 +157,8 @@ DSA ElGamal RSA + ECDH + ECDSA Open… Warning Error @@ -268,6 +271,8 @@ generating new RSA key… generating new DSA key… generating new ElGamal key… + generating new ECDSA key… + generating new ECDH key… modifying keyring… @@ -301,10 +306,14 @@ decompressing data… verifying integrity… deleting \'%s\' securely… + deleting keys… + + consolidate: saving to cache… + consolidate: reimporting… - Name/Email/Key ID… - Name/Keybase.io username… + Name/Email/Key ID… + Name/Email/Proof/Key… 512 @@ -320,6 +329,16 @@ RSA key length must be greater than 1024 and at most 16384. Also it must be multiplicity of 8. DSA key length must be at least 512 and at most 1024. Also it must be multiplicity of 64. + + NIST P-256 + NIST P-384 + NIST P-521 + + fast very slow @@ -395,9 +414,10 @@ Allow access Disallow access Please select a key! - No public keys were found for these identities: - More than one public key exist for these identities: + No keys were found for these identities: + More than one key exist for these identities: Please review the list of recipients! + Please select the recipients! Signature check failed! Have you installed this app from a different source? If you are sure that this is not an attack, revoke this app\'s registration in OpenKeychain and then register the app again. @@ -433,7 +453,7 @@ Verified This identity has been verified. Not verified - This identity has not been verified yet. You can not be sure if the identity really corresponds to a specific person. + This identity has not been verified yet. You cannot be sure if the identity really corresponds to a specific person. Invalid Something is wrong with this identity! @@ -456,6 +476,7 @@ Revoke Subkey new + Please select at least one flag! Upload key to keyserver @@ -494,9 +515,12 @@ error! key unavailable + + Applying insert batch operation. Tried to import secret keyring as public. This is a bug, please file a report! + This key is an OpenPGP version 3 key and thus insecure. It has not been imported. No old key deleted (creating a new one?) Deleted old key from database Operation failed due to encoding error @@ -604,18 +628,20 @@ Removing user id with future timestamp Removing user id certificate of unknown type (%s) Removing bad self certificate for user id "%s" - Removing outdated self certificate for user id "%s" + Removing outdated self certificate for user id "%s" Removing foreign user id certificate by %s Removing redundant revocation certificate for user id "%s" Removing outdated revocation certificate for user id "%s" No valid self-certificate found for user id %s, removing from ring Removing invalid user id %s + Removing duplicate user id "%s". The secret key contained two of them. This may result in missing certifications! + New public subkey found, but secret subkey dummy generation is not supported! + Tried to merge keyrings with differing fingerprints! + Fatal error encoding signature! Merging into public keyring %s Merging into secret keyring %s - Fatal error encoding signature - Tried to consolidate heterogeneous keyrings Adding new subkey %s Found %s new certificates in keyring No new certificates @@ -625,10 +651,16 @@ No master key options specified! Keyrings must be created with at least one user id! Master key must have certify flag! + Expiry time cannot be "same as before" on key creation. This is a programming error, please file a bug report! Key size must be greater or equal 512! + No key size specified! This is a programming error, please file a bug report! + No elliptic curve specified! This is a programming error, please file a bug report! Internal PGP error! - Bad algorithm choice! - Master key must not be of type ElGamal! + Unknown algorithm selected! This is a programming error, please file a bug report! + Bad key flags selected, DSA cannot be used for encryption! + Bad key flags selected, ElGamal cannot be used for signing! + Bad key flags selected, ECDSA cannot be used for encryption! + Bad key flags selected, ECDH cannot be used for signing! Modifying keyring %s @@ -636,18 +668,26 @@ Actual key fingerprint does not match the expected one! No key ID. This is an internal error, please file a bug report! Internal error, integrity check failed! + No master certificate found to operate on! (All revoked?) Bad primary user id specified! + Bad user id for revocation specified! Revoked user ids cannot be primary! + Expiry time cannot be "same as before" on subkey creation. This is a programming error, please file a bug report! + Fatal error decrypting master key! This is likely a programming error, please file a bug report! PGP internal exception! Signature exception! - Changing passphrase + Modifying master certifications + Changing passphrase for keyring… + Re-encrypting subkey %s with new passphrase + Setting new passphrase failed, trying again with empty old passphrase + Passphrase for subkey could not be changed! (Does it have a different one from the other keys?) Replacing certificate of previous primary user id Generating new certificate for new primary user id Modifying subkey %s - Tried to operate on missing subkey %s! - Generating new %1$s bit %2$s subkey + Tried to operate on missing subkey %s! + Adding new subkey of type %s New subkey ID: %s - Expiry date cannot be in the past! + Expiry date cannot be in the past! Revoking subkey %s Keyring successfully modified Adding user id %s @@ -657,6 +697,41 @@ Error unlocking keyring! Unlocking keyring + + Consolidating database + Consolidation was started while no database was cached! This is probably a programming error, please file a bug report. + Consolidation aborted, already running on other thread! + Saving secret keyrings + Saving public keyrings + Clearing database + Successfully consolidated database + Entering critical phase + Leaving critical phase + Deleting public keyring cache file + Deleting secret keyring cache file + Error opening database! + IO error writing public keys to cache! + IO error writing secret keys to cache! + Error reimporting public keys! + Error reimporting secret keys! + + Recovering consolidation with %1$d secret and %2$d public keys + Recovering consolidation with %1$d secret and %2$d public keys + + Recovering from unknown state + + Reimporting one public key + Reimporting %d public keys + + No public keys to reimport, skipping… + + Reimporting one secret key + Reimporting %d secret keys + + No secret keys to reimport, skipping… + Exception deleting public cache file + Exception deleting secret cache file + Click to clear cached passphrases OpenKeychain has cached %d passphrases @@ -671,6 +746,7 @@ Identity <unknown> No certificates for this key + Only self-certificates and certificates created with your keys are displayed here. Identities to certify Revocation Reason Verification Status diff --git a/OpenKeychain/src/main/res/xml/account_desc.xml b/OpenKeychain/src/main/res/xml/account_desc.xml index a6989e9ef..d29395202 100644 --- a/OpenKeychain/src/main/res/xml/account_desc.xml +++ b/OpenKeychain/src/main/res/xml/account_desc.xml @@ -1,6 +1,5 @@ - + android:accountType="org.sufficientlysecure.keychain.account" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" /> diff --git a/OpenKeychain/src/main/res/xml/adv_preferences.xml b/OpenKeychain/src/main/res/xml/adv_preferences.xml index 49f241ffe..02fa4ec40 100644 --- a/OpenKeychain/src/main/res/xml/adv_preferences.xml +++ b/OpenKeychain/src/main/res/xml/adv_preferences.xml @@ -1,20 +1,3 @@ - - - + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/xml/gen_preferences.xml b/OpenKeychain/src/main/res/xml/gen_preferences.xml index 10be61b6a..fd3c6c31a 100644 --- a/OpenKeychain/src/main/res/xml/gen_preferences.xml +++ b/OpenKeychain/src/main/res/xml/gen_preferences.xml @@ -1,22 +1,5 @@ - - - + - - 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 . ---> -
                                  - - 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 . ---> - diff --git a/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml b/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml index d8fe60e91..6871e1a5d 100644 --- a/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml +++ b/OpenKeychain/src/main/res/xml/sync_adapter_desc.xml @@ -1,6 +1,8 @@ \ No newline at end of file + android:contentAuthority="com.android.contacts" + android:accountType="org.sufficientlysecure.keychain.account" + android:supportsUploading="false" + android:userVisible="true" + android:allowParallelSyncs="false" + android:isAlwaysSyncable="true" /> \ No newline at end of file diff --git a/README.md b/README.md index 4b8f23063..7338f1071 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ For a more detailed description and installation instructions go to http://www.o ### Travis CI Build Status -Currently, some tests could fail even when everything is okay! - [![Build Status](https://travis-ci.org/open-keychain/open-keychain.png?branch=master)](https://travis-ci.org/open-keychain/open-keychain) ## How to help the project? @@ -107,6 +105,7 @@ see * Tests in https://github.com/bcgit/bc-java/tree/master/pg/src/test/java/org/bouncycastle/openpgp/test * Examples in https://github.com/bcgit/bc-java/tree/master/pg/src/main/java/org/bouncycastle/openpgp/examples * Mailinglist Archive at http://bouncy-castle.1462172.n4.nabble.com/Bouncy-Castle-Dev-f1462173.html +* Commit changelog of pg subpackage: https://github.com/bcgit/bc-java/commits/master/pg ## Notes diff --git a/Resources/graphics/function.png b/Resources/graphics/function.png new file mode 100644 index 000000000..9b8983c48 Binary files /dev/null and b/Resources/graphics/function.png differ diff --git a/Resources/graphics/function.svg b/Resources/graphics/function.svg new file mode 100644 index 000000000..97bc936ba --- /dev/null +++ b/Resources/graphics/function.svg @@ -0,0 +1,4042 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Dominik Schürmann + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Manage Keys.Encrypt your Files and Communications.Compatible with the OpenPGP Standard. + + + diff --git a/extern/KeybaseLib b/extern/KeybaseLib index e214906a5..2e8f38f6e 160000 --- a/extern/KeybaseLib +++ b/extern/KeybaseLib @@ -1 +1 @@ -Subproject commit e214906a52233bb6f230bc6759ac7be803d24bf5 +Subproject commit 2e8f38f6e38fe77b828826d3c6e8cb89a8a9aee5 diff --git a/extern/StickyListHeaders b/extern/StickyListHeaders index 706c0e447..911f8ddfd 160000 --- a/extern/StickyListHeaders +++ b/extern/StickyListHeaders @@ -1 +1 @@ -Subproject commit 706c0e447229226b6edc82ab10630d39fd0f6c38 +Subproject commit 911f8ddfd007ce65aededae7e7b79e5a8d903a43 diff --git a/extern/spongycastle b/extern/spongycastle index 918340cbc..526d23382 160000 --- a/extern/spongycastle +++ b/extern/spongycastle @@ -1 +1 @@ -Subproject commit 918340cbc6d5396764bfe94fdba07aca11cd8168 +Subproject commit 526d23382e53ce46a17a5efd9c23d884125e32ce