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..110fec2d3 --- /dev/null +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java @@ -0,0 +1,211 @@ +/* + * 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.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.EditKeyResult; +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.PgpKeyOperation; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +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); + + 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()); + + 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"); + + 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()); + + 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 testExportAllPublic() throws Exception { + ImportExportOperation op = new ImportExportOperation(Robolectric.application, + new ProviderHelper(Robolectric.application), null); + + 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(); + } + + Iterator 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()); + 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()); + 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()); + 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()); + 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()); + 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()); + checkForLocal(ring); + } + + } + + private void checkForLocal(UncachedKeyRing ring) { + Iterator sigs = ring.getPublicKey().getSignatures(); + while (sigs.hasNext()) { + Assert.assertFalse("there must be no local signatures in an exported keyring", + sigs.next().isLocal()); + } + } + +}