mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-30 12:32:17 -05:00
fix encrypt/decrypt classes
This commit is contained in:
parent
27cc92cbef
commit
a3e7406b3d
@ -18,20 +18,24 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.*;
|
import org.robolectric.*;
|
||||||
import org.robolectric.shadows.ShadowLog;
|
import org.robolectric.shadows.ShadowLog;
|
||||||
|
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.spongycastle.openpgp.PGPEncryptedData;
|
import org.spongycastle.openpgp.PGPEncryptedData;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify.NoSecretKeyException;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify.PassphraseCache;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt.Builder;
|
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt.Builder;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||||
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||||
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
|
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
|
||||||
|
import org.sufficientlysecure.keychain.service.results.EditKeyResult;
|
||||||
import org.sufficientlysecure.keychain.service.results.SignEncryptResult;
|
import org.sufficientlysecure.keychain.service.results.SignEncryptResult;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
|
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@ -42,58 +46,229 @@ import java.security.Security;
|
|||||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||||
public class PgpEncryptDecryptTest {
|
public class PgpEncryptDecryptTest {
|
||||||
|
|
||||||
String mPassphrase = TestingUtils.genPassphrase(true);
|
static String mPassphrase = TestingUtils.genPassphrase(true);
|
||||||
|
|
||||||
|
static UncachedKeyRing mStaticRing1, mStaticRing2;
|
||||||
|
static String mKeyPhrase1 = TestingUtils.genPassphrase(true);
|
||||||
|
static String mKeyPhrase2 = TestingUtils.genPassphrase(true);
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setUpOnce() throws Exception {
|
public static void setUpOnce() throws Exception {
|
||||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
||||||
ShadowLog.stream = System.out;
|
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("bloom");
|
||||||
|
parcel.mNewPassphrase = 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("belle");
|
||||||
|
parcel.mNewPassphrase = mKeyPhrase2;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
|
||||||
|
providerHelper.saveSecretKeyRing(mStaticRing1, new ProgressScaler());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSymmetricEncryptDecrypt() {
|
public void testSymmetricEncryptDecrypt() {
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true);
|
||||||
|
byte[] ciphertext;
|
||||||
|
|
||||||
{
|
{ // encrypt data with a given passphrase
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream("dies ist ein plaintext ☭".getBytes());
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes());
|
||||||
|
|
||||||
InputData data = new InputData(in, in.available());
|
InputData data = new InputData(in, in.available());
|
||||||
Builder b = new PgpSignEncrypt.Builder(new ProviderHelper(Robolectric.application), data, out);
|
Builder b = new PgpSignEncrypt.Builder(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
new DummyPassphraseCache(mPassphrase, 0L),
|
||||||
|
data, out);
|
||||||
|
|
||||||
b.setSymmetricPassphrase(mPassphrase);
|
b.setSymmetricPassphrase(mPassphrase);
|
||||||
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||||
SignEncryptResult result = b.build().execute();
|
SignEncryptResult result = b.build().execute();
|
||||||
Assert.assertTrue("encryption must succeed", result.success());
|
Assert.assertTrue("encryption must succeed", result.success());
|
||||||
|
|
||||||
|
ciphertext = out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{ // decryption with same passphrase should yield the same result
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
|
||||||
InputData data = new InputData(in, in.available());
|
|
||||||
|
|
||||||
out.reset();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
||||||
new ProviderHelper(Robolectric.application),
|
new ProviderHelper(Robolectric.application),
|
||||||
new DummyPassphraseCache(mPassphrase), data, out);
|
new DummyPassphraseCache(mPassphrase, 0L), data, out);
|
||||||
b.setPassphrase(mPassphrase);
|
b.setPassphrase(mPassphrase);
|
||||||
DecryptVerifyResult result = b.build().execute();
|
DecryptVerifyResult result = b.build().execute();
|
||||||
Assert.assertTrue("decryption must succeed", result.success());
|
Assert.assertTrue("decryption must succeed", result.success());
|
||||||
|
Assert.assertArrayEquals("decrypted ciphertext should equal plaintext",
|
||||||
|
out.toByteArray(), plaintext.getBytes());
|
||||||
|
Assert.assertNull("signature should be an error", result.getSignatureResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // decryption with a bad passphrase should fail
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
new DummyPassphraseCache(mPassphrase, 0L), data, out);
|
||||||
|
b.setPassphrase(mPassphrase + "x");
|
||||||
|
DecryptVerifyResult result = b.build().execute();
|
||||||
|
Assert.assertFalse("decryption must succeed", result.success());
|
||||||
|
Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
|
||||||
|
Assert.assertNull("signature should be an error", result.getSignatureResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // decryption with an unset passphrase should fail
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
new DummyPassphraseCache(mPassphrase, 0L), data, out);
|
||||||
|
DecryptVerifyResult result = b.build().execute();
|
||||||
|
Assert.assertFalse("decryption must succeed", result.success());
|
||||||
|
Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
|
||||||
|
Assert.assertNull("signature should be an error", result.getSignatureResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DummyPassphraseCache implements PassphraseCache {
|
@Test
|
||||||
|
public void testAsymmetricEncryptDecrypt() {
|
||||||
|
|
||||||
|
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(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
new DummyPassphraseCache(mPassphrase, 0L),
|
||||||
|
data, out);
|
||||||
|
|
||||||
|
b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
|
||||||
|
b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
|
||||||
|
SignEncryptResult result = b.build().execute();
|
||||||
|
Assert.assertTrue("encryption must succeed", result.success());
|
||||||
|
|
||||||
|
ciphertext = out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // decryption with provided passphrase should yield the same result
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
new DummyPassphraseCache(null, null), data, out);
|
||||||
|
b.setPassphrase(mKeyPhrase1);
|
||||||
|
DecryptVerifyResult result = b.build().execute();
|
||||||
|
Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
|
||||||
|
Assert.assertArrayEquals("decrypted ciphertext with provided passphrase should equal plaintext",
|
||||||
|
out.toByteArray(), plaintext.getBytes());
|
||||||
|
Assert.assertNull("signature be empty", result.getSignatureResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // decryption with passphrase cached should succeed
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
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
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
|
||||||
|
InputData data = new InputData(in, in.available());
|
||||||
|
|
||||||
|
PgpDecryptVerify.Builder b = new PgpDecryptVerify.Builder(
|
||||||
|
new ProviderHelper(Robolectric.application),
|
||||||
|
new DummyPassphraseCache(null, null), data, out);
|
||||||
|
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 {
|
||||||
|
|
||||||
String mPassphrase;
|
String mPassphrase;
|
||||||
public DummyPassphraseCache(String passphrase) {
|
Long mExpectedId;
|
||||||
|
public DummyPassphraseCache(String passphrase, Long expectedId) {
|
||||||
mPassphrase = passphrase;
|
mPassphrase = passphrase;
|
||||||
|
mExpectedId = expectedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCachedPassphrase(long masterKeyId) throws NoSecretKeyException {
|
public String getCachedPassphrase(long masterKeyId) throws NoSecretKeyException {
|
||||||
Assert.assertEquals("requested passphrase must be for symmetric id", 0L, masterKeyId);
|
if (mExpectedId != null){
|
||||||
|
Assert.assertEquals("requested passphrase must be for expected id",
|
||||||
|
(long) mExpectedId, masterKeyId);
|
||||||
|
}
|
||||||
return mPassphrase;
|
return mPassphrase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,9 +643,6 @@ public class PgpDecryptVerify {
|
|||||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no integrity check
|
|
||||||
Log.d(Constants.TAG, "Encrypted data was not integrity protected! MDC packet is missing!");
|
|
||||||
|
|
||||||
// If no valid signature is present:
|
// If no valid signature is present:
|
||||||
// Handle missing integrity protection like failed integrity protection!
|
// Handle missing integrity protection like failed integrity protection!
|
||||||
// The MDC packet can be stripped by an attacker!
|
// The MDC packet can be stripped by an attacker!
|
||||||
|
Loading…
Reference in New Issue
Block a user