diff --git a/.gitignore b/.gitignore index 1dfe84d5a..68f5b5a9e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ ant.properties .gradle build gradle.properties +# this is in here because the prepare-tests thing modifies it, and we DON'T +# want this to be commited. use git add -f to work on this file. +settings.gradle #Maven target diff --git a/.travis.yml b/.travis.yml index fd6e55ea7..ef69eb556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ before_install: # Install required Android components. #- echo "y" | android update sdk -a --filter build-tools-19.1.0,android-19,platform-tools,extra-android-support,extra-android-m2repository --no-ui --force - ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --no-ui --all --force --filter build-tools-19.1.0,android-19,platform-tools,extra-android-support,extra-android-m2repository - - ./install-custom-gradle-test-plugin.sh + - ./prepare-tests.sh install: echo "Installation done" script: - gradle assemble -S -q diff --git a/OpenKeychain-Test/build.gradle b/OpenKeychain-Test/build.gradle index d795ace3d..a98a79dc1 100644 --- a/OpenKeychain-Test/build.gradle +++ b/OpenKeychain-Test/build.gradle @@ -1,5 +1,20 @@ +buildscript { + repositories { + mavenCentral() + // need this for com.novoda:gradle-android-test-plugin:0.9.9-SNAPSHOT below (0.9.3 in repos doesn't work!) + // run ./install-custom-gradle-test-plugin.sh to pull the thing into the local repository + mavenLocal() + } + + dependencies { + // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information + classpath 'com.novoda:gradle-android-test-plugin:0.9.9-SNAPSHOT' + } +} + apply plugin: 'java' apply plugin: 'android-test' +apply plugin: 'jacoco' dependencies { testCompile 'junit:junit:4.11' @@ -31,6 +46,29 @@ android { projectUnderTest ':OpenKeychain' } +jacoco { + toolVersion = "0.7.0.201403182114" +} + +coverageSourceDirs = [ + '../OpenKeychain/src/main/java', + '../OpenKeychain/src/gen', + '../OpenKeychain/build/source/apt/debug', + '../OpenKeychain/build/source/generated/buildConfig/debug', + '../OpenKeychain/build/source/generated/r/debug' + ] + +jacocoTestReport { + reports { + xml.enabled = true + html.destination "${buildDir}/jacocoHtml" + } + // class R is used, but usage will not be covered, so ignore this class from report + classDirectories = fileTree(dir: '../OpenKeychain/build/intermediates/classes/debug/org/sufficientlysecure/keychain', exclude: 'R*.class') + additionalSourceDirs = files(coverageSourceDirs) + executionData = files('build/jacoco/testDebug.exec') +} + // new workaround to force add custom output dirs for android studio task addTest { def file = file(project.name + ".iml") diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java index 398b2393e..3fa668e6e 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java @@ -21,6 +21,7 @@ import android.content.Context; import org.spongycastle.util.Arrays; import org.sufficientlysecure.keychain.pgp.NullProgressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.OperationResults; @@ -68,6 +69,11 @@ public class KeyringTestingHelper { return saveSuccess; } + public static UncachedKeyRing removePacket(UncachedKeyRing ring, int position) + throws IOException, PgpGeneralException { + return UncachedKeyRing.decodeFromData(removePacket(ring.getEncoded(), position)); + } + public static byte[] removePacket(byte[] ring, int position) throws IOException { Iterator it = parseKeyring(ring); ByteArrayOutputStream out = new ByteArrayOutputStream(ring.length); @@ -76,6 +82,7 @@ public class KeyringTestingHelper { while(it.hasNext()) { // at the right position, skip the packet if(i++ == position) { + it.next(); continue; } // write the old one @@ -89,6 +96,11 @@ public class KeyringTestingHelper { return out.toByteArray(); } + public static UncachedKeyRing injectPacket(UncachedKeyRing ring, byte[] inject, int position) + throws IOException, PgpGeneralException { + return UncachedKeyRing.decodeFromData(injectPacket(ring.getEncoded(), inject, position)); + } + public static byte[] injectPacket(byte[] ring, byte[] inject, int position) throws IOException { Iterator it = parseKeyring(ring); diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java new file mode 100644 index 000000000..6f3cf31b5 --- /dev/null +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java @@ -0,0 +1,213 @@ +package org.sufficientlysecure.keychain.tests; + +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.Assert; +import org.junit.Test; +import org.junit.Before; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowLog; +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.Packet; +import org.spongycastle.bcpg.PacketTags; +import org.spongycastle.bcpg.UserIDPacket; +import org.spongycastle.bcpg.sig.KeyFlags; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; +import org.sufficientlysecure.keychain.pgp.WrappedSignature; +import org.sufficientlysecure.keychain.service.OperationResultParcel; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.support.KeyringTestingHelper; +import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Iterator; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class UncachedKeyringCanonicalizeTest { + + static UncachedKeyRing staticRing; + static int totalPackets; + UncachedKeyRing ring; + ArrayList onlyA = new ArrayList(); + ArrayList onlyB = new ArrayList(); + OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog(); + + @BeforeClass + public static void setUpOnce() throws Exception { + ShadowLog.stream = System.out; + + SaveKeyringParcel parcel = new SaveKeyringParcel(); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null)); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null)); + parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( + Constants.choice.algorithm.rsa, 1024, KeyFlags.ENCRYPT_COMMS, null)); + + parcel.mAddUserIds.add("twi"); + parcel.mAddUserIds.add("pink"); + // passphrase is tested in PgpKeyOperationTest, just use empty here + parcel.mNewPassphrase = ""; + PgpKeyOperation op = new PgpKeyOperation(null); + + OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog(); + staticRing = op.createSecretKeyRing(parcel, log, 0); + + Assert.assertNotNull("initial test key creation must succeed", staticRing); + + // just for later reference + totalPackets = 9; + + // we sleep here for a second, to make sure all new certificates have different timestamps + Thread.sleep(1000); + } + + @Before public void setUp() throws Exception { + // show Log.x messages in system.out + ShadowLog.stream = System.out; + ring = staticRing; + } + + @Test public void testGeneratedRingStructure() throws Exception { + + Iterator it = KeyringTestingHelper.parseKeyring(ring.getEncoded()); + + Assert.assertEquals("packet #1 should be secret key", + PacketTags.SECRET_KEY, it.next().tag); + + Assert.assertEquals("packet #2 should be user id", + PacketTags.USER_ID, it.next().tag); + Assert.assertEquals("packet #3 should be signature", + PacketTags.SIGNATURE, it.next().tag); + + Assert.assertEquals("packet #4 should be user id", + PacketTags.USER_ID, it.next().tag); + Assert.assertEquals("packet #5 should be signature", + PacketTags.SIGNATURE, it.next().tag); + + Assert.assertEquals("packet #6 should be secret subkey", + PacketTags.SECRET_SUBKEY, it.next().tag); + Assert.assertEquals("packet #7 should be signature", + PacketTags.SIGNATURE, it.next().tag); + + Assert.assertEquals("packet #8 should be secret subkey", + PacketTags.SECRET_SUBKEY, it.next().tag); + Assert.assertEquals("packet #9 should be signature", + PacketTags.SIGNATURE, it.next().tag); + + Assert.assertFalse("exactly 9 packets total", it.hasNext()); + + Assert.assertArrayEquals("created keyring should be constant through canonicalization", + ring.getEncoded(), ring.canonicalize(log, 0).getEncoded()); + + } + + @Test public void testBrokenSignature() throws Exception { + + byte[] brokenSig; + { + UncachedPublicKey masterKey = ring.getPublicKey(); + WrappedSignature sig = masterKey.getSignaturesForId("twi").next(); + brokenSig = sig.getEncoded(); + // break the signature + brokenSig[brokenSig.length - 5] += 1; + } + + byte[] reng = ring.getEncoded(); + for(int i = 0; i < totalPackets; i++) { + + byte[] brokenBytes = KeyringTestingHelper.injectPacket(reng, brokenSig, i); + Assert.assertEquals("broken ring must be original + injected size", + reng.length + brokenSig.length, brokenBytes.length); + + try { + UncachedKeyRing brokenRing = UncachedKeyRing.decodeFromData(brokenBytes); + + brokenRing = brokenRing.canonicalize(log, 0); + if (brokenRing == null) { + System.out.println("ok, canonicalization failed."); + continue; + } + + Assert.assertArrayEquals("injected bad signature must be gone after canonicalization", + ring.getEncoded(), brokenRing.getEncoded()); + + } catch (Exception e) { + System.out.println("ok, rejected with: " + e.getMessage()); + } + } + + } + + @Test public void testUidSignature() throws Exception { + + UncachedPublicKey masterKey = ring.getPublicKey(); + final WrappedSignature sig = masterKey.getSignaturesForId("twi").next(); + + byte[] raw = sig.getEncoded(); + // destroy the signature + raw[raw.length - 5] += 1; + final WrappedSignature brokenSig = WrappedSignature.fromBytes(raw); + + { // bad certificates get stripped + UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, brokenSig.getEncoded(), 3); + modified = modified.canonicalize(log, 0); + + Assert.assertTrue("canonicalized keyring with invalid extra sig must be same as original one", + !KeyringTestingHelper.diffKeyrings( + ring.getEncoded(), modified.getEncoded(), onlyA, onlyB)); + } + + // remove user id certificate for one user + final UncachedKeyRing base = KeyringTestingHelper.removePacket(ring, 2); + + { // user id without certificate should be removed + UncachedKeyRing modified = base.canonicalize(log, 0); + Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings( + ring.getEncoded(), modified.getEncoded(), onlyA, onlyB)); + + Assert.assertEquals("two packets should be stripped after canonicalization", 2, onlyA.size()); + Assert.assertEquals("no new packets after canonicalization", 0, onlyB.size()); + + Packet p; + p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket(); + Assert.assertTrue("first stripped packet must be user id", p instanceof UserIDPacket); + Assert.assertEquals("missing user id must be the expected one", + "twi", ((UserIDPacket) p).getID()); + + p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket(); + Assert.assertArrayEquals("second stripped packet must be signature we removed", + sig.getEncoded(), onlyA.get(1).buf); + + } + + { // add error to signature + + UncachedKeyRing modified = KeyringTestingHelper.injectPacket(base, brokenSig.getEncoded(), 3); + modified = modified.canonicalize(log, 0); + + Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings( + ring.getEncoded(), modified.getEncoded(), onlyA, onlyB)); + + Assert.assertEquals("two packets should be missing after canonicalization", 2, onlyA.size()); + Assert.assertEquals("no new packets after canonicalization", 0, onlyB.size()); + + Packet p; + p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket(); + Assert.assertTrue("first stripped packet must be user id", p instanceof UserIDPacket); + Assert.assertEquals("missing user id must be the expected one", + "twi", ((UserIDPacket) p).getID()); + + p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket(); + Assert.assertArrayEquals("second stripped packet must be signature we removed", + sig.getEncoded(), onlyA.get(1).buf); + } + + } + +} diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java deleted file mode 100644 index 66e31c272..000000000 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.sufficientlysecure.keychain.tests; - -import org.junit.BeforeClass; -import org.junit.runner.RunWith; -import org.junit.Assert; -import org.junit.Test; -import org.junit.Before; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.shadows.ShadowLog; -import org.spongycastle.bcpg.BCPGInputStream; -import org.spongycastle.bcpg.Packet; -import org.spongycastle.bcpg.PacketTags; -import org.spongycastle.bcpg.UserIDPacket; -import org.spongycastle.bcpg.sig.KeyFlags; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; -import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; -import org.sufficientlysecure.keychain.pgp.WrappedSignature; -import org.sufficientlysecure.keychain.service.OperationResultParcel; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.support.KeyringTestingHelper; -import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; - -import java.io.ByteArrayInputStream; -import java.util.ArrayList; -import java.util.Iterator; - -@RunWith(RobolectricTestRunner.class) -@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 -public class UncachedKeyringTest { - - static UncachedKeyRing staticRing; - UncachedKeyRing ring; - ArrayList onlyA = new ArrayList(); - ArrayList onlyB = new ArrayList(); - OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog(); - - @BeforeClass - public static void setUpOnce() throws Exception { - ShadowLog.stream = System.out; - - SaveKeyringParcel parcel = new SaveKeyringParcel(); - parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null)); - parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null)); - parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( - Constants.choice.algorithm.rsa, 1024, KeyFlags.ENCRYPT_COMMS, null)); - - parcel.mAddUserIds.add("twi"); - parcel.mAddUserIds.add("pink"); - // passphrase is tested in PgpKeyOperationTest, just use empty here - parcel.mNewPassphrase = ""; - PgpKeyOperation op = new PgpKeyOperation(null); - - OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog(); - staticRing = op.createSecretKeyRing(parcel, log, 0); - - Assert.assertNotNull("initial test key creation must succeed", staticRing); - - // we sleep here for a second, to make sure all new certificates have different timestamps - Thread.sleep(1000); - } - - @Before public void setUp() throws Exception { - // show Log.x messages in system.out - ShadowLog.stream = System.out; - ring = staticRing; - } - - @Test public void testGeneratedRingStructure() throws Exception { - - Iterator it = KeyringTestingHelper.parseKeyring(ring.getEncoded()); - - Assert.assertEquals("packet #1 should be secret key", - PacketTags.SECRET_KEY, it.next().tag); - - Assert.assertEquals("packet #2 should be secret key", - PacketTags.USER_ID, it.next().tag); - Assert.assertEquals("packet #3 should be secret key", - PacketTags.SIGNATURE, it.next().tag); - - Assert.assertEquals("packet #4 should be secret key", - PacketTags.USER_ID, it.next().tag); - Assert.assertEquals("packet #5 should be secret key", - PacketTags.SIGNATURE, it.next().tag); - - Assert.assertEquals("packet #6 should be secret key", - PacketTags.SECRET_SUBKEY, it.next().tag); - Assert.assertEquals("packet #7 should be secret key", - PacketTags.SIGNATURE, it.next().tag); - - Assert.assertEquals("packet #8 should be secret key", - PacketTags.SECRET_SUBKEY, it.next().tag); - Assert.assertEquals("packet #9 should be secret key", - PacketTags.SIGNATURE, it.next().tag); - - Assert.assertFalse("exactly 9 packets total", it.hasNext()); - - Assert.assertArrayEquals("created keyring should be constant through canonicalization", - ring.getEncoded(), ring.canonicalize(log, 0).getEncoded()); - - } - -} diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index c4c8156cd..aa1932c56 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'android' -apply plugin: 'robolectric' dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index bd8a9201e..00f73a5b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -599,6 +599,7 @@ public class PgpKeyOperation { log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1); return null; } catch (PGPException e) { + Log.e(Constants.TAG, "encountered pgp error while modifying key", e); log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1); return null; } catch (SignatureException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index a89cb6bfa..5dc3c598e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -232,7 +232,7 @@ public class UncachedKeyRing { PGPPublicKey modified = masterKey; PGPSignature revocation = null; - for (PGPSignature zert : new IterableIterator(masterKey.getSignatures())) { + for (PGPSignature zert : new IterableIterator(masterKey.getKeySignatures())) { int type = zert.getSignatureType(); // Disregard certifications on user ids, we will deal with those later @@ -241,6 +241,10 @@ public class UncachedKeyRing { || type == PGPSignature.CASUAL_CERTIFICATION || type == PGPSignature.POSITIVE_CERTIFICATION || type == PGPSignature.CERTIFICATION_REVOCATION) { + // These should not be here... + log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent); + modified = PGPPublicKey.removeCertification(modified, zert); + badCerts += 1; continue; } WrappedSignature cert = new WrappedSignature(zert); @@ -432,7 +436,7 @@ public class UncachedKeyRing { // If no valid certificate (if only a revocation) remains, drop it if (selfCert == null && revocation == null) { modified = PGPPublicKey.removeCertification(modified, userId); - log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REVOKE_DUP, + log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE, indent, userId); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index ef5aa8e86..358b1c552 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -1,8 +1,6 @@ package org.sufficientlysecure.keychain.pgp; -import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; @@ -11,7 +9,6 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; -import java.security.SignatureException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index 705d6afaf..89d534df6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -202,6 +202,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_REVOKE_BAD_LOCAL (R.string.msg_kc_revoke_bad_local), MSG_KC_REVOKE_BAD_TIME (R.string.msg_kc_revoke_bad_time), MSG_KC_REVOKE_BAD_TYPE (R.string.msg_kc_revoke_bad_type), + MSG_KC_REVOKE_BAD_TYPE_UID (R.string.msg_kc_revoke_bad_type_uid), MSG_KC_REVOKE_BAD (R.string.msg_kc_revoke_bad), MSG_KC_REVOKE_DUP (R.string.msg_kc_revoke_dup), MSG_KC_SUB (R.string.msg_kc_sub), @@ -233,6 +234,7 @@ public class OperationResultParcel implements Parcelable { MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert), MSG_KC_UID_REVOKE_DUP (R.string.msg_kc_uid_revoke_dup), MSG_KC_UID_REVOKE_OLD (R.string.msg_kc_uid_revoke_old), + MSG_KC_UID_REMOVE (R.string.msg_kc_uid_remove), // keyring consolidation diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index c1c18effe..55ecf3ae0 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -592,6 +592,7 @@ Removing keyring revocation certificate with "local" flag Removing keyring revocation certificate with future timestamp Removing master key certificate of unknown type (%s) + Removing user id certification in bad position Removing bad keyring revocation certificate Removing redundant keyring revocation certificate Processing subkey %s @@ -629,6 +630,7 @@ Removing redundant revocation certificate for user id "%s" Removing outdated revocation certificate for user id "%s" No valid self-certificate found for user id %s, removing from ring + Removing invalid user id %s Merging into public keyring %s diff --git a/README.md b/README.md index 4a2fad2ee..230fd2d6f 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,13 @@ Expand the Tools directory and select "Android SDK Build-tools (Version 19.1)". Expand the Extras directory and install "Android Support Repository" Select everything for the newest SDK Platform (API-Level 19) 4. Export ANDROID_HOME pointing to your Android SDK -5. Use OpenJDK 7 instead of Oracle JDK (this is required for OpenKeychain's tests based on Bouncy Castle) -6. Execute ``./install-custom-gradle-test-plugin.sh`` -7. Execute ``./gradlew build`` -8. You can install the app with ``adb install -r OpenKeychain/build/outputs/apk/OpenKeychain-debug-unaligned.apk`` +5. Execute ``./gradlew build`` +6. You can install the app with ``adb install -r OpenKeychain/build/outputs/apk/OpenKeychain-debug-unaligned.apk`` + +### Run Tests +1. Use OpenJDK instead of Oracle JDK +2. Execute ``./prepare-tests.sh`` +3. Execute ``./gradlew build`` ### Build API Demo with Gradle @@ -50,13 +53,10 @@ Select everything for the newest SDK Platform (API-Level 19) ### Development with Android Studio -I am using the newest [Android Studio](http://developer.android.com/sdk/installing/studio.html) for development. Development with Eclipse is currently not possible because I am using the new [project structure](http://developer.android.com/sdk/installing/studio-tips.html). +We are using the newest [Android Studio](http://developer.android.com/sdk/installing/studio.html) for development. Development with Eclipse is currently not possible because we are using the new [project structure](http://developer.android.com/sdk/installing/studio-tips.html). -1. Clone the project from github -2. From Android Studio: File -> Import Project -> ... - * Select the cloned top folder if you want to develop on the main project - * Select the "OpenKeychain-API" folder if you want to develop on the API example -3. Import project from external model -> choose Gradle +1. Clone the project from Github +2. From Android Studio: File -> Import Project -> Select the cloned top folder ## OpenKeychain's API diff --git a/build.gradle b/build.gradle index 698b4cd37..05e90eb17 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,11 @@ buildscript { repositories { mavenCentral() - // need this for com.novoda:gradle-android-test-plugin:0.9.9-SNAPSHOT below (0.9.3 in repos doesn't work!) - // run ./install-custom-gradle-test-plugin.sh to pull the thing into the local repository - mavenLocal() } dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information classpath 'com.android.tools.build:gradle:0.12.0' - classpath 'org.robolectric:robolectric-gradle-plugin:0.11.0' - classpath 'com.novoda:gradle-android-test-plugin:0.9.9-SNAPSHOT' } } diff --git a/install-custom-gradle-test-plugin.sh b/install-custom-gradle-test-plugin.sh deleted file mode 100755 index 85c13d959..000000000 --- a/install-custom-gradle-test-plugin.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -mkdir temp -cd temp - - git clone https://github.com/nenick/gradle-android-test-plugin.git - cd gradle-android-test-plugin - - echo "rootProject.name = 'gradle-android-test-plugin-parent'" > settings.gradle - echo "include ':gradle-android-test-plugin'" >> settings.gradle - - ./gradlew :gradle-android-test-plugin:install - - cd .. -cd .. \ No newline at end of file diff --git a/prepare-tests.sh b/prepare-tests.sh new file mode 100755 index 000000000..027c76f84 --- /dev/null +++ b/prepare-tests.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# This script installs a plugin which is necessary to run OpenKeychain's tests +# into the local maven repository, then puts a line to include the -Test +# subproject into settings.gradle + +echo "checking jdk runtime.." +if ! java -version 2>&1 | grep OpenJDK; then + echo "tests will only run on openjdk, see readme for details!" >&2 + return +fi + +tmpdir="$(mktemp -d)" +( + cd "$tmpdir"; + git clone https://github.com/nenick/gradle-android-test-plugin.git + cd gradle-android-test-plugin + echo "rootProject.name = 'gradle-android-test-plugin-parent'" > settings.gradle + echo "include ':gradle-android-test-plugin'" >> settings.gradle + ./gradlew :gradle-android-test-plugin:install +) +rm -rf "$tmpdir" + +echo -n "ok, adding tests to include list.. " +if grep OpenKeychain-Test settings.gradle >/dev/null ; then + echo " already in." +else + echo "include ':OpenKeychain-Test'" >> settings.gradle + echo "ok" +fi diff --git a/settings.gradle b/settings.gradle index d0b2b9954..e134c52a8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,4 @@ include ':OpenKeychain' -include ':OpenKeychain-Test' include ':extern:openpgp-api-lib' include ':extern:openkeychain-api-lib' include ':extern:html-textview' @@ -14,4 +13,4 @@ include ':extern:AppMsg:library' include ':extern:SuperToasts:supertoasts' include ':extern:minidns' include ':extern:KeybaseLib:Lib' -include ':extern:openpgp-card-nfc-lib:library' \ No newline at end of file +include ':extern:openpgp-card-nfc-lib:library'