diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java index 016651c3b..5e7bc6fb6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -22,7 +22,10 @@ package org.sufficientlysecure.keychain.pgp; import java.util.regex.Matcher; import java.util.regex.Pattern; +import android.support.annotation.CheckResult; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; @@ -104,18 +107,52 @@ public class PgpHelper { } } - public static String getPgpKeyContent(@NonNull CharSequence input) { + public static String getPgpPublicKeyContent(@NonNull CharSequence input) { Log.dEscaped(Constants.TAG, "input: " + input); Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(input); - if (matcher.matches()) { - String text = matcher.group(1); - text = fixPgpMessage(text); - - Log.dEscaped(Constants.TAG, "input fixed: " + text); - return text; + if (!matcher.matches()) { + return null; } - return null; + + String text = matcher.group(1); + text = fixPgpMessage(text); + text = reformatPgpPublicKeyBlock(text); + + // Log.dEscaped(Constants.TAG, "input fixed: " + text); + return text; + } + + @Nullable + @CheckResult + @VisibleForTesting + static String reformatPgpPublicKeyBlock(@NonNull String text) { + StringBuilder reformattedKeyBlocks = new StringBuilder(); + + while (!text.isEmpty()) { + int indexOfBlock = text.indexOf("-----BEGIN PGP PUBLIC KEY BLOCK-----"); + int indexOfPubkeyMaterial = text.indexOf("mQ", indexOfBlock); + int indexOfBlockEnd = text.indexOf("-----END PGP PUBLIC KEY BLOCK-----"); + if (indexOfBlock < 0 || indexOfPubkeyMaterial < 0 || indexOfBlockEnd < 0) { + break; + } + + String keyMaterial = text.substring(indexOfPubkeyMaterial, indexOfBlockEnd); + keyMaterial = keyMaterial.replaceAll("\\s+", "\n"); + + reformattedKeyBlocks.append("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"); + reformattedKeyBlocks.append('\n'); + reformattedKeyBlocks.append(keyMaterial); + reformattedKeyBlocks.append("-----END PGP PUBLIC KEY BLOCK-----\n"); + + text = text.substring(indexOfBlockEnd +34).trim(); + } + + if (reformattedKeyBlocks.length() == 0) { + return null; + } + + return reformattedKeyBlocks.toString(); } } diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpHelperTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpHelperTest.java new file mode 100644 index 000000000..340dd7ce9 --- /dev/null +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/pgp/PgpHelperTest.java @@ -0,0 +1,76 @@ +package org.sufficientlysecure.keychain.pgp; + + +import java.io.ByteArrayInputStream; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.sufficientlysecure.keychain.KeychainTestRunner; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; + + +@SuppressWarnings("WeakerAccess") +@RunWith(KeychainTestRunner.class) +public class PgpHelperTest { + + static final String INPUT_KEY_BLOCK = "-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2 " + + "mQENBFnA7Y0BCAC+pdQ1mV9QguWvAyMsKiKkzeP5VxbIDUyQ8OBDFKIrZKZGTzjZ " + + "xvZs22j5pXWYlyDi4HU4+nuQmAMo6jxJMKQQkW7Y5AmRYijboLN+lZ8L28bYJC4o " + + "PcAnS3xlib2lE7aNK2BRUbctkhahhb2hohAxiXTUdGmfr9sHgZ2+B9sPTfuhrvtI " + + "9cFHZ5/0Wp4pLhLB53gduYeLuw4vVfUd7t0C4IhqB+t5HE+F3lolgya7hXxdH0ji " + + "oFNldCWT2qNdmmehIyY0WaoIrnUm2gVA4LMZ2fQTfsInpec5YT85OZaPEeBs3Opg " + + "3aGxV4mhXOxHkfREJRuYXcqL1s/UERGNprp9ABEBAAG0E01yLiBNYWxmb3JtZWQg " + + "QXNjaWmJAVQEEwEIAD4WIQTLHyFqT4uzqqEXR5Zz6vdoZVkMnwUCWcDtjQIbAwUJ " + + "A8JnAAULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBz6vdoZVkMnwpcB/985UbR " + + "qxG5pzaLGePdl+Cm6JBra2mC3AfMbJobjHR8YVufDw1tA6qyCMW0emHFi8t/rG8j " + + "tzPIadcl30rOzaGUTF85G5SqbwgAFHddZ01af36F6em0d5tY3+FCclQR7ynFPQlA " + + "+KB9k/M5X91ty6Q3/EAaXst5Uwh1WnKNC1js9RAcYL1s1MXKxg2iMmtE0DvwMAWq " + + "XRR+ADjzqkVdpdrzanTY7b72nuiGfe/H75b7/StIAfyxSZc5BU5535J0wF7Boz4p " + + "A6zRFXTphabmAE9FIKhgj5X7fbU64Hsrc5OkvWt4dF/6VRE4oXgUYwLKaDEH7A0k " + + "32GBGOkmnQGLKuqhuQENBFnA7Y0BCADKxQ1APSraxNKMpJv9vEVcXK3Sr91SYpGY " + + "s/ugYNio2xvIt9Qe2AjYGNSE9+wS6qLRxbbzucIRxl9jbn2QTNbJr7epLVdj3wtL " + + "JlkKsv13iao77Hg9WMvKh+NHpGoIFn4g5LOeYYG0QkZOvdu6b4Eg0RAryTLBh9jB " + + "eMLELkZTFDuQAgOSrVY0XgoURNcaDRtVarnVNBIO1N7/7TNXtmL22wR0wpqh4mKv " + + "vIvhE5itlIrthJHWzTcLDv5BHfyX23wqEpQFEffs1D72k9Ruh60OGgU0XAiVF654 " + + "WjgZCUoscPCLWcDGDOlcN7FpBxMDi1Ao3+7sLOMi9zES0InJ9q8LABEBAAGJATwE " + + "GAEIACYWIQTLHyFqT4uzqqEXR5Zz6vdoZVkMnwUCWcDtjQIbDAUJA8JnAAAKCRBz " + + "6vdoZVkMn3/+B/9B0vrDITV2FpdT+99WVsXJsoiXOsqLfv3WkC0l0RBAcCaUeXxp " + + "EqzyXQ0dVi6RoXu67dSnah6bdgfVnH14hJE8jc30GJ4QEpPD9kAKyodej15ledR5 " + + "sbdjeEfsavn9tvACJ0svfu8YVJjUjJLOj5axXy8wUBm5UvCdZuSL4EjPq7hXdq+j " + + "O/eTJGOfMl6hC4rRxRUbM+piZzbYcQ0lO3R2yPlEwzlO+asM9820V9bkviJUrXiY " + + "c5EX44mwFdhpXuHbRS18DJjCVcMhEsPG6rQ0Qy/6/dafow5HExRBmZl6ZkfjR2Lb " + + "alOZH0SNi47bvn6QKqKgiqT4f9mImyEDtSj/ =2V66 -----END PGP PUBLIC KEY BLOCK-----"; + + @Test + public void reformatPgpPublicKeyBlock() throws Exception { + String reformattedKey = PgpHelper.reformatPgpPublicKeyBlock(INPUT_KEY_BLOCK); + + assertNotNull(reformattedKey); + UncachedKeyRing.decodeFromData(reformattedKey.getBytes()); + } + + @Test + public void reformatPgpPublicKeyBlock_consecutiveKeys() throws Exception { + String reformattedKey = PgpHelper.reformatPgpPublicKeyBlock(INPUT_KEY_BLOCK + INPUT_KEY_BLOCK); + + assertNotNull(reformattedKey); + IteratorWithIOThrow uncachedKeyRingIteratorWithIOThrow = + UncachedKeyRing.fromStream(new ByteArrayInputStream(reformattedKey.getBytes())); + assertNotNull(uncachedKeyRingIteratorWithIOThrow.next()); + assertNotNull(uncachedKeyRingIteratorWithIOThrow.next()); + assertFalse(uncachedKeyRingIteratorWithIOThrow.hasNext()); + } + + @Test + public void reformatPgpPublicKeyBlock_shouldBeIdempotent() throws Exception { + String reformattedKey1 = PgpHelper.reformatPgpPublicKeyBlock(INPUT_KEY_BLOCK); + String reformattedKey2 = PgpHelper.reformatPgpPublicKeyBlock(reformattedKey1); + + assertEquals(reformattedKey1, reformattedKey2); + } + +} \ No newline at end of file