aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain-Test/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain-Test/src/test')
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java238
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java223
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java269
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java85
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java9
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java14
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java23
-rw-r--r--OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc17
8 files changed, 796 insertions, 82 deletions
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
new file mode 100644
index 000000000..c05f4a029
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.CertifyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.ExportResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
+import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
+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.InputData;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+import org.sufficientlysecure.keychain.util.TestingUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+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 CertifyOperationTest {
+
+ static String mPassphrase = TestingUtils.genPassphrase(true);
+
+ static UncachedKeyRing mStaticRing1, mStaticRing2;
+ static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
+ static String mKeyPhrase2 = 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());
+
+ mStaticRing1 = result.getRing();
+ }
+
+ {
+ 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("ditz");
+ parcel.mNewUnlock = new ChangeUnlockParcel(null, "1234");
+
+ 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());
+
+ mStaticRing2 = 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.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
+ providerHelper.savePublicKeyRing(mStaticRing2.extractPublicKeyRing(), new ProgressScaler());
+
+ // ok NOW log verbosely!
+ ShadowLog.stream = System.out;
+ }
+
+ @Test
+ public void testSelfCertifyFlag() throws Exception {
+
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ .getCanonicalizedPublicKeyRing(mStaticRing1.getMasterKeyId());
+ Assert.assertEquals("secret key must be marked self-certified in database",
+ // TODO this should be more correctly be VERIFIED_SELF at some point!
+ Certs.VERIFIED_SECRET, ring.getVerified());
+
+ }
+
+ @Test
+ public void testCertify() throws Exception {
+ CertifyOperation op = operationWithFakePassphraseCache(
+ mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(), mKeyPhrase1);
+
+ {
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ .getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
+ Assert.assertEquals("public key must not be marked verified prior to certification",
+ Certs.UNVERIFIED, ring.getVerified());
+ }
+
+ CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
+ actions.add(new CertifyAction(mStaticRing2.getMasterKeyId()));
+ CertifyResult result = op.certify(actions, null);
+
+ Assert.assertTrue("certification must succeed", result.success());
+
+ {
+ CanonicalizedPublicKeyRing ring = new ProviderHelper(Robolectric.application)
+ .getCanonicalizedPublicKeyRing(mStaticRing2.getMasterKeyId());
+ Assert.assertEquals("new key must be verified now",
+ Certs.VERIFIED_SECRET, ring.getVerified());
+ }
+
+ }
+
+ @Test
+ public void testCertifySelf() throws Exception {
+ CertifyOperation op = operationWithFakePassphraseCache(
+ mStaticRing1.getMasterKeyId(), mStaticRing1.getMasterKeyId(), mKeyPhrase1);
+
+ CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
+ actions.add(new CertifyAction(mStaticRing1.getMasterKeyId()));
+
+ CertifyResult result = op.certify(actions, null);
+
+ Assert.assertFalse("certification with itself must fail!", result.success());
+ Assert.assertTrue("error msg must be about self certification",
+ result.getLog().containsType(LogType.MSG_CRT_ERROR_SELF));
+ }
+
+ @Test
+ public void testCertifyNonexistent() throws Exception {
+
+ CertifyOperation op = operationWithFakePassphraseCache(null, null, mKeyPhrase1);
+
+ {
+ CertifyActionsParcel actions = new CertifyActionsParcel(mStaticRing1.getMasterKeyId());
+ actions.add(new CertifyAction(1234L));
+
+ CertifyResult result = op.certify(actions, null);
+
+ Assert.assertFalse("certification of nonexistent key must fail", result.success());
+ Assert.assertTrue("must contain error msg about not found",
+ result.getLog().containsType(LogType.MSG_CRT_WARN_NOT_FOUND));
+ }
+
+ {
+ CertifyActionsParcel actions = new CertifyActionsParcel(1234L);
+ actions.add(new CertifyAction(mStaticRing1.getMasterKeyId()));
+
+ CertifyResult result = op.certify(actions, null);
+
+ Assert.assertFalse("certification of nonexistent key must fail", result.success());
+ Assert.assertTrue("must contain error msg about not found",
+ result.getLog().containsType(LogType.MSG_CRT_ERROR_MASTER_NOT_FOUND));
+ }
+
+ }
+
+ private CertifyOperation operationWithFakePassphraseCache(
+ final Long checkMasterKeyId, final Long checkSubKeyId, final String passphrase) {
+
+ return new CertifyOperation(Robolectric.application,
+ new ProviderHelper(Robolectric.application),
+ null, null) {
+ @Override
+ public String getCachedPassphrase(long masterKeyId, long subKeyId)
+ throws NoSecretKeyException {
+ if (checkMasterKeyId != null) {
+ Assert.assertEquals("requested passphrase should be for expected master key id",
+ (long) checkMasterKeyId, masterKeyId);
+ }
+ if (checkSubKeyId != null) {
+ Assert.assertEquals("requested passphrase should be for expected sub key id",
+ (long) checkSubKeyId, subKeyId);
+ }
+ if (passphrase == null) {
+ return null;
+ }
+ return passphrase;
+ }
+ };
+ }
+
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
new file mode 100644
index 000000000..b6fdbfc6c
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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.ExportResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
+import org.sufficientlysecure.keychain.pgp.WrappedSignature;
+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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+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 ExportTest {
+
+ static String mPassphrase = TestingUtils.genPassphrase(true);
+
+ static UncachedKeyRing mStaticRing1, mStaticRing2;
+ static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
+ static String mKeyPhrase2 = 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("snips");
+ 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());
+
+ mStaticRing1 = result.getRing();
+ }
+
+ {
+ 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("snails");
+ parcel.mNewUnlock = new ChangeUnlockParcel(null, "1234");
+
+ 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());
+
+ mStaticRing2 = result.getRing();
+ }
+
+ }
+
+ @Before
+ public void setUp() {
+ ProviderHelper providerHelper = new ProviderHelper(Robolectric.application);
+
+ // don't log verbosely here, we're not here to test imports
+ ShadowLog.stream = oldShadowStream;
+
+ providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
+ providerHelper.saveSecretKeyRing(mStaticRing2, new ProgressScaler());
+
+ // ok NOW log verbosely!
+ ShadowLog.stream = System.out;
+ }
+
+ @Test
+ public void testExportAll() throws Exception {
+ ImportExportOperation op = new ImportExportOperation(Robolectric.application,
+ new ProviderHelper(Robolectric.application), null);
+
+ // make sure there is a local cert (so the later checks that there are none are meaningful)
+ Assert.assertTrue("second keyring has local certification", checkForLocal(mStaticRing2));
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ExportResult result = op.exportKeyRings(new OperationLog(), null, false, out);
+
+ Assert.assertTrue("export must be a success", result.success());
+
+ long masterKeyId1, masterKeyId2;
+ if (mStaticRing1.getMasterKeyId() < mStaticRing2.getMasterKeyId()) {
+ masterKeyId1 = mStaticRing1.getMasterKeyId();
+ masterKeyId2 = mStaticRing2.getMasterKeyId();
+ } else {
+ masterKeyId2 = mStaticRing1.getMasterKeyId();
+ masterKeyId1 = mStaticRing2.getMasterKeyId();
+ }
+
+ IteratorWithIOThrow<UncachedKeyRing> unc =
+ UncachedKeyRing.fromStream(new ByteArrayInputStream(out.toByteArray()));
+
+ {
+ Assert.assertTrue("export must have two keys (1/2)", unc.hasNext());
+ UncachedKeyRing ring = unc.next();
+ Assert.assertEquals("first exported key has correct masterkeyid",
+ masterKeyId1, ring.getMasterKeyId());
+ Assert.assertFalse("first exported key must not be secret", ring.isSecret());
+ Assert.assertFalse("there must be no local signatures in an exported keyring",
+ checkForLocal(ring));
+ }
+
+ {
+ Assert.assertTrue("export must have two keys (2/2)", unc.hasNext());
+ UncachedKeyRing ring = unc.next();
+ Assert.assertEquals("second exported key has correct masterkeyid",
+ masterKeyId2, ring.getMasterKeyId());
+ Assert.assertFalse("second exported key must not be secret", ring.isSecret());
+ Assert.assertFalse("there must be no local signatures in an exported keyring",
+ checkForLocal(ring));
+ }
+
+ out = new ByteArrayOutputStream();
+ result = op.exportKeyRings(new OperationLog(), null, true, out);
+
+ Assert.assertTrue("export must be a success", result.success());
+
+ unc = UncachedKeyRing.fromStream(new ByteArrayInputStream(out.toByteArray()));
+
+ {
+ Assert.assertTrue("export must have four keys (1/4)", unc.hasNext());
+ UncachedKeyRing ring = unc.next();
+ Assert.assertEquals("1/4 exported key has correct masterkeyid",
+ masterKeyId1, ring.getMasterKeyId());
+ Assert.assertFalse("1/4 exported key must not be public", ring.isSecret());
+ Assert.assertFalse("there must be no local signatures in an exported keyring",
+ checkForLocal(ring));
+
+ Assert.assertTrue("export must have four keys (2/4)", unc.hasNext());
+ ring = unc.next();
+ Assert.assertEquals("2/4 exported key has correct masterkeyid",
+ masterKeyId1, ring.getMasterKeyId());
+ Assert.assertTrue("2/4 exported key must be public", ring.isSecret());
+ Assert.assertFalse("there must be no local signatures in an exported keyring",
+ checkForLocal(ring));
+ }
+
+ {
+ Assert.assertTrue("export must have four keys (3/4)", unc.hasNext());
+ UncachedKeyRing ring = unc.next();
+ Assert.assertEquals("3/4 exported key has correct masterkeyid",
+ masterKeyId2, ring.getMasterKeyId());
+ Assert.assertFalse("3/4 exported key must not be public", ring.isSecret());
+ Assert.assertFalse("there must be no local signatures in an exported keyring",
+ checkForLocal(ring));
+
+ Assert.assertTrue("export must have four keys (4/4)", unc.hasNext());
+ ring = unc.next();
+ Assert.assertEquals("4/4 exported key has correct masterkeyid",
+ masterKeyId2, ring.getMasterKeyId());
+ Assert.assertTrue("4/4 exported key must be public", ring.isSecret());
+ Assert.assertFalse("there must be no local signatures in an exported keyring",
+ checkForLocal(ring));
+ }
+
+ }
+
+ /** This function checks whether or not there are any local signatures in a keyring. */
+ private boolean checkForLocal(UncachedKeyRing ring) {
+ Iterator<WrappedSignature> sigs = ring.getPublicKey().getSignatures();
+ while (sigs.hasNext()) {
+ if (sigs.next().isLocal()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
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 ebe31c9f0..008edcda4 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,25 +22,32 @@ import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.openintents.openpgp.OpenPgpSignatureResult;
import org.robolectric.*;
import org.robolectric.shadows.ShadowLog;
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.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.EditKeyResult;
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;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
import java.security.Security;
+import java.util.HashSet;
@RunWith(RobolectricTestRunner.class)
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
@@ -52,10 +59,13 @@ public class PgpEncryptDecryptTest {
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
static String mKeyPhrase2 = TestingUtils.genPassphrase(true);
+ static PrintStream oldShadowStream;
+
@BeforeClass
public static void setUpOnce() throws Exception {
Security.insertProviderAt(new BouncyCastleProvider(), 1);
- ShadowLog.stream = System.out;
+ oldShadowStream = ShadowLog.stream;
+ // ShadowLog.stream = System.out;
PgpKeyOperation op = new PgpKeyOperation(null);
@@ -68,9 +78,9 @@ public class PgpEncryptDecryptTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("bloom");
- parcel.mNewUnlock = mKeyPhrase1;
+ parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase1);
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ 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());
@@ -86,9 +96,9 @@ public class PgpEncryptDecryptTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("belle");
- parcel.mNewUnlock = mKeyPhrase2;
+ parcel.mNewUnlock = new ChangeUnlockParcel(mKeyPhrase2);
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ 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());
@@ -101,8 +111,14 @@ public class PgpEncryptDecryptTest {
public void setUp() {
ProviderHelper providerHelper = new ProviderHelper(Robolectric.application);
+ // don't log verbosely here, we're not here to test imports
+ ShadowLog.stream = oldShadowStream;
+
providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
- providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
+ providerHelper.saveSecretKeyRing(mStaticRing2, new ProgressScaler());
+
+ // ok NOW log verbosely!
+ ShadowLog.stream = System.out;
}
@Test
@@ -118,7 +134,7 @@ public class PgpEncryptDecryptTest {
InputData data = new InputData(in, in.available());
Builder b = new PgpSignEncrypt.Builder(Robolectric.application,
new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(mPassphrase, 0L)
+ null,
data, out);
b.setSymmetricPassphrase(mPassphrase);
@@ -215,11 +231,8 @@ public class PgpEncryptDecryptTest {
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
InputData data = new InputData(in, in.available());
- PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
- Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(null, null),
- data, out);
+
+ 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());
@@ -230,74 +243,230 @@ public class PgpEncryptDecryptTest {
// TODO how to test passphrase cache?
- /*{ // decryption with passphrase cached should succeed
+ { // decryption with passphrase cached should succeed
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
InputData data = new InputData(in, in.available());
- PassphraseCacheService.addCachedPassphrase(
- Robolectric.application, mStaticRing1.getMasterKeyId(),
- mStaticRing1.getMasterKeyId(), mKeyPhrase1, "dummy");
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
- PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
- Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(mKeyPhrase1, null),
- data, out);
DecryptVerifyResult result = b.build().execute();
Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
out.toByteArray(), plaintext.getBytes());
Assert.assertNull("signature should be empty", result.getSignatureResult());
- }*/
+ }
- /*{ // decryption with no passphrase provided should return status pending
+ { // decryption with no passphrase provided should return status pending
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
InputData data = new InputData(in, in.available());
- PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
- Robolectric.application,
- new ProviderHelper(Robolectric.application),
- null, // new DummyPassphraseCache(null, null),
- data, out);
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ null, mStaticRing1.getMasterKeyId(), null);
DecryptVerifyResult result = b.build().execute();
Assert.assertFalse("decryption with no passphrase must return pending", result.success());
Assert.assertTrue("decryption with no passphrase should return pending", result.isPending());
Assert.assertEquals("decryption with no passphrase should return pending passphrase",
DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE, result.getResult());
- }*/
+ }
}
- static class DummyPassphraseCache implements PassphraseCacheInterface {
+ @Test
+ public void testMultiAsymmetricEncryptDecrypt() {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ 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);
+
+ b.setEncryptionMasterKeyIds(new long[] {
+ mStaticRing1.getMasterKeyId(),
+ mStaticRing2.getMasterKeyId()
+ });
+ b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
+ SignEncryptResult result = b.build().execute();
+ Assert.assertTrue("encryption must succeed", result.success());
- String mPassphrase;
- Long mExpectedId;
- public DummyPassphraseCache(String passphrase, Long expectedId) {
- mPassphrase = passphrase;
- mExpectedId = expectedId;
+ ciphertext = out.toByteArray();
}
- @Override
- public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException {
- if (mExpectedId != null){
- Assert.assertEquals("requested passphrase must be for expected id",
- (long) mExpectedId, subKeyId);
- }
- return mPassphrase;
+ { // decryption with passphrase cached should succeed for the first key
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
+
+ DecryptVerifyResult result = b.build().execute();
+ Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertNull("signature should be empty", result.getSignatureResult());
}
- @Override
- public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
- if (mExpectedId != null){
- Assert.assertEquals("requested passphrase must be for expected id",
- (long) mExpectedId, subKeyId);
- }
- return mPassphrase;
+ { // decryption with passphrase cached should succeed for the first key
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ // allow only the second to decrypt
+ HashSet<Long> allowed = new HashSet<Long>();
+ allowed.add(mStaticRing2.getMasterKeyId());
+
+ // provide passphrase for the second, and check that the first is never asked for!
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+ b.setAllowedKeyIds(allowed);
+
+ DecryptVerifyResult result = b.build().execute();
+ Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertNull("signature should be empty", result.getSignatureResult());
}
+
+ { // decryption with passphrase cached should succeed for the other key if first is gone
+
+ // delete first key from database
+ new ProviderHelper(Robolectric.application).getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(mStaticRing1.getMasterKeyId()), null, null
+ );
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+
+ DecryptVerifyResult result = b.build().execute();
+ Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertNull("signature should be empty", result.getSignatureResult());
+ }
+
+ }
+
+ @Test
+ public void testMultiAsymmetricSignEncryptDecryptVerify() {
+
+ String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
+ byte[] ciphertext;
+
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
+
+ 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);
+
+ b.setEncryptionMasterKeyIds(new long[] {
+ mStaticRing1.getMasterKeyId(),
+ mStaticRing2.getMasterKeyId()
+ });
+ b.setSignatureMasterKeyId(mStaticRing1.getMasterKeyId());
+ b.setSignatureSubKeyId(KeyringTestingHelper.getSubkeyId(mStaticRing1, 1));
+ b.setSignaturePassphrase(mKeyPhrase1);
+ b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
+ SignEncryptResult result = b.build().execute();
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with passphrase cached should succeed for the first key
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ mKeyPhrase1, mStaticRing1.getMasterKeyId(), null);
+
+ DecryptVerifyResult result = b.build().execute();
+ Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("signature should be verified and certified",
+ OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED, result.getSignatureResult().getStatus());
+ }
+
+ { // decryption with passphrase cached should succeed for the other key if first is gone
+
+ // delete first key from database
+ new ProviderHelper(Robolectric.application).getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(mStaticRing1.getMasterKeyId()), null, null
+ );
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out,
+ mKeyPhrase2, mStaticRing2.getMasterKeyId(), null);
+
+ DecryptVerifyResult result = b.build().execute();
+ Assert.assertTrue("decryption with cached passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext",
+ out.toByteArray(), plaintext.getBytes());
+ Assert.assertEquals("signature key should be missing",
+ OpenPgpSignatureResult.SIGNATURE_KEY_MISSING,
+ result.getSignatureResult().getStatus());
+ }
+
+ }
+
+ private PgpDecryptVerify.Builder builderWithFakePassphraseCache (
+ InputData data, OutputStream out,
+ final String passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
+
+ return new PgpDecryptVerify.Builder(Robolectric.application,
+ new ProviderHelper(Robolectric.application),
+ null,
+ data, out) {
+ public PgpDecryptVerify build() {
+ return new PgpDecryptVerify(this) {
+ @Override
+ public String getCachedPassphrase(long masterKeyId, long subKeyId)
+ throws NoSecretKeyException {
+ if (checkMasterKeyId != null) {
+ Assert.assertEquals("requested passphrase should be for expected master key id",
+ (long) checkMasterKeyId, masterKeyId);
+ }
+ if (checkSubKeyId != null) {
+ Assert.assertEquals("requested passphrase should be for expected sub key id",
+ (long) checkSubKeyId, subKeyId);
+ }
+ if (passphrase == null) {
+ return null;
+ }
+ return passphrase;
+ }
+ };
+ }
+ };
}
}
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 79778c6d0..52115a76d 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
@@ -40,9 +40,11 @@ import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.support.KeyringBuilder;
@@ -91,14 +93,15 @@ public class PgpKeyOperationTest {
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
PgpKeyOperation op = new PgpKeyOperation(null);
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ 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());
staticRing = result.getRing();
+ staticRing = staticRing.canonicalize(new OperationLog(), 0).getUncachedKeyRing();
// we sleep here for a second, to make sure all new certificates have different timestamps
Thread.sleep(1000);
@@ -127,7 +130,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, new Random().nextInt(256)+255, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
assertFailure("creating ring with < 512 bytes keysize should fail", parcel,
LogType.MSG_CR_ERROR_KEYSIZE_512);
@@ -138,7 +141,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ELGAMAL, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
assertFailure("creating ring with ElGamal master key should fail", parcel,
LogType.MSG_CR_ERROR_FLAGS_ELGAMAL);
@@ -149,7 +152,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, null));
parcel.mAddUserIds.add("lotus");
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
assertFailure("creating master key with null expiry should fail", parcel,
LogType.MSG_CR_ERROR_NULL_EXPIRY);
@@ -160,7 +163,7 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
assertFailure("creating ring with non-certifying master key should fail", parcel,
LogType.MSG_CR_ERROR_NO_CERTIFY);
@@ -170,7 +173,7 @@ public class PgpKeyOperationTest {
parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.CERTIFY_OTHER, 0L));
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
assertFailure("creating ring without user ids should fail", parcel,
LogType.MSG_CR_ERROR_NO_USER_ID);
@@ -179,7 +182,7 @@ public class PgpKeyOperationTest {
{
parcel.reset();
parcel.mAddUserIds.add("shy");
- parcel.mNewUnlock = passphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(passphrase);
assertFailure("creating ring with no master key should fail", parcel,
LogType.MSG_CR_ERROR_NO_MASTER);
@@ -910,8 +913,10 @@ public class PgpKeyOperationTest {
public void testPassphraseChange() throws Exception {
// change passphrase to empty
- parcel.mNewUnlock = "";
- UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+ parcel.mNewUnlock = new ChangeUnlockParcel("");
+ // note that canonicalization here necessarily strips the empty notation packet
+ UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB,
+ passphrase);
Assert.assertEquals("exactly three packets should have been modified (the secret keys)",
3, onlyB.size());
@@ -923,7 +928,7 @@ public class PgpKeyOperationTest {
// modify keyring, change to non-empty passphrase
String otherPassphrase = TestingUtils.genPassphrase(true);
- parcel.mNewUnlock = otherPassphrase;
+ parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase);
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, "");
Assert.assertEquals("exactly three packets should have been modified (the secret keys)",
@@ -948,7 +953,7 @@ public class PgpKeyOperationTest {
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
String otherPassphrase2 = TestingUtils.genPassphrase(true);
- parcel.mNewUnlock = otherPassphrase2;
+ parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase2);
{
// if we replace a secret key with one without passphrase
modified = KeyringTestingHelper.removePacket(modified, sKeyNoPassphrase.position);
@@ -957,7 +962,7 @@ public class PgpKeyOperationTest {
// 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);
+ PgpEditKeyResult 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());
@@ -973,7 +978,7 @@ public class PgpKeyOperationTest {
PgpKeyOperation op = new PgpKeyOperation(null);
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
- EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, otherPassphrase2);
+ PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, otherPassphrase2);
Assert.assertTrue("key modification must succeed", result.success());
Assert.assertTrue("log must contain a failed passphrase change warning",
result.getLog().containsType(LogType.MSG_MF_PASSPHRASE_FAIL));
@@ -981,6 +986,46 @@ public class PgpKeyOperationTest {
}
+ @Test
+ public void testUnlockPin() throws Exception {
+
+ String pin = "5235125";
+
+ // change passphrase to a pin type
+ parcel.mNewUnlock = new ChangeUnlockParcel(null, pin);
+ UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
+
+ Assert.assertEquals("exactly three packets should have been added (the secret keys + notation packet)",
+ 3, onlyA.size());
+ Assert.assertEquals("exactly four packets should have been added (the secret keys + notation packet)",
+ 4, onlyB.size());
+
+ RawPacket dkSig = onlyB.get(1);
+ Assert.assertEquals("second modified packet should be notation data",
+ PacketTags.SIGNATURE, dkSig.tag);
+
+ // check that notation data contains pin
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
+ modified.getEncoded(), false, 0);
+ Assert.assertEquals("secret key type should be 'pin' after this",
+ SecretKeyType.PIN,
+ secretRing.getSecretKey().getSecretKeyType());
+
+ // need to sleep for a sec, so the timestamp changes for notation data
+ Thread.sleep(1000);
+
+ {
+ parcel.mNewUnlock = new ChangeUnlockParcel("phrayse", null);
+ applyModificationWithChecks(parcel, modified, onlyA, onlyB, pin, true, false);
+
+ Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)",
+ 4, onlyA.size());
+ Assert.assertEquals("exactly three packets should have been added (no more notation packet)",
+ 3, onlyB.size());
+ }
+
+ }
+
private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
UncachedKeyRing ring,
ArrayList<RawPacket> onlyA,
@@ -1011,7 +1056,7 @@ public class PgpKeyOperationTest {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
PgpKeyOperation op = new PgpKeyOperation(null);
- EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase);
+ PgpEditKeyResult 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);
@@ -1068,7 +1113,7 @@ public class PgpKeyOperationTest {
private void assertFailure(String reason, SaveKeyringParcel parcel, LogType expected) {
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertFalse(reason, result.success());
Assert.assertNull(reason, result.getRing());
@@ -1082,7 +1127,7 @@ public class PgpKeyOperationTest {
throws Exception {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
- EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase);
+ PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase);
Assert.assertFalse(reason, result.success());
Assert.assertNull(reason, result.getRing());
@@ -1096,7 +1141,7 @@ public class PgpKeyOperationTest {
throws Exception {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
- EditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase);
+ PgpEditKeyResult result = op.modifySecretKeyRing(secretRing, parcel, passphrase);
Assert.assertFalse(reason, result.success());
Assert.assertNull(reason, result.getRing());
@@ -1107,7 +1152,7 @@ public class PgpKeyOperationTest {
private UncachedKeyRing assertCreateSuccess(String reason, SaveKeyringParcel parcel) {
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue(reason, result.success());
Assert.assertNotNull(reason, result.getRing());
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 cbdbd0e72..721d1a51d 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
@@ -53,11 +53,12 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.spongycastle.util.Strings;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
@@ -104,14 +105,16 @@ public class UncachedKeyringCanonicalizeTest {
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = "";
+ parcel.mNewUnlock = new ChangeUnlockParcel("");
PgpKeyOperation op = new PgpKeyOperation(null);
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success());
staticRing = result.getRing();
Assert.assertNotNull("initial test key creation must succeed", staticRing);
+ staticRing = staticRing.canonicalize(new OperationLog(), 0).getUncachedKeyRing();
+
// just for later reference
totalPackets = 9;
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 006c77fbc..7f6f480d4 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
@@ -33,9 +33,11 @@ import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.util.Strings;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -96,13 +98,14 @@ public class UncachedKeyringMergeTest {
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = "";
+ parcel.mNewUnlock = new ChangeUnlockParcel("");
PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog();
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
staticRingA = result.getRing();
+ staticRingA = staticRingA.canonicalize(new OperationLog(), 0).getUncachedKeyRing();
}
{
@@ -112,12 +115,13 @@ public class UncachedKeyringMergeTest {
parcel.mAddUserIds.add("shy");
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = "";
+ parcel.mNewUnlock = new ChangeUnlockParcel("");
PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog();
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
staticRingB = result.getRing();
+ staticRingB = staticRingB.canonicalize(new OperationLog(), 0).getUncachedKeyRing();
}
Assert.assertNotNull("initial test key creation must succeed", staticRingA);
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 d8d825783..a3c58a5c8 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
@@ -26,13 +26,16 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.ShadowLog;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
+import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.Iterator;
@RunWith(RobolectricTestRunner.class)
@@ -57,10 +60,10 @@ public class UncachedKeyringTest {
parcel.mAddUserIds.add("twi");
parcel.mAddUserIds.add("pink");
// passphrase is tested in PgpKeyOperationTest, just use empty here
- parcel.mNewUnlock = "";
+ parcel.mNewUnlock = new ChangeUnlockParcel("");
PgpKeyOperation op = new PgpKeyOperation(null);
- EditKeyResult result = op.createSecretKeyRing(parcel);
+ PgpEditKeyResult result = op.createSecretKeyRing(parcel);
staticRing = result.getRing();
staticPubRing = staticRing.extractPublicKeyRing();
@@ -108,7 +111,7 @@ public class UncachedKeyringTest {
ring.encodeArmored(out, "OpenKeychain");
pubRing.encodeArmored(out, "OpenKeychain");
- Iterator<UncachedKeyRing> it =
+ IteratorWithIOThrow<UncachedKeyRing> it =
UncachedKeyRing.fromStream(new ByteArrayInputStream(out.toByteArray()));
Assert.assertTrue("there should be two rings in the stream", it.hasNext());
Assert.assertArrayEquals("first ring should be the first we put in",
@@ -128,4 +131,16 @@ public class UncachedKeyringTest {
pubRing.extractPublicKeyRing();
}
+ @Test(expected = IOException.class)
+ public void testBrokenVersionCert() throws Throwable {
+ // this is a test for one of the patches we use on top of stock bouncycastle, which
+ // returns an IOException rather than a RuntimeException in case of a bad certificate
+ // version byte
+ readRingFromResource("/test-keys/broken_cert_version.asc");
+ }
+
+ UncachedKeyRing readRingFromResource(String name) throws Throwable {
+ return UncachedKeyRing.fromStream(UncachedKeyringTest.class.getResourceAsStream(name)).next();
+ }
+
}
diff --git a/OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc b/OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc
new file mode 100644
index 000000000..e2d2abd8e
--- /dev/null
+++ b/OpenKeychain-Test/src/test/resources/test-keys/broken_cert_version.asc
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFSl5DIBCADqsGJJ8RhV4Uw6a3Q48QyTMrUtvZquOTlLVaqOdEFZNL5/OBal
+prft9LNkcOkIVA89Hdn715WwkmG2OJiJoQ/ZAKwal1CPGm4Q8kZIM7k57ISJL6J5
+300e7UIznc74XbG7eFNxNcjCM9wG12vW2rFwc+ogJtkBSf0IXukPwtUkRK+H5ufO
+lpqS5NNZfiGbNQCrb+YsGZNRk4QTGR6WGyaIRHlcG8G00VPGNSauTqe/11MO9MoF
+BvPgFeur3nefWunCQ+uDmzIEs8r94gaHu3LWbctd5w5x/o/PDfTSSiO+U8zzXrKC
+4ZpEl5bk7t7jH1hYMLWyO6nn0vWTOMO1EYLBABEBAAG0GGJyb2tlbiBzaWduYXR1
+cmUgdmVyc2lvbokBOMATAQIAIgUCVKXkMgIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC
+HgECF4AACgkQDe00lH/2SnprLggAh64TsdHDfIhTNc1DeJLCuvuHsitAcUdEEnue
+yJjodxboKNSplIwnmb5CpM3P8f736dNaW77Yd6aO4IeAy6cBlxT1tSRkJMsp+cBt
+kBa3lRr+GnWZlLZs3coL2g0t5RbuyYKyQxm2qvgFJGi/7Qfty5nJOW5U1ElT3VT8
+jISNdQdDAIaBsCE+TuyW3VsP3PqnJ7x14K7VhkFuCyvYB9paLcJBnan93R0Ja0Ip
+Cv1pbrNxXp0UELf0RYc2X5C1m6otZ9LKf3PmzxlEkApkb1TZUEBak2Za5p99koZT
++pg/XpZPyawi+gZeYkBAohxRGmzG/a4L+YacAZHbchfN0eG7lg==
+=mxTR
+-----END PGP PUBLIC KEY BLOCK-----