diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-02 14:39:28 +0100 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2015-03-02 14:39:28 +0100 | 
| commit | 2f83291920e024b0f8038fe0caa747051b41cf1c (patch) | |
| tree | 780fc0975e56b3dbd8a1eb4cb2988ac3e2e23ea1 /OpenKeychain-Test | |
| parent | aa22a0defcf94d5f734d3a00288f5c8d916d2e57 (diff) | |
| parent | 4e29d027af05fc574dc5398d2fb3afcdf3defc70 (diff) | |
| download | open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.gz open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.tar.bz2 open-keychain-2f83291920e024b0f8038fe0caa747051b41cf1c.zip | |
NON-WORKING Merge branch 'development' into linked-identities
Conflicts:
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java
	OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
	OpenKeychain/src/main/res/values/strings.xml
	extern/spongycastle
Diffstat (limited to 'OpenKeychain-Test')
4 files changed, 311 insertions, 51 deletions
| diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java new file mode 100644 index 000000000..40ade064b --- /dev/null +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.operations; + +import org.junit.Assert; +import org.junit.Before; +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.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; +import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; +import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; +import org.sufficientlysecure.keychain.util.ProgressScaler; +import org.sufficientlysecure.keychain.util.TestingUtils; + +import java.io.PrintStream; +import java.security.Security; +import java.util.Iterator; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class PromoteKeyOperationTest { + +    static UncachedKeyRing mStaticRing; +    static String mKeyPhrase1 = TestingUtils.genPassphrase(true); + +    static PrintStream oldShadowStream; + +    @BeforeClass +    public static void setUpOnce() throws Exception { +        Security.insertProviderAt(new BouncyCastleProvider(), 1); +        oldShadowStream = ShadowLog.stream; +        // ShadowLog.stream = System.out; + +        PgpKeyOperation op = new PgpKeyOperation(null); + +        { +            SaveKeyringParcel parcel = new SaveKeyringParcel(); +            parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( +                    Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L)); +            parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( +                    Algorithm.DSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); +            parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( +                    Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L)); +            parcel.mAddUserIds.add("derp"); +            parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1); + +            PgpEditKeyResult result = op.createSecretKeyRing(parcel); +            Assert.assertTrue("initial test key creation must succeed", result.success()); +            Assert.assertNotNull("initial test key creation must succeed", result.getRing()); + +            mStaticRing = result.getRing(); +        } + +    } + +    @Before +    public void setUp() throws Exception { +        ProviderHelper providerHelper = new ProviderHelper(Robolectric.application); + +        // don't log verbosely here, we're not here to test imports +        ShadowLog.stream = oldShadowStream; + +        providerHelper.savePublicKeyRing(mStaticRing.extractPublicKeyRing(), new ProgressScaler()); + +        // ok NOW log verbosely! +        ShadowLog.stream = System.out; +    } + +    @Test +    public void testPromote() throws Exception { +        PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application, +                new ProviderHelper(Robolectric.application), null, null); + +        PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId()); + +        Assert.assertTrue("promotion must succeed", result.success()); + +        { +            CachedPublicKeyRing ring = new ProviderHelper(Robolectric.application) +                    .getCachedPublicKeyRing(mStaticRing.getMasterKeyId()); +            Assert.assertTrue("key must have a secret now", ring.hasAnySecret()); + +            Iterator<UncachedPublicKey> it = mStaticRing.getPublicKeys(); +            while (it.hasNext()) { +                long keyId = it.next().getKeyId(); +                Assert.assertEquals("all subkeys must be divert-to-card", +                        SecretKeyType.GNU_DUMMY, ring.getSecretKeyType(keyId)); +            } +        } + +        // second attempt should fail +        result = op.execute(mStaticRing.getMasterKeyId()); +        Assert.assertFalse("promotion of secret key must fail", result.success()); + +    } + +} diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java index 008edcda4..d782230c7 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java @@ -22,6 +22,7 @@ import org.junit.Before;  import org.junit.BeforeClass;  import org.junit.Test;  import org.junit.runner.RunWith; +import org.openintents.openpgp.OpenPgpMetadata;  import org.openintents.openpgp.OpenPgpSignatureResult;  import org.robolectric.*;  import org.robolectric.shadows.ShadowLog; @@ -29,13 +30,12 @@ import org.spongycastle.bcpg.sig.KeyFlags;  import org.spongycastle.jce.provider.BouncyCastleProvider;  import org.spongycastle.openpgp.PGPEncryptedData;  import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; -import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt.Builder; +import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;  import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;  import org.sufficientlysecure.keychain.provider.ProviderHelper;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;  import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;  import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;  import org.sufficientlysecure.keychain.support.KeyringTestingHelper;  import org.sufficientlysecure.keychain.util.InputData; @@ -131,15 +131,17 @@ public class PgpEncryptDecryptTest {              ByteArrayOutputStream out = new ByteArrayOutputStream();              ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); +            PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application, +                    new ProviderHelper(Robolectric.application), null); +              InputData data = new InputData(in, in.available()); -            Builder b = new PgpSignEncrypt.Builder(Robolectric.application, -                    new ProviderHelper(Robolectric.application), -                    null, -                    data, out); +            PgpSignEncryptInput b = new PgpSignEncryptInput();              b.setSymmetricPassphrase(mPassphrase);              b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128); -            SignEncryptResult result = b.build().execute(); + +            PgpSignEncryptResult result = op.execute(b, data, out); +              Assert.assertTrue("encryption must succeed", result.success());              ciphertext = out.toByteArray(); @@ -161,6 +163,10 @@ public class PgpEncryptDecryptTest {              Assert.assertArrayEquals("decrypted ciphertext should equal plaintext",                      out.toByteArray(), plaintext.getBytes());              Assert.assertNull("signature should be an error", result.getSignatureResult()); + +            OpenPgpMetadata metadata = result.getDecryptMetadata(); +            Assert.assertEquals("filesize must be correct", +                    out.toByteArray().length, metadata.getOriginalSize());          }          { // decryption with a bad passphrase should fail @@ -210,16 +216,15 @@ public class PgpEncryptDecryptTest {              ByteArrayOutputStream out = new ByteArrayOutputStream();              ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); +            PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application, +                    new ProviderHelper(Robolectric.application), null); +              InputData data = new InputData(in, in.available()); -            Builder b = new PgpSignEncrypt.Builder( -                    Robolectric.application, -                    new ProviderHelper(Robolectric.application), -                    null, // new DummyPassphraseCache(mPassphrase, 0L), -                    data, out); +            PgpSignEncryptInput b = new PgpSignEncryptInput();              b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });              b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128); -            SignEncryptResult result = b.build().execute(); +            PgpSignEncryptResult result = op.execute(b, data, out);              Assert.assertTrue("encryption must succeed", result.success());              ciphertext = out.toByteArray(); @@ -239,6 +244,11 @@ public class PgpEncryptDecryptTest {              Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",                      out.toByteArray(), plaintext.getBytes());              Assert.assertNull("signature be empty", result.getSignatureResult()); + +            OpenPgpMetadata metadata = result.getDecryptMetadata(); +            Assert.assertEquals("filesize must be correct", +                    out.toByteArray().length, metadata.getOriginalSize()); +          }          // TODO how to test passphrase cache? @@ -286,19 +296,19 @@ public class PgpEncryptDecryptTest {              ByteArrayOutputStream out = new ByteArrayOutputStream();              ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); +            PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application, +                    new ProviderHelper(Robolectric.application), null); +              InputData data = new InputData(in, in.available()); -            Builder b = new PgpSignEncrypt.Builder( -                    Robolectric.application, -                    new ProviderHelper(Robolectric.application), -                    null, // new DummyPassphraseCache(mPassphrase, 0L), -                    data, out); +            PgpSignEncryptInput b = new PgpSignEncryptInput();              b.setEncryptionMasterKeyIds(new long[] {                      mStaticRing1.getMasterKeyId(),                      mStaticRing2.getMasterKeyId()              });              b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128); -            SignEncryptResult result = b.build().execute(); + +            PgpSignEncryptResult result = op.execute(b, data, out);              Assert.assertTrue("encryption must succeed", result.success());              ciphertext = out.toByteArray(); @@ -318,6 +328,10 @@ public class PgpEncryptDecryptTest {              Assert.assertArrayEquals("decrypted ciphertext with cached passphrase  should equal plaintext",                      out.toByteArray(), plaintext.getBytes());              Assert.assertNull("signature should be empty", result.getSignatureResult()); + +            OpenPgpMetadata metadata = result.getDecryptMetadata(); +            Assert.assertEquals("filesize must be correct", +                    out.toByteArray().length, metadata.getOriginalSize());          }          { // decryption with passphrase cached should succeed for the first key @@ -375,12 +389,11 @@ public class PgpEncryptDecryptTest {              ByteArrayOutputStream out = new ByteArrayOutputStream();              ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); +            PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application, +                    new ProviderHelper(Robolectric.application), null); +              InputData data = new InputData(in, in.available()); -            Builder b = new PgpSignEncrypt.Builder( -                    Robolectric.application, -                    new ProviderHelper(Robolectric.application), -                    null, // new DummyPassphraseCache(mPassphrase, 0L), -                    data, out); +            PgpSignEncryptInput b = new PgpSignEncryptInput();              b.setEncryptionMasterKeyIds(new long[] {                      mStaticRing1.getMasterKeyId(), @@ -390,7 +403,8 @@ public class PgpEncryptDecryptTest {              b.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));              b.setSignaturePassphrase(mKeyPhrase1);              b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128); -            SignEncryptResult result = b.build().execute(); + +            PgpSignEncryptResult result = op.execute(b, data, out);              Assert.assertTrue("encryption must succeed", result.success());              ciphertext = out.toByteArray(); @@ -411,6 +425,10 @@ public class PgpEncryptDecryptTest {                      out.toByteArray(), plaintext.getBytes());              Assert.assertEquals("signature should be verified and certified",                      OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED, result.getSignatureResult().getStatus()); + +            OpenPgpMetadata metadata = result.getDecryptMetadata(); +            Assert.assertEquals("filesize must be correct", +                    out.toByteArray().length, metadata.getOriginalSize());          }          { // decryption with passphrase cached should succeed for the other key if first is gone @@ -438,6 +456,59 @@ public class PgpEncryptDecryptTest {      } +    @Test +    public void testForeignEncoding () throws Exception { +        String plaintext = "ウィキペディア"; +        byte[] plaindata = plaintext.getBytes("iso-2022-jp"); + +        { // some quick sanity checks +            Assert.assertEquals(plaintext, new String(plaindata, "iso-2022-jp")); +            Assert.assertNotEquals(plaintext, new String(plaindata, "utf-8")); +        } + +        byte[] ciphertext; +        { // encrypt data with a given passphrase +            ByteArrayOutputStream out = new ByteArrayOutputStream(); +            ByteArrayInputStream in = new ByteArrayInputStream(plaindata); + +            PgpSignEncryptOperation op = new PgpSignEncryptOperation(Robolectric.application, +                    new ProviderHelper(Robolectric.application), null); + +            InputData data = new InputData(in, in.available()); +            PgpSignEncryptInput b = new PgpSignEncryptInput(); + +            b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() }); +            b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128); +            // this only works with ascii armored output! +            b.setEnableAsciiArmorOutput(true); +            b.setCharset("iso-2022-jp"); +            PgpSignEncryptResult result = op.execute(b, data, out); +            Assert.assertTrue("encryption must succeed", result.success()); + +            ciphertext = out.toByteArray(); +        } + +        { // decryption with provided passphrase should yield the same result + +            ByteArrayOutputStream out = new ByteArrayOutputStream(); +            ByteArrayInputStream in = new ByteArrayInputStream(ciphertext); +            InputData data = new InputData(in, in.available()); + +            PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out, null, null, null); +            b.setPassphrase(mKeyPhrase1); +            DecryptVerifyResult result = b.build().execute(); +            Assert.assertTrue("decryption with provided passphrase must succeed", result.success()); +            Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes", +                    out.toByteArray(), plaindata); +            Assert.assertEquals("charset should be read correctly", +                    "iso-2022-jp", result.getCharset()); +            Assert.assertEquals("decrypted ciphertext should equal plaintext", +                    new String(out.toByteArray(), result.getCharset()), plaintext); +            Assert.assertNull("signature be empty", result.getSignatureResult()); +        } + +    } +      private PgpDecryptVerify.Builder builderWithFakePassphraseCache (              InputData data, OutputStream out,              final String passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) { 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 3ea88aac6..dd2feb825 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 @@ -231,7 +231,7 @@ public class PgpKeyOperationTest {                  ring.getPublicKey().getCreationTime().after(new Date(new Date().getTime()-1000*120)));          Assert.assertNull("key ring should not expire", -                ring.getPublicKey().getExpiryTime()); +                ring.getPublicKey().getUnsafeExpiryTimeForTesting());          Assert.assertEquals("first (master) key can certify",                  KeyFlags.CERTIFY_OTHER, (long) subkeys.get(0).getKeyUsage()); @@ -342,9 +342,9 @@ public class PgpKeyOperationTest {          Assert.assertNotNull("new key is not null", newKey);          Assert.assertNotNull("added key must have an expiry date", -                newKey.getExpiryTime()); +                newKey.getUnsafeExpiryTimeForTesting());          Assert.assertEquals("added key must have expected expiry date", -                expiry, newKey.getExpiryTime().getTime()/1000); +                expiry, newKey.getUnsafeExpiryTimeForTesting().getTime()/1000);          Assert.assertEquals("added key must have expected flags",                  flags, (long) newKey.getKeyUsage());          Assert.assertEquals("added key must have expected bitsize", @@ -403,9 +403,9 @@ public class PgpKeyOperationTest {                      ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());              Assert.assertNotNull("modified key must have an expiry date", -                    modified.getPublicKey(keyId).getExpiryTime()); +                    modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());              Assert.assertEquals("modified key must have expected expiry date", -                    expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000); +                    expiry, modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting().getTime()/1000);              Assert.assertEquals("modified key must have same flags as before",                      ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());          } @@ -417,9 +417,9 @@ public class PgpKeyOperationTest {              modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);              Assert.assertNotNull("modified key must have an expiry date", -                    modified.getPublicKey(keyId).getExpiryTime()); +                    modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());              Assert.assertEquals("modified key must have expected expiry date", -                    expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000); +                    expiry, modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting().getTime()/1000);              Assert.assertEquals("modified key must have same flags as before",                      ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());          } @@ -443,9 +443,9 @@ public class PgpKeyOperationTest {              Assert.assertEquals("modified key must have expected flags",                      flags, (long) modified.getPublicKey(keyId).getKeyUsage());              Assert.assertNotNull("key must retain its expiry", -                    modified.getPublicKey(keyId).getExpiryTime()); +                    modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());              Assert.assertEquals("key expiry must be unchanged", -                    expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000); +                    expiry, modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting().getTime()/1000);          }          { // expiry of 0 should be "no expiry" @@ -463,7 +463,7 @@ public class PgpKeyOperationTest {              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()); +            Assert.assertNull("key must not expire anymore", modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());          }          { // a past expiry should fail @@ -517,9 +517,9 @@ public class PgpKeyOperationTest {                      PacketTags.SIGNATURE, onlyB.get(1).tag);              Assert.assertNotNull("modified key must have an expiry date", -                    modified.getPublicKey().getExpiryTime()); +                    modified.getPublicKey().getUnsafeExpiryTimeForTesting());              Assert.assertEquals("modified key must have expected expiry date", -                    expiry, modified.getPublicKey().getExpiryTime().getTime() / 1000); +                    expiry, modified.getPublicKey().getUnsafeExpiryTimeForTesting().getTime() / 1000);              Assert.assertEquals("modified key must have same flags as before",                      ring.getPublicKey().getKeyUsage(), modified.getPublicKey().getKeyUsage());          } @@ -531,9 +531,9 @@ public class PgpKeyOperationTest {              modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);              Assert.assertNotNull("modified key must have an expiry date", -                    modified.getPublicKey(keyId).getExpiryTime()); +                    modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());              Assert.assertEquals("modified key must have expected expiry date", -                    expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000); +                    expiry, modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting().getTime() / 1000);              Assert.assertEquals("modified key must have same flags as before",                      ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());          } @@ -547,17 +547,29 @@ public class PgpKeyOperationTest {              Assert.assertEquals("modified key must have expected flags",                      flags, (long) modified.getPublicKey(keyId).getKeyUsage());              Assert.assertNotNull("key must retain its expiry", -                    modified.getPublicKey(keyId).getExpiryTime()); +                    modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());              Assert.assertEquals("key expiry must be unchanged", -                    expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000); +                    expiry, modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting().getTime()/1000);          }          { // expiry of 0 should be "no expiry" + +            // even if there is a non-expiring user id while all others are revoked, it doesn't count! +            // for this purpose we revoke one while they still have expiry times +            parcel.reset(); +            parcel.mRevokeUserIds.add("aloe"); +            modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); +              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()); +            // for this check, it is relevant that we DON'T use the unsafe one! +            Assert.assertNull("key must not expire anymore", +                    modified.canonicalize(new OperationLog(), 0).getPublicKey().getExpiryTime()); +            // make sure the unsafe one behaves incorrectly as expected +            Assert.assertNotNull("unsafe expiry must yield wrong result from revoked user id", +                    modified.getPublicKey(keyId).getUnsafeExpiryTimeForTesting());          }          { // if we revoke everything, nothing is left to properly sign... @@ -609,7 +621,7 @@ public class PgpKeyOperationTest {                  ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());          Assert.assertTrue("subkey must actually be revoked", -                modified.getPublicKey().isRevoked()); +                modified.getPublicKey().isMaybeRevoked());      } @@ -653,13 +665,14 @@ public class PgpKeyOperationTest {                      ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());              Assert.assertTrue("subkey must actually be revoked", -                    modified.getPublicKey(keyId).isRevoked()); +                    modified.getPublicKey(keyId).isMaybeRevoked());          }          { // re-add second subkey              parcel.reset(); -            parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, null)); +            // re-certify the revoked subkey +            parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true));              modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB); @@ -690,7 +703,7 @@ public class PgpKeyOperationTest {                      ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());              Assert.assertFalse("subkey must no longer be revoked", -                    modified.getPublicKey(keyId).isRevoked()); +                    modified.getPublicKey(keyId).isMaybeRevoked());              Assert.assertEquals("subkey must have the same usage flags as before",                      flags, (long) modified.getPublicKey(keyId).getKeyUsage()); @@ -701,7 +714,7 @@ public class PgpKeyOperationTest {      public void testSubkeyStrip() throws Exception {          long keyId = KeyringTestingHelper.getSubkeyId(ring, 1); -        parcel.mStripSubKeys.add(keyId); +        parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));          applyModificationWithChecks(parcel, ring, onlyA, onlyB);          Assert.assertEquals("one extra packet in original", 1, onlyA.size()); @@ -727,7 +740,7 @@ public class PgpKeyOperationTest {      public void testMasterStrip() throws Exception {          long keyId = ring.getMasterKeyId(); -        parcel.mStripSubKeys.add(keyId); +        parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null));          applyModificationWithChecks(parcel, ring, onlyA, onlyB);          Assert.assertEquals("one extra packet in original", 1, onlyA.size()); @@ -746,6 +759,44 @@ public class PgpKeyOperationTest {          Assert.assertEquals("new packet secret key data should have length zero",                  0, ((SecretKeyPacket) p).getSecretKeyData().length);          Assert.assertNull("new packet should have no iv data", ((SecretKeyPacket) p).getIV()); +    } + +    @Test +    public void testRestrictedStrip() throws Exception { + +        long keyId = KeyringTestingHelper.getSubkeyId(ring, 1); +        UncachedKeyRing modified; + +        { // we should be able to change the stripped/divert status of subkeys without passphrase +            parcel.reset(); +            parcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, null)); +            modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, null); +            Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); +            Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket(); +            Assert.assertEquals("new packet should have GNU_DUMMY S2K type", +                    S2K.GNU_DUMMY_S2K, ((SecretKeyPacket) p).getS2K().getType()); +            Assert.assertEquals("new packet should have GNU_DUMMY protection mode stripped", +                    S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY, ((SecretKeyPacket) p).getS2K().getProtectionMode()); +        } + +        { // and again, changing to divert-to-card +            parcel.reset(); +            byte[] serial = new byte[] { +                    0x6a, 0x6f, 0x6c, 0x6f, 0x73, 0x77, 0x61, 0x67, +                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +            }; +            parcel.mChangeSubKeys.add(new SubkeyChange(keyId, false, serial)); +            modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, null); +            Assert.assertEquals("one extra packet in modified", 1, onlyB.size()); +            Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket(); +            Assert.assertEquals("new packet should have GNU_DUMMY S2K type", +                    S2K.GNU_DUMMY_S2K, ((SecretKeyPacket) p).getS2K().getType()); +            Assert.assertEquals("new packet should have GNU_DUMMY protection mode divert-to-card", +                    S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, ((SecretKeyPacket) p).getS2K().getProtectionMode()); +            Assert.assertArrayEquals("new packet should have correct serial number as iv", +                    serial, ((SecretKeyPacket) p).getIV()); + +        }      } @@ -1092,6 +1143,17 @@ public class PgpKeyOperationTest {      } +    @Test +    public void testRestricted () throws Exception { + +        CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0); + +        parcel.mAddUserIds.add("discord"); +        PgpKeyOperation op = new PgpKeyOperation(null); +        PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, null); +        Assert.assertFalse("non-restricted operations should fail without passphrase", result.success()); +    } +      private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,                                                                 UncachedKeyRing ring,                                                                 ArrayList<RawPacket> onlyA, 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 index e4a1d62ae..171ecc377 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/provider/ProviderHelperSaveTest.java @@ -106,8 +106,8 @@ public class ProviderHelperSaveTest {          UncachedKeyRing pub = readRingFromResource("/test-keys/mailvelope_07_no_key_flags.asc");          long keyId = pub.getMasterKeyId(); -        Assert.assertNull("key flags should be null", -                pub.canonicalize(new OperationLog(), 0).getPublicKey().getKeyUsage()); +        Assert.assertEquals("key flags should be zero", +                0, (long) pub.canonicalize(new OperationLog(), 0).getPublicKey().getKeyUsage());          mProviderHelper.savePublicKeyRing(pub); @@ -117,7 +117,8 @@ public class ProviderHelperSaveTest {          Assert.assertEquals("master key should be encryption key", keyId, pubRing.getEncryptId());          Assert.assertEquals("master key should be encryption key (cached)", keyId, cachedRing.getEncryptId()); -        Assert.assertNull("canonicalized key flags should be null", pubRing.getPublicKey().getKeyUsage()); +        Assert.assertEquals("canonicalized key flags should be zero", +                0, (long) pubRing.getPublicKey().getKeyUsage());          Assert.assertTrue("master key should be able to certify", pubRing.getPublicKey().canCertify());          Assert.assertTrue("master key should be allowed to sign", pubRing.getPublicKey().canSign());          Assert.assertTrue("master key should be able to encrypt", pubRing.getPublicKey().canEncrypt()); | 
