move classes around a bit
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) Art O Cathain
|
||||
*
|
||||
* 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 tests;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.*;
|
||||
import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||
import org.sufficientlysecure.keychain.support.PgpVerifyTestingHelper;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class PgpDecryptVerifyTest {
|
||||
|
||||
@Test
|
||||
public void testVerifySuccess() throws Exception {
|
||||
|
||||
String testFileName = "/sample.txt";
|
||||
int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED;
|
||||
|
||||
int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName);
|
||||
|
||||
Assert.assertEquals(expectedSignatureResult, status);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyFailure() throws Exception {
|
||||
|
||||
String testFileName = "/sample-altered.txt";
|
||||
int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_ERROR;
|
||||
|
||||
int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName);
|
||||
|
||||
Assert.assertEquals(expectedSignatureResult, status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,764 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.*;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
import org.spongycastle.bcpg.BCPGInputStream;
|
||||
import org.spongycastle.bcpg.Packet;
|
||||
import org.spongycastle.bcpg.PacketTags;
|
||||
import org.spongycastle.bcpg.SecretSubkeyPacket;
|
||||
import org.spongycastle.bcpg.SignaturePacket;
|
||||
import org.spongycastle.bcpg.UserIDPacket;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Constants.choice.algorithm;
|
||||
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
|
||||
import org.sufficientlysecure.keychain.support.KeyringBuilder;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
|
||||
import org.sufficientlysecure.keychain.support.TestDataUtil;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class PgpKeyOperationTest {
|
||||
|
||||
static UncachedKeyRing staticRing;
|
||||
static String passphrase;
|
||||
|
||||
UncachedKeyRing ring;
|
||||
PgpKeyOperation op;
|
||||
SaveKeyringParcel parcel;
|
||||
ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
|
||||
ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
|
||||
|
||||
@BeforeClass public static void setUpOnce() throws Exception {
|
||||
ShadowLog.stream = System.out;
|
||||
|
||||
{
|
||||
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
|
||||
Random r = new Random();
|
||||
StringBuilder passbuilder = new StringBuilder();
|
||||
// 20% chance for an empty passphrase
|
||||
for(int i = 0, j = r.nextInt(10) > 2 ? r.nextInt(20) : 0; i < j; i++) {
|
||||
passbuilder.append(chars.charAt(r.nextInt(chars.length())));
|
||||
}
|
||||
passphrase = passbuilder.toString();
|
||||
System.out.println("Passphrase is '" + passphrase + "'");
|
||||
}
|
||||
|
||||
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");
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
staticRing = op.createSecretKeyRing(parcel).getRing();
|
||||
|
||||
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;
|
||||
|
||||
// setting up some parameters just to reduce code duplication
|
||||
op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
|
||||
|
||||
// set this up, gonna need it more than once
|
||||
parcel = new SaveKeyringParcel();
|
||||
parcel.mMasterKeyId = ring.getMasterKeyId();
|
||||
parcel.mFingerprint = ring.getFingerprint();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSecretKeyRingTests() {
|
||||
|
||||
{
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, new Random().nextInt(256)+255, KeyFlags.CERTIFY_OTHER, null));
|
||||
parcel.mAddUserIds.add("shy");
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
|
||||
UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
|
||||
|
||||
Assert.assertNull("creating ring with < 512 bytes keysize should fail", ring);
|
||||
}
|
||||
|
||||
{
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.elgamal, 1024, KeyFlags.CERTIFY_OTHER, null));
|
||||
parcel.mAddUserIds.add("shy");
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
|
||||
UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
|
||||
|
||||
Assert.assertNull("creating ring with ElGamal master key should fail", ring);
|
||||
}
|
||||
|
||||
{
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
12345, 1024, KeyFlags.CERTIFY_OTHER, null));
|
||||
parcel.mAddUserIds.add("shy");
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
|
||||
UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
|
||||
Assert.assertNull("creating ring with bad algorithm choice should fail", ring);
|
||||
}
|
||||
|
||||
{
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
|
||||
parcel.mAddUserIds.add("shy");
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
|
||||
UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
|
||||
Assert.assertNull("creating ring with non-certifying master key should fail", ring);
|
||||
}
|
||||
|
||||
{
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
|
||||
UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
|
||||
Assert.assertNull("creating ring without user ids should fail", ring);
|
||||
}
|
||||
|
||||
{
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add("shy");
|
||||
parcel.mNewPassphrase = passphrase;
|
||||
|
||||
UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
|
||||
Assert.assertNull("creating ring without subkeys should fail", ring);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
// this is a special case since the flags are in user id certificates rather than
|
||||
// subkey binding certificates
|
||||
public void testMasterFlags() throws Exception {
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, null));
|
||||
parcel.mAddUserIds.add("luna");
|
||||
ring = op.createSecretKeyRing(parcel).getRing();
|
||||
|
||||
Assert.assertEquals("the keyring should contain only the master key",
|
||||
1, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
|
||||
Assert.assertEquals("first (master) key must have both flags",
|
||||
KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, ring.getPublicKey().getKeyUsage());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatedKey() throws Exception {
|
||||
|
||||
// an empty modification should change nothing. this also ensures the keyring
|
||||
// is constant through canonicalization.
|
||||
// applyModificationWithChecks(parcel, ring, onlyA, onlyB);
|
||||
|
||||
Assert.assertNotNull("key creation failed", ring);
|
||||
|
||||
Assert.assertNull("primary user id must be empty",
|
||||
ring.getPublicKey().getPrimaryUserId());
|
||||
|
||||
Assert.assertEquals("number of user ids must be two",
|
||||
2, ring.getPublicKey().getUnorderedUserIds().size());
|
||||
|
||||
Assert.assertEquals("number of subkeys must be three",
|
||||
3, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
|
||||
|
||||
Assert.assertTrue("key ring should have been created in the last 120 seconds",
|
||||
ring.getPublicKey().getCreationTime().after(new Date(new Date().getTime()-1000*120)));
|
||||
|
||||
Assert.assertNull("key ring should not expire",
|
||||
ring.getPublicKey().getExpiryTime());
|
||||
|
||||
Iterator<UncachedPublicKey> it = ring.getPublicKeys();
|
||||
|
||||
Assert.assertEquals("first (master) key can certify",
|
||||
KeyFlags.CERTIFY_OTHER, it.next().getKeyUsage());
|
||||
|
||||
UncachedPublicKey signingKey = it.next();
|
||||
Assert.assertEquals("second key can sign",
|
||||
KeyFlags.SIGN_DATA, signingKey.getKeyUsage());
|
||||
ArrayList<WrappedSignature> sigs = signingKey.getSignatures().next().getEmbeddedSignatures();
|
||||
Assert.assertEquals("signing key signature should have one embedded signature",
|
||||
1, sigs.size());
|
||||
Assert.assertEquals("embedded signature should be of primary key binding type",
|
||||
PGPSignature.PRIMARYKEY_BINDING, sigs.get(0).getSignatureType());
|
||||
Assert.assertEquals("primary key binding signature issuer should be signing subkey",
|
||||
signingKey.getKeyId(), sigs.get(0).getKeyId());
|
||||
|
||||
Assert.assertEquals("third key can encrypt",
|
||||
KeyFlags.ENCRYPT_COMMS, it.next().getKeyUsage());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadKeyModification() throws Exception {
|
||||
|
||||
{
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
// off by one
|
||||
parcel.mMasterKeyId = ring.getMasterKeyId() -1;
|
||||
parcel.mFingerprint = ring.getFingerprint();
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("keyring modification with bad master key id should fail", modified);
|
||||
}
|
||||
|
||||
{
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
// off by one
|
||||
parcel.mMasterKeyId = null;
|
||||
parcel.mFingerprint = ring.getFingerprint();
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("keyring modification with null master key id should fail", modified);
|
||||
}
|
||||
|
||||
{
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
parcel.mMasterKeyId = ring.getMasterKeyId();
|
||||
parcel.mFingerprint = ring.getFingerprint();
|
||||
// some byte, off by one
|
||||
parcel.mFingerprint[5] += 1;
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("keyring modification with bad fingerprint should fail", modified);
|
||||
}
|
||||
|
||||
{
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
parcel.mMasterKeyId = ring.getMasterKeyId();
|
||||
parcel.mFingerprint = null;
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("keyring modification with null fingerprint should fail", modified);
|
||||
}
|
||||
|
||||
{
|
||||
String badphrase = "";
|
||||
if (badphrase.equals(passphrase)) {
|
||||
badphrase = "a";
|
||||
}
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, badphrase).getRing();
|
||||
|
||||
Assert.assertNull("keyring modification with bad passphrase should fail", modified);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubkeyAdd() throws Exception {
|
||||
|
||||
long expiry = new Date().getTime() / 1000 + 159;
|
||||
int flags = KeyFlags.SIGN_DATA;
|
||||
int bits = 1024 + new Random().nextInt(8);
|
||||
parcel.mAddSubKeys.add(new SubkeyAdd(algorithm.rsa, bits, flags, expiry));
|
||||
|
||||
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("no extra packets in original", 0, onlyA.size());
|
||||
Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size());
|
||||
|
||||
Packet p;
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first new packet must be secret subkey", p instanceof SecretSubkeyPacket);
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(1).buf)).readPacket();
|
||||
Assert.assertTrue("second new packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("signature type must be subkey binding certificate",
|
||||
PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
// get new key from ring. it should be the last one (add a check to make sure?)
|
||||
UncachedPublicKey newKey = null;
|
||||
{
|
||||
Iterator<UncachedPublicKey> it = modified.getPublicKeys();
|
||||
while (it.hasNext()) {
|
||||
newKey = it.next();
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertNotNull("new key is not null", newKey);
|
||||
Assert.assertNotNull("added key must have an expiry date",
|
||||
newKey.getExpiryTime());
|
||||
Assert.assertEquals("added key must have expected expiry date",
|
||||
expiry, newKey.getExpiryTime().getTime()/1000);
|
||||
Assert.assertEquals("added key must have expected flags",
|
||||
flags, newKey.getKeyUsage());
|
||||
Assert.assertEquals("added key must have expected bitsize",
|
||||
bits, newKey.getBitStrength());
|
||||
|
||||
{ // bad keysize should fail
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SubkeyAdd(
|
||||
algorithm.rsa, new Random().nextInt(512), KeyFlags.SIGN_DATA, null));
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("creating a subkey with keysize < 512 should fail", modified);
|
||||
}
|
||||
|
||||
{ // a past expiry should fail
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.SIGN_DATA,
|
||||
new Date().getTime()/1000-10));
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("creating subkey with past expiry date should fail", modified);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubkeyModify() throws Exception {
|
||||
|
||||
long expiry = new Date().getTime()/1000 + 1024;
|
||||
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
|
||||
|
||||
UncachedKeyRing modified = ring;
|
||||
{
|
||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry));
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("one extra packet in original", 1, onlyA.size());
|
||||
Assert.assertEquals("one extra packet in modified", 1, onlyB.size());
|
||||
|
||||
Assert.assertEquals("old packet must be signature",
|
||||
PacketTags.SIGNATURE, onlyA.get(0).tag);
|
||||
|
||||
Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first new packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("signature type must be subkey binding certificate",
|
||||
PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
Assert.assertNotNull("modified key must have an expiry date",
|
||||
modified.getPublicKey(keyId).getExpiryTime());
|
||||
Assert.assertEquals("modified key must have expected expiry date",
|
||||
expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
|
||||
Assert.assertEquals("modified key must have same flags as before",
|
||||
ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());
|
||||
}
|
||||
|
||||
{
|
||||
int flags = KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS;
|
||||
parcel.reset();
|
||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, flags, null));
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("old packet must be signature",
|
||||
PacketTags.SIGNATURE, onlyA.get(0).tag);
|
||||
|
||||
Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first new packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("signature type must be subkey binding certificate",
|
||||
PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
Assert.assertEquals("modified key must have expected flags",
|
||||
flags, modified.getPublicKey(keyId).getKeyUsage());
|
||||
Assert.assertNotNull("key must retain its expiry",
|
||||
modified.getPublicKey(keyId).getExpiryTime());
|
||||
Assert.assertEquals("key expiry must be unchanged",
|
||||
expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
|
||||
}
|
||||
|
||||
{ // a past expiry should fail
|
||||
parcel.reset();
|
||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10));
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("setting subkey expiry to a past date should fail", modified);
|
||||
}
|
||||
|
||||
{ // modifying nonexistent keyring should fail
|
||||
parcel.reset();
|
||||
parcel.mChangeSubKeys.add(new SubkeyChange(123, null, null));
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("modifying non-existent subkey should fail", modified);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubkeyRevoke() throws Exception {
|
||||
|
||||
long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
|
||||
int flags = ring.getPublicKey(keyId).getKeyUsage();
|
||||
|
||||
UncachedKeyRing modified;
|
||||
|
||||
{
|
||||
|
||||
parcel.reset();
|
||||
parcel.mRevokeSubKeys.add(123L);
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("revoking a nonexistent subkey should fail", otherModified);
|
||||
|
||||
}
|
||||
|
||||
{ // revoked second subkey
|
||||
|
||||
parcel.reset();
|
||||
parcel.mRevokeSubKeys.add(keyId);
|
||||
|
||||
modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("no extra packets in original", 0, onlyA.size());
|
||||
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
|
||||
|
||||
Packet p;
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first new packet must be secret subkey", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("signature type must be subkey binding certificate",
|
||||
PGPSignature.SUBKEY_REVOCATION, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
Assert.assertTrue("subkey must actually be revoked",
|
||||
modified.getPublicKey(keyId).isRevoked());
|
||||
}
|
||||
|
||||
{ // re-add second subkey
|
||||
|
||||
parcel.reset();
|
||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, null));
|
||||
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
|
||||
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
|
||||
|
||||
Packet p;
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first outdated packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("first outdated signature type must be subkey binding certification",
|
||||
PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("first outdated signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
|
||||
Assert.assertTrue("second outdated packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("second outdated signature type must be subkey revocation",
|
||||
PGPSignature.SUBKEY_REVOCATION, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("second outdated signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("new packet must be signature ", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("new signature type must be subkey binding certification",
|
||||
PGPSignature.SUBKEY_BINDING, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
Assert.assertFalse("subkey must no longer be revoked",
|
||||
modified.getPublicKey(keyId).isRevoked());
|
||||
Assert.assertEquals("subkey must have the same usage flags as before",
|
||||
flags, modified.getPublicKey(keyId).getKeyUsage());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserIdRevoke() throws Exception {
|
||||
|
||||
UncachedKeyRing modified;
|
||||
String uid = ring.getPublicKey().getUnorderedUserIds().get(1);
|
||||
|
||||
{ // revoke second user id
|
||||
|
||||
parcel.mRevokeUserIds.add(uid);
|
||||
|
||||
modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("no extra packets in original", 0, onlyA.size());
|
||||
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
|
||||
|
||||
Packet p;
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first new packet must be secret subkey", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("signature type must be subkey binding certificate",
|
||||
PGPSignature.CERTIFICATION_REVOCATION, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
}
|
||||
|
||||
{ // re-add second user id
|
||||
|
||||
parcel.reset();
|
||||
parcel.mChangePrimaryUserId = uid;
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
|
||||
UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("setting primary user id to a revoked user id should fail", otherModified);
|
||||
|
||||
}
|
||||
|
||||
{ // re-add second user id
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add(uid);
|
||||
|
||||
applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("exactly two outdated packets in original", 2, onlyA.size());
|
||||
Assert.assertEquals("exactly one extra packet in modified", 1, onlyB.size());
|
||||
|
||||
Packet p;
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first outdated packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("first outdated signature type must be positive certification",
|
||||
PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("first outdated signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
|
||||
Assert.assertTrue("second outdated packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("second outdated signature type must be certificate revocation",
|
||||
PGPSignature.CERTIFICATION_REVOCATION, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("second outdated signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("new packet must be signature ", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("new signature type must be positive certification",
|
||||
PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
|
||||
Assert.assertEquals("signature must have been created by master key",
|
||||
ring.getMasterKeyId(), ((SignaturePacket) p).getKeyID());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserIdAdd() throws Exception {
|
||||
|
||||
{
|
||||
parcel.mAddUserIds.add("");
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
Assert.assertNull("adding an empty user id should fail", modified);
|
||||
}
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add("rainbow");
|
||||
|
||||
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
|
||||
|
||||
Assert.assertTrue("keyring must contain added user id",
|
||||
modified.getPublicKey().getUnorderedUserIds().contains("rainbow"));
|
||||
|
||||
Assert.assertEquals("no extra packets in original", 0, onlyA.size());
|
||||
Assert.assertEquals("exactly two extra packets in modified", 2, onlyB.size());
|
||||
|
||||
Assert.assertTrue("keyring must contain added user id",
|
||||
modified.getPublicKey().getUnorderedUserIds().contains("rainbow"));
|
||||
|
||||
Packet p;
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(0).buf)).readPacket();
|
||||
Assert.assertTrue("first new packet must be user id", p instanceof UserIDPacket);
|
||||
Assert.assertEquals("user id packet must match added user id",
|
||||
"rainbow", ((UserIDPacket) p).getID());
|
||||
|
||||
p = new BCPGInputStream(new ByteArrayInputStream(onlyB.get(1).buf)).readPacket();
|
||||
Assert.assertTrue("second new packet must be signature", p instanceof SignaturePacket);
|
||||
Assert.assertEquals("signature type must be positive certification",
|
||||
PGPSignature.POSITIVE_CERTIFICATION, ((SignaturePacket) p).getSignatureType());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserIdPrimary() throws Exception {
|
||||
|
||||
UncachedKeyRing modified = ring;
|
||||
String uid = ring.getPublicKey().getUnorderedUserIds().get(1);
|
||||
|
||||
{ // first part, add new user id which is also primary
|
||||
parcel.mAddUserIds.add("jack");
|
||||
parcel.mChangePrimaryUserId = "jack";
|
||||
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("primary user id must be the one added",
|
||||
"jack", modified.getPublicKey().getPrimaryUserId());
|
||||
}
|
||||
|
||||
{ // second part, change primary to a different one
|
||||
parcel.reset();
|
||||
parcel.mChangePrimaryUserId = uid;
|
||||
|
||||
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||
|
||||
Assert.assertEquals("old keyring must have two outdated certificates", 2, onlyA.size());
|
||||
Assert.assertEquals("new keyring must have two new packets", 2, onlyB.size());
|
||||
|
||||
Assert.assertEquals("primary user id must be the one changed to",
|
||||
"pink", modified.getPublicKey().getPrimaryUserId());
|
||||
}
|
||||
|
||||
{ // third part, change primary to a non-existent one
|
||||
parcel.reset();
|
||||
//noinspection SpellCheckingInspection
|
||||
parcel.mChangePrimaryUserId = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
if (parcel.mChangePrimaryUserId.equals(passphrase)) {
|
||||
parcel.mChangePrimaryUserId += "A";
|
||||
}
|
||||
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
|
||||
Assert.assertNull("changing primary user id to a non-existent one should fail", modified);
|
||||
}
|
||||
|
||||
// check for revoked primary user id already done in revoke test
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
|
||||
UncachedKeyRing ring,
|
||||
ArrayList<RawPacket> onlyA,
|
||||
ArrayList<RawPacket> onlyB) {
|
||||
return applyModificationWithChecks(parcel, ring, onlyA, onlyB, true, true);
|
||||
}
|
||||
|
||||
// applies a parcel modification while running some integrity checks
|
||||
private static UncachedKeyRing applyModificationWithChecks(SaveKeyringParcel parcel,
|
||||
UncachedKeyRing ring,
|
||||
ArrayList<RawPacket> onlyA,
|
||||
ArrayList<RawPacket> onlyB,
|
||||
boolean canonicalize,
|
||||
boolean constantCanonicalize) {
|
||||
|
||||
try {
|
||||
|
||||
Assert.assertTrue("modified keyring must be secret", ring.isSecret());
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
||||
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
UncachedKeyRing rawModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
|
||||
Assert.assertNotNull("key modification failed", rawModified);
|
||||
|
||||
if (!canonicalize) {
|
||||
Assert.assertTrue("keyring must differ from original", KeyringTestingHelper.diffKeyrings(
|
||||
ring.getEncoded(), rawModified.getEncoded(), onlyA, onlyB));
|
||||
return rawModified;
|
||||
}
|
||||
|
||||
CanonicalizedKeyRing modified = rawModified.canonicalize(new OperationLog(), 0);
|
||||
if (constantCanonicalize) {
|
||||
Assert.assertTrue("key must be constant through canonicalization",
|
||||
!KeyringTestingHelper.diffKeyrings(
|
||||
modified.getEncoded(), rawModified.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
Assert.assertTrue("keyring must differ from original", KeyringTestingHelper.diffKeyrings(
|
||||
ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
|
||||
|
||||
return modified.getUncachedKeyRing();
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new AssertionFailedError("error during encoding!");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifySuccess() throws Exception {
|
||||
|
||||
UncachedKeyRing expectedKeyRing = KeyringBuilder.correctRing();
|
||||
UncachedKeyRing inputKeyRing = KeyringBuilder.ringWithExtraIncorrectSignature();
|
||||
|
||||
CanonicalizedKeyRing canonicalized = inputKeyRing.canonicalize(new OperationLog(), 0);
|
||||
Assert.assertNotNull("canonicalization must succeed", canonicalized);
|
||||
|
||||
ArrayList onlyA = new ArrayList<RawPacket>();
|
||||
ArrayList onlyB = new ArrayList<RawPacket>();
|
||||
//noinspection unchecked
|
||||
Assert.assertTrue("keyrings differ", !KeyringTestingHelper.diffKeyrings(
|
||||
expectedKeyRing.getEncoded(), expectedKeyRing.getEncoded(), onlyA, onlyB));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Just testing my own test code. Should really be using a library for this.
|
||||
*/
|
||||
@Test
|
||||
public void testConcat() throws Exception {
|
||||
byte[] actual = TestDataUtil.concatAll(new byte[]{1}, new byte[]{2,-2}, new byte[]{5},new byte[]{3});
|
||||
byte[] expected = new byte[]{1,2,-2,5,3};
|
||||
Assert.assertEquals(java.util.Arrays.toString(expected), java.util.Arrays.toString(actual));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
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.spongycastle.openpgp.PGPPrivateKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.service.OperationResultParcel;
|
||||
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
|
||||
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.Date;
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/** Tests for the UncachedKeyring.canonicalize method.
|
||||
*
|
||||
* This is a complex and crypto-relevant method, which takes care of sanitizing keyrings.
|
||||
* Test cases are made for all its assertions.
|
||||
*/
|
||||
|
||||
@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<RawPacket> onlyA = new ArrayList<RawPacket>();
|
||||
ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
|
||||
OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen;
|
||||
PGPSecretKey secretKey;
|
||||
|
||||
@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);
|
||||
|
||||
EditKeyResult 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);
|
||||
|
||||
// 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;
|
||||
|
||||
subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
secretKey = new PGPSecretKeyRing(ring.getEncoded(), new JcaKeyFingerprintCalculator())
|
||||
.getSecretKey();
|
||||
}
|
||||
|
||||
/** Make sure the assumptions made about the generated ring packet structure are valid. */
|
||||
@Test public void testGeneratedRingStructure() throws Exception {
|
||||
|
||||
Iterator<RawPacket> 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 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);
|
||||
CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
|
||||
|
||||
Assert.assertTrue("canonicalized keyring with invalid extra sig must be same as original one",
|
||||
!KeyringTestingHelper.diffKeyrings(
|
||||
ring.getEncoded(), canonicalized.getEncoded(), onlyA, onlyB));
|
||||
}
|
||||
|
||||
// remove user id certificate for one user
|
||||
final UncachedKeyRing base = KeyringTestingHelper.removePacket(ring, 2);
|
||||
|
||||
{ // user id without certificate should be removed
|
||||
CanonicalizedKeyRing 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 = 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());
|
||||
|
||||
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);
|
||||
CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
|
||||
|
||||
Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings(
|
||||
ring.getEncoded(), canonicalized.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 = 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());
|
||||
|
||||
Assert.assertArrayEquals("second stripped packet must be signature we removed",
|
||||
sig.getEncoded(), onlyA.get(1).buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test public void testUidDestroy() throws Exception {
|
||||
|
||||
// signature for "twi"
|
||||
ring = KeyringTestingHelper.removePacket(ring, 2);
|
||||
// signature for "pink"
|
||||
ring = KeyringTestingHelper.removePacket(ring, 3);
|
||||
|
||||
// canonicalization should fail, because there are no valid uids left
|
||||
CanonicalizedKeyRing canonicalized = ring.canonicalize(log, 0);
|
||||
Assert.assertNull("canonicalization of keyring with no valid uids should fail", canonicalized);
|
||||
|
||||
}
|
||||
|
||||
@Test public void testRevocationRedundant() throws Exception {
|
||||
|
||||
PGPSignature revocation = forgeSignature(
|
||||
secretKey, PGPSignature.KEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey());
|
||||
|
||||
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 1);
|
||||
|
||||
// try to add the same packet again, it should be rejected in all positions
|
||||
injectEverywhere(modified, revocation.getEncoded());
|
||||
|
||||
// an older (but different!) revocation should be rejected as well
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
|
||||
revocation = forgeSignature(
|
||||
secretKey, PGPSignature.KEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey());
|
||||
|
||||
injectEverywhere(modified, revocation.getEncoded());
|
||||
|
||||
}
|
||||
|
||||
@Test public void testUidRedundant() throws Exception {
|
||||
|
||||
// an older uid certificate should be rejected
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
|
||||
PGPSignature revocation = forgeSignature(
|
||||
secretKey, PGPSignature.DEFAULT_CERTIFICATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
|
||||
|
||||
injectEverywhere(ring, revocation.getEncoded());
|
||||
|
||||
}
|
||||
|
||||
@Test public void testUidRevocationOutdated() throws Exception {
|
||||
// an older uid revocation cert should be rejected
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
|
||||
PGPSignature revocation = forgeSignature(
|
||||
secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
|
||||
|
||||
injectEverywhere(ring, revocation.getEncoded());
|
||||
|
||||
}
|
||||
|
||||
@Test public void testUidRevocationRedundant() throws Exception {
|
||||
|
||||
PGPSignature revocation = forgeSignature(
|
||||
secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
|
||||
|
||||
// add that revocation to the base, and check if the redundant one will be rejected as well
|
||||
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 2);
|
||||
|
||||
injectEverywhere(modified, revocation.getEncoded());
|
||||
|
||||
// an older (but different!) uid revocation should be rejected as well
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
|
||||
revocation = forgeSignature(
|
||||
secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
|
||||
|
||||
injectEverywhere(modified, revocation.getEncoded());
|
||||
|
||||
}
|
||||
|
||||
@Test public void testSignatureBroken() throws Exception {
|
||||
|
||||
injectEverytype(secretKey, ring, subHashedPacketsGen, true);
|
||||
|
||||
}
|
||||
|
||||
@Test public void testForeignSignature() throws Exception {
|
||||
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
|
||||
parcel.mAddUserIds.add("trix");
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
|
||||
UncachedKeyRing foreign = op.createSecretKeyRing(parcel).getRing();
|
||||
|
||||
Assert.assertNotNull("initial test key creation must succeed", foreign);
|
||||
PGPSecretKey foreignSecretKey =
|
||||
new PGPSecretKeyRing(foreign.getEncoded(), new JcaKeyFingerprintCalculator())
|
||||
.getSecretKey();
|
||||
|
||||
injectEverytype(foreignSecretKey, ring, subHashedPacketsGen);
|
||||
|
||||
}
|
||||
|
||||
@Test public void testSignatureFuture() throws Exception {
|
||||
|
||||
// generate future
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() + 1000 * 1000));
|
||||
|
||||
injectEverytype(secretKey, ring, subHashedPacketsGen);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test public void testSignatureLocal() throws Exception {
|
||||
|
||||
// generate future
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
|
||||
subHashedPacketsGen.setExportable(false, false);
|
||||
|
||||
injectEverytype(secretKey, ring, subHashedPacketsGen);
|
||||
|
||||
}
|
||||
|
||||
@Test public void testSubkeyDestroy() throws Exception {
|
||||
|
||||
// signature for second key (first subkey)
|
||||
UncachedKeyRing modified = KeyringTestingHelper.removePacket(ring, 6);
|
||||
|
||||
// canonicalization should fail, because there are no valid uids left
|
||||
CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
|
||||
Assert.assertTrue("keyring with missing subkey binding sig should differ from intact one after canonicalization",
|
||||
KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
|
||||
onlyA, onlyB)
|
||||
);
|
||||
|
||||
Assert.assertEquals("canonicalized keyring should have two extra packets", 2, onlyA.size());
|
||||
Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size());
|
||||
|
||||
Assert.assertEquals("first missing packet should be the subkey",
|
||||
PacketTags.SECRET_SUBKEY, onlyA.get(0).tag);
|
||||
Assert.assertEquals("second missing packet should be subkey's signature",
|
||||
PacketTags.SIGNATURE, onlyA.get(1).tag);
|
||||
Assert.assertEquals("second missing packet should be next to subkey",
|
||||
onlyA.get(0).position + 1, onlyA.get(1).position);
|
||||
|
||||
}
|
||||
|
||||
@Test public void testSubkeyBindingNoPKB() throws Exception {
|
||||
|
||||
UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 1);
|
||||
Assert.assertTrue("second subkey must be able to sign", pKey.canSign());
|
||||
|
||||
PGPSignature sig;
|
||||
|
||||
subHashedPacketsGen.setKeyFlags(false, KeyFlags.SIGN_DATA);
|
||||
|
||||
{
|
||||
// forge a (newer) signature, which has the sign flag but no primary key binding sig
|
||||
PGPSignatureSubpacketGenerator unhashedSubs = new PGPSignatureSubpacketGenerator();
|
||||
|
||||
// just add any random signature, because why not
|
||||
unhashedSubs.setEmbeddedSignature(false, forgeSignature(
|
||||
secretKey, PGPSignature.POSITIVE_CERTIFICATION, subHashedPacketsGen,
|
||||
secretKey.getPublicKey()
|
||||
)
|
||||
);
|
||||
|
||||
sig = forgeSignature(
|
||||
secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, unhashedSubs,
|
||||
secretKey.getPublicKey(), pKey.getPublicKey());
|
||||
|
||||
// inject in the right position
|
||||
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig.getEncoded(), 6);
|
||||
|
||||
// canonicalize, and check if we lose the bad signature
|
||||
CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
|
||||
Assert.assertFalse("subkey binding signature should be gone after canonicalization",
|
||||
KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
|
||||
onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
|
||||
{ // now try one with a /bad/ primary key binding signature
|
||||
|
||||
PGPSignatureSubpacketGenerator unhashedSubs = new PGPSignatureSubpacketGenerator();
|
||||
// this one is signed by the primary key itself, not the subkey - but it IS primary binding
|
||||
unhashedSubs.setEmbeddedSignature(false, forgeSignature(
|
||||
secretKey, PGPSignature.PRIMARYKEY_BINDING, subHashedPacketsGen,
|
||||
secretKey.getPublicKey(), pKey.getPublicKey()
|
||||
)
|
||||
);
|
||||
|
||||
sig = forgeSignature(
|
||||
secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, unhashedSubs,
|
||||
secretKey.getPublicKey(), pKey.getPublicKey());
|
||||
|
||||
// inject in the right position
|
||||
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig.getEncoded(), 6);
|
||||
|
||||
// canonicalize, and check if we lose the bad signature
|
||||
CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
|
||||
Assert.assertFalse("subkey binding signature should be gone after canonicalization",
|
||||
KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
|
||||
onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test public void testSubkeyBindingRedundant() throws Exception {
|
||||
|
||||
UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 2);
|
||||
|
||||
subHashedPacketsGen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS);
|
||||
PGPSignature sig2 = forgeSignature(
|
||||
secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen,
|
||||
secretKey.getPublicKey(), pKey.getPublicKey());
|
||||
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
|
||||
PGPSignature sig1 = forgeSignature(
|
||||
secretKey, PGPSignature.SUBKEY_REVOCATION, subHashedPacketsGen,
|
||||
secretKey.getPublicKey(), pKey.getPublicKey());
|
||||
|
||||
subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -100*1000));
|
||||
PGPSignature sig3 = forgeSignature(
|
||||
secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen,
|
||||
secretKey.getPublicKey(), pKey.getPublicKey());
|
||||
|
||||
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig1.getEncoded(), 8);
|
||||
modified = KeyringTestingHelper.injectPacket(modified, sig2.getEncoded(), 9);
|
||||
modified = KeyringTestingHelper.injectPacket(modified, sig1.getEncoded(), 10);
|
||||
modified = KeyringTestingHelper.injectPacket(modified, sig3.getEncoded(), 11);
|
||||
|
||||
// canonicalize, and check if we lose the bad signature
|
||||
CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
|
||||
Assert.assertTrue("subkey binding signature should be gone after canonicalization",
|
||||
KeyringTestingHelper.diffKeyrings(modified.getEncoded(), canonicalized.getEncoded(),
|
||||
onlyA, onlyB)
|
||||
);
|
||||
|
||||
Assert.assertEquals("canonicalized keyring should have lost two packets", 3, onlyA.size());
|
||||
Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size());
|
||||
|
||||
Assert.assertEquals("first missing packet should be the subkey",
|
||||
PacketTags.SIGNATURE, onlyA.get(0).tag);
|
||||
Assert.assertEquals("second missing packet should be a signature",
|
||||
PacketTags.SIGNATURE, onlyA.get(1).tag);
|
||||
Assert.assertEquals("second missing packet should be a signature",
|
||||
PacketTags.SIGNATURE, onlyA.get(2).tag);
|
||||
|
||||
}
|
||||
|
||||
private static final int[] sigtypes_direct = new int[] {
|
||||
PGPSignature.KEY_REVOCATION,
|
||||
PGPSignature.DIRECT_KEY,
|
||||
};
|
||||
private static final int[] sigtypes_uid = new int[] {
|
||||
PGPSignature.DEFAULT_CERTIFICATION,
|
||||
PGPSignature.NO_CERTIFICATION,
|
||||
PGPSignature.CASUAL_CERTIFICATION,
|
||||
PGPSignature.POSITIVE_CERTIFICATION,
|
||||
PGPSignature.CERTIFICATION_REVOCATION,
|
||||
};
|
||||
private static final int[] sigtypes_subkey = new int[] {
|
||||
PGPSignature.SUBKEY_BINDING,
|
||||
PGPSignature.PRIMARYKEY_BINDING,
|
||||
PGPSignature.SUBKEY_REVOCATION,
|
||||
};
|
||||
|
||||
private static void injectEverytype(PGPSecretKey secretKey,
|
||||
UncachedKeyRing ring,
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen)
|
||||
throws Exception {
|
||||
injectEverytype(secretKey, ring, subHashedPacketsGen, false);
|
||||
}
|
||||
|
||||
private static void injectEverytype(PGPSecretKey secretKey,
|
||||
UncachedKeyRing ring,
|
||||
PGPSignatureSubpacketGenerator subHashedPacketsGen,
|
||||
boolean breakSig)
|
||||
throws Exception {
|
||||
|
||||
for (int sigtype : sigtypes_direct) {
|
||||
PGPSignature sig = forgeSignature(
|
||||
secretKey, sigtype, subHashedPacketsGen, secretKey.getPublicKey());
|
||||
byte[] encoded = sig.getEncoded();
|
||||
if (breakSig) {
|
||||
encoded[encoded.length-10] += 1;
|
||||
}
|
||||
injectEverywhere(ring, encoded);
|
||||
}
|
||||
|
||||
for (int sigtype : sigtypes_uid) {
|
||||
PGPSignature sig = forgeSignature(
|
||||
secretKey, sigtype, subHashedPacketsGen, "twi", secretKey.getPublicKey());
|
||||
|
||||
byte[] encoded = sig.getEncoded();
|
||||
if (breakSig) {
|
||||
encoded[encoded.length-10] += 1;
|
||||
}
|
||||
injectEverywhere(ring, encoded);
|
||||
}
|
||||
|
||||
for (int sigtype : sigtypes_subkey) {
|
||||
PGPSignature sig = forgeSignature(
|
||||
secretKey, sigtype, subHashedPacketsGen,
|
||||
secretKey.getPublicKey(), secretKey.getPublicKey());
|
||||
|
||||
byte[] encoded = sig.getEncoded();
|
||||
if (breakSig) {
|
||||
encoded[encoded.length-10] += 1;
|
||||
}
|
||||
injectEverywhere(ring, encoded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void injectEverywhere(UncachedKeyRing ring, byte[] packet) throws Exception {
|
||||
|
||||
OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
|
||||
|
||||
byte[] encodedRing = ring.getEncoded();
|
||||
|
||||
for(int i = 0; i < totalPackets; i++) {
|
||||
|
||||
byte[] brokenEncoded = KeyringTestingHelper.injectPacket(encodedRing, packet, i);
|
||||
|
||||
try {
|
||||
|
||||
UncachedKeyRing brokenRing = UncachedKeyRing.decodeFromData(brokenEncoded);
|
||||
|
||||
CanonicalizedKeyRing canonicalized = brokenRing.canonicalize(log, 0);
|
||||
if (canonicalized == null) {
|
||||
System.out.println("ok, canonicalization failed.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.assertArrayEquals("injected bad signature must be gone after canonicalization",
|
||||
ring.getEncoded(), canonicalized.getEncoded());
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("ok, rejected with: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static PGPSignature forgeSignature(PGPSecretKey key, int type,
|
||||
PGPSignatureSubpacketGenerator subpackets,
|
||||
PGPPublicKey publicKey)
|
||||
throws Exception {
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
||||
PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
publicKey.getAlgorithm(), PGPUtil.SHA1)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.setHashedSubpackets(subpackets.generate());
|
||||
sGen.init(type, privateKey);
|
||||
return sGen.generateCertification(publicKey);
|
||||
|
||||
}
|
||||
|
||||
private static PGPSignature forgeSignature(PGPSecretKey key, int type,
|
||||
PGPSignatureSubpacketGenerator subpackets,
|
||||
String userId, PGPPublicKey publicKey)
|
||||
throws Exception {
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
||||
PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
publicKey.getAlgorithm(), PGPUtil.SHA1)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.setHashedSubpackets(subpackets.generate());
|
||||
sGen.init(type, privateKey);
|
||||
return sGen.generateCertification(userId, publicKey);
|
||||
|
||||
}
|
||||
|
||||
private static PGPSignature forgeSignature(PGPSecretKey key, int type,
|
||||
PGPSignatureSubpacketGenerator subpackets,
|
||||
PGPPublicKey publicKey, PGPPublicKey signedKey)
|
||||
throws Exception {
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
||||
PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
publicKey.getAlgorithm(), PGPUtil.SHA1)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.setHashedSubpackets(subpackets.generate());
|
||||
sGen.init(type, privateKey);
|
||||
return sGen.generateCertification(publicKey, signedKey);
|
||||
|
||||
}
|
||||
|
||||
private static PGPSignature forgeSignature(PGPSecretKey key, int type,
|
||||
PGPSignatureSubpacketGenerator hashedSubs,
|
||||
PGPSignatureSubpacketGenerator unhashedSubs,
|
||||
PGPPublicKey publicKey, PGPPublicKey signedKey)
|
||||
throws Exception {
|
||||
|
||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
||||
PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
|
||||
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
publicKey.getAlgorithm(), PGPUtil.SHA1)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
sGen.setHashedSubpackets(hashedSubs.generate());
|
||||
sGen.setUnhashedSubpackets(unhashedSubs.generate());
|
||||
sGen.init(type, privateKey);
|
||||
return sGen.generateCertification(publicKey, signedKey);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
import org.spongycastle.bcpg.PacketTags;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.service.OperationResultParcel;
|
||||
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/** Tests for the UncachedKeyring.merge method.
|
||||
*
|
||||
* This is another complex, crypto-related method. It merges information from one keyring into
|
||||
* another, keeping information from the base (ie, called object) keyring in case of conflicts.
|
||||
* The types of keys may be Public or Secret and can be mixed, For mixed types the result type
|
||||
* will be the same as the base keyring.
|
||||
*
|
||||
* Test cases:
|
||||
* - Merging keyrings with different masterKeyIds should fail
|
||||
* - Merging a key with itself should be a no-operation
|
||||
* - Merging a key with an extra revocation certificate, it should have that certificate
|
||||
* - Merging a key with an extra user id, it should have that extra user id and its certificates
|
||||
* - Merging a key with an extra user id certificate, it should have that certificate
|
||||
* - Merging a key with an extra subkey, it should have that subkey
|
||||
* - Merging a key with an extra subkey certificate, it should have that certificate
|
||||
* - All of the above operations should work regardless of the key types. This means in particular
|
||||
* that for new subkeys, an equivalent subkey of the proper type must be generated.
|
||||
* - In case of two secret keys with the same id but different S2K, the key of the base keyring
|
||||
* should be preferred (TODO or should it?)
|
||||
*
|
||||
* Note that the merge operation does not care about certificate validity, a bad certificate or
|
||||
* packet will be copied regardless. Filtering out bad packets is done with canonicalization.
|
||||
*
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
|
||||
public class UncachedKeyringMergeTest {
|
||||
|
||||
static UncachedKeyRing staticRingA, staticRingB;
|
||||
UncachedKeyRing ringA, ringB;
|
||||
ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
|
||||
ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
|
||||
OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
|
||||
PgpKeyOperation op;
|
||||
SaveKeyringParcel parcel;
|
||||
|
||||
@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.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();
|
||||
|
||||
EditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
staticRingA = result.getRing();
|
||||
}
|
||||
|
||||
{
|
||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
|
||||
|
||||
parcel.mAddUserIds.add("shy");
|
||||
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
||||
parcel.mNewPassphrase = "";
|
||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||
|
||||
OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
|
||||
EditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
staticRingB = result.getRing();
|
||||
}
|
||||
|
||||
Assert.assertNotNull("initial test key creation must succeed", staticRingA);
|
||||
Assert.assertNotNull("initial test key creation must succeed", staticRingB);
|
||||
|
||||
// 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;
|
||||
ringA = staticRingA;
|
||||
ringB = staticRingB;
|
||||
|
||||
// setting up some parameters just to reduce code duplication
|
||||
op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
|
||||
|
||||
// set this up, gonna need it more than once
|
||||
parcel = new SaveKeyringParcel();
|
||||
parcel.mMasterKeyId = ringA.getMasterKeyId();
|
||||
parcel.mFingerprint = ringA.getFingerprint();
|
||||
}
|
||||
|
||||
public void testSelfNoOp() throws Exception {
|
||||
|
||||
UncachedKeyRing merged = mergeWithChecks(ringA, ringA, null);
|
||||
Assert.assertArrayEquals("keyring merged with itself must be identical",
|
||||
ringA.getEncoded(), merged.getEncoded()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentMasterKeyIds() throws Exception {
|
||||
|
||||
Assert.assertNotEquals("generated key ids must be different",
|
||||
ringA.getMasterKeyId(), ringB.getMasterKeyId());
|
||||
|
||||
Assert.assertNull("merging keys with differing key ids must fail",
|
||||
ringA.merge(ringB, log, 0));
|
||||
Assert.assertNull("merging keys with differing key ids must fail",
|
||||
ringB.merge(ringA, log, 0));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddedUserId() throws Exception {
|
||||
|
||||
UncachedKeyRing modifiedA, modifiedB; {
|
||||
CanonicalizedSecretKeyRing secretRing =
|
||||
new CanonicalizedSecretKeyRing(ringA.getEncoded(), false, 0);
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add("flim");
|
||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddUserIds.add("flam");
|
||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
}
|
||||
|
||||
{ // merge A into base
|
||||
UncachedKeyRing merged = mergeWithChecks(ringA, modifiedA);
|
||||
|
||||
Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
|
||||
Assert.assertEquals("merged keyring must have gained two packets", 2, onlyB.size());
|
||||
Assert.assertTrue("merged keyring must contain new user id",
|
||||
merged.getPublicKey().getUnorderedUserIds().contains("flim"));
|
||||
}
|
||||
|
||||
{ // merge A into B
|
||||
UncachedKeyRing merged = mergeWithChecks(modifiedA, modifiedB, ringA);
|
||||
|
||||
Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
|
||||
Assert.assertEquals("merged keyring must have gained four packets", 4, onlyB.size());
|
||||
Assert.assertTrue("merged keyring must contain first new user id",
|
||||
merged.getPublicKey().getUnorderedUserIds().contains("flim"));
|
||||
Assert.assertTrue("merged keyring must contain second new user id",
|
||||
merged.getPublicKey().getUnorderedUserIds().contains("flam"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddedSubkeyId() throws Exception {
|
||||
|
||||
UncachedKeyRing modifiedA, modifiedB;
|
||||
long subKeyIdA, subKeyIdB;
|
||||
{
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), false, 0);
|
||||
|
||||
parcel.reset();
|
||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||
Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
|
||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
|
||||
subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
|
||||
subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
UncachedKeyRing merged = mergeWithChecks(ringA, modifiedA);
|
||||
|
||||
Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
|
||||
Assert.assertEquals("merged keyring must have gained two packets", 2, onlyB.size());
|
||||
|
||||
long mergedKeyId = KeyringTestingHelper.getSubkeyId(merged, 2);
|
||||
Assert.assertEquals("merged keyring must contain the new subkey", subKeyIdA, mergedKeyId);
|
||||
}
|
||||
|
||||
{
|
||||
UncachedKeyRing merged = mergeWithChecks(modifiedA, modifiedB, ringA);
|
||||
|
||||
Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
|
||||
Assert.assertEquals("merged keyring must have gained four packets", 4, onlyB.size());
|
||||
|
||||
Iterator<UncachedPublicKey> it = merged.getPublicKeys();
|
||||
it.next(); it.next();
|
||||
Assert.assertEquals("merged keyring must contain the new subkey",
|
||||
subKeyIdA, it.next().getKeyId());
|
||||
Assert.assertEquals("merged keyring must contain both new subkeys",
|
||||
subKeyIdB, it.next().getKeyId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddedKeySignature() throws Exception {
|
||||
|
||||
final UncachedKeyRing modified; {
|
||||
parcel.reset();
|
||||
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
|
||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
|
||||
ringA.getEncoded(), false, 0);
|
||||
modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||
}
|
||||
|
||||
{
|
||||
UncachedKeyRing merged = ringA.merge(modified, log, 0);
|
||||
Assert.assertNotNull("merge must succeed", merged);
|
||||
Assert.assertFalse(
|
||||
"merging keyring with extra signatures into its base should yield that same keyring",
|
||||
KeyringTestingHelper.diffKeyrings(merged.getEncoded(), modified.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddedUserIdSignature() throws Exception {
|
||||
|
||||
final UncachedKeyRing pubRing = ringA.extractPublicKeyRing();
|
||||
|
||||
final UncachedKeyRing modified; {
|
||||
CanonicalizedPublicKeyRing publicRing = new CanonicalizedPublicKeyRing(
|
||||
pubRing.getEncoded(), 0);
|
||||
|
||||
CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing(
|
||||
ringB.getEncoded(), false, 0).getSecretKey();
|
||||
secretKey.unlock("");
|
||||
// sign all user ids
|
||||
modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds());
|
||||
}
|
||||
|
||||
{
|
||||
UncachedKeyRing merged = ringA.merge(modified, log, 0);
|
||||
Assert.assertNotNull("merge must succeed", merged);
|
||||
Assert.assertArrayEquals("foreign signatures should not be merged into secret key",
|
||||
ringA.getEncoded(), merged.getEncoded()
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
byte[] sig = KeyringTestingHelper.getNth(
|
||||
modified.getPublicKey().getSignaturesForId("twi"), 1).getEncoded();
|
||||
|
||||
// inject the (foreign!) signature into subkey signature position
|
||||
UncachedKeyRing moreModified = KeyringTestingHelper.injectPacket(modified, sig, 1);
|
||||
|
||||
UncachedKeyRing merged = ringA.merge(moreModified, log, 0);
|
||||
Assert.assertNotNull("merge must succeed", merged);
|
||||
Assert.assertArrayEquals("foreign signatures should not be merged into secret key",
|
||||
ringA.getEncoded(), merged.getEncoded()
|
||||
);
|
||||
|
||||
merged = pubRing.merge(moreModified, log, 0);
|
||||
Assert.assertNotNull("merge must succeed", merged);
|
||||
Assert.assertTrue(
|
||||
"merged keyring should contain new signature",
|
||||
KeyringTestingHelper.diffKeyrings(pubRing.getEncoded(), merged.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
Assert.assertEquals("merged keyring should be missing no packets", 0, onlyA.size());
|
||||
Assert.assertEquals("merged keyring should contain exactly two more packets", 2, onlyB.size());
|
||||
Assert.assertEquals("first added packet should be a signature",
|
||||
PacketTags.SIGNATURE, onlyB.get(0).tag);
|
||||
Assert.assertEquals("first added packet should be in the position we injected it at",
|
||||
1, onlyB.get(0).position);
|
||||
Assert.assertEquals("second added packet should be a signature",
|
||||
PacketTags.SIGNATURE, onlyB.get(1).tag);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
UncachedKeyRing merged = pubRing.merge(modified, log, 0);
|
||||
Assert.assertNotNull("merge must succeed", merged);
|
||||
Assert.assertFalse(
|
||||
"merging keyring with extra signatures into its base should yield that same keyring",
|
||||
KeyringTestingHelper.diffKeyrings(merged.getEncoded(), modified.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private UncachedKeyRing mergeWithChecks(UncachedKeyRing a, UncachedKeyRing b)
|
||||
throws Exception {
|
||||
return mergeWithChecks(a, b, a);
|
||||
}
|
||||
|
||||
private UncachedKeyRing mergeWithChecks(UncachedKeyRing a, UncachedKeyRing b,
|
||||
UncachedKeyRing base)
|
||||
throws Exception {
|
||||
|
||||
Assert.assertTrue("merging keyring must be secret type", a.isSecret());
|
||||
Assert.assertTrue("merged keyring must be secret type", b.isSecret());
|
||||
|
||||
final UncachedKeyRing resultA;
|
||||
UncachedKeyRing resultB;
|
||||
|
||||
{ // sec + sec
|
||||
resultA = a.merge(b, log, 0);
|
||||
Assert.assertNotNull("merge must succeed as sec(a)+sec(b)", resultA);
|
||||
|
||||
resultB = b.merge(a, log, 0);
|
||||
Assert.assertNotNull("merge must succeed as sec(b)+sec(a)", resultB);
|
||||
|
||||
// check commutativity, if requested
|
||||
Assert.assertFalse("result of merge must be commutative",
|
||||
KeyringTestingHelper.diffKeyrings(
|
||||
resultA.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
|
||||
final UncachedKeyRing pubA = a.extractPublicKeyRing();
|
||||
final UncachedKeyRing pubB = b.extractPublicKeyRing();
|
||||
|
||||
{ // sec + pub, pub + sec, and pub + pub
|
||||
|
||||
try {
|
||||
resultB = a.merge(pubB, log, 0);
|
||||
Assert.assertNotNull("merge must succeed as sec(a)+pub(b)", resultA);
|
||||
|
||||
Assert.assertFalse("result of sec(a)+pub(b) must be same as sec(a)+sec(b)",
|
||||
KeyringTestingHelper.diffKeyrings(
|
||||
resultA.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
} catch (RuntimeException e) {
|
||||
System.out.println("special case, dummy key generation not in yet");
|
||||
}
|
||||
|
||||
final UncachedKeyRing pubResult = resultA.extractPublicKeyRing();
|
||||
|
||||
resultB = pubA.merge(b, log, 0);
|
||||
Assert.assertNotNull("merge must succeed as pub(a)+sec(b)", resultA);
|
||||
|
||||
Assert.assertFalse("result of pub(a)+sec(b) must be same as pub(sec(a)+sec(b))",
|
||||
KeyringTestingHelper.diffKeyrings(
|
||||
pubResult.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
|
||||
resultB = pubA.merge(pubB, log, 0);
|
||||
Assert.assertNotNull("merge must succeed as pub(a)+pub(b)", resultA);
|
||||
|
||||
Assert.assertFalse("result of pub(a)+pub(b) must be same as pub(sec(a)+sec(b))",
|
||||
KeyringTestingHelper.diffKeyrings(
|
||||
pubResult.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if (base != null) {
|
||||
// set up onlyA and onlyB to be a diff to the base
|
||||
Assert.assertTrue("merged keyring must differ from base",
|
||||
KeyringTestingHelper.diffKeyrings(
|
||||
base.getEncoded(), resultA.getEncoded(), onlyA, onlyB)
|
||||
);
|
||||
}
|
||||
|
||||
return resultA;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
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, staticPubRing;
|
||||
UncachedKeyRing ring, pubRing;
|
||||
ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
|
||||
ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
|
||||
PgpKeyOperation op;
|
||||
SaveKeyringParcel parcel;
|
||||
|
||||
@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);
|
||||
|
||||
EditKeyResult result = op.createSecretKeyRing(parcel);
|
||||
staticRing = result.getRing();
|
||||
staticPubRing = staticRing.extractPublicKeyRing();
|
||||
|
||||
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;
|
||||
pubRing = staticPubRing;
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testPublicKeyItRemove() throws Exception {
|
||||
Iterator<UncachedPublicKey> it = ring.getPublicKeys();
|
||||
it.remove();
|
||||
}
|
||||
|
||||
@Test(expected = PgpGeneralException.class)
|
||||
public void testDecodeFromEmpty() throws Exception {
|
||||
UncachedKeyRing.decodeFromData(new byte[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArmorIdentity() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ring.encodeArmored(out, "OpenKeychain");
|
||||
|
||||
Assert.assertArrayEquals("armor encoded and decoded ring should be identical to original",
|
||||
ring.getEncoded(),
|
||||
UncachedKeyRing.decodeFromData(out.toByteArray()).getEncoded());
|
||||
}
|
||||
|
||||
@Test(expected = PgpGeneralException.class)
|
||||
public void testDecodeEncodeMulti() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
// encode secret and public ring in here
|
||||
ring.encodeArmored(out, "OpenKeychain");
|
||||
pubRing.encodeArmored(out, "OpenKeychain");
|
||||
|
||||
Iterator<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",
|
||||
ring.getEncoded(), it.next().getEncoded());
|
||||
Assert.assertTrue("there should be two rings in the stream", it.hasNext());
|
||||
Assert.assertArrayEquals("second ring should be the second we put in",
|
||||
pubRing.getEncoded(), it.next().getEncoded());
|
||||
Assert.assertFalse("there should be two rings in the stream", it.hasNext());
|
||||
|
||||
// this should fail with PgpGeneralException, since it expects exactly one ring
|
||||
UncachedKeyRing.decodeFromData(out.toByteArray());
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testPublicExtractPublic() throws Exception {
|
||||
// can't do this, either!
|
||||
pubRing.extractPublicKeyRing();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user