mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
Add more introduction tests for fake MAC and modified timestamp
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
package org.briarproject;
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import net.jodah.concurrentunit.Waiter;
|
import net.jodah.concurrentunit.Waiter;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestDatabaseModule;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.SessionId;
|
import org.briarproject.api.clients.SessionId;
|
||||||
@@ -13,8 +16,10 @@ import org.briarproject.api.contact.ContactManager;
|
|||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.crypto.KeyPair;
|
import org.briarproject.api.crypto.KeyPair;
|
||||||
import org.briarproject.api.crypto.SecretKey;
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.api.crypto.Signature;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfEntry;
|
import org.briarproject.api.data.BdfEntry;
|
||||||
|
import org.briarproject.api.data.BdfList;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Metadata;
|
import org.briarproject.api.db.Metadata;
|
||||||
@@ -29,7 +34,6 @@ import org.briarproject.api.event.MessageStateChangedEvent;
|
|||||||
import org.briarproject.api.identity.AuthorFactory;
|
import org.briarproject.api.identity.AuthorFactory;
|
||||||
import org.briarproject.api.identity.IdentityManager;
|
import org.briarproject.api.identity.IdentityManager;
|
||||||
import org.briarproject.api.identity.LocalAuthor;
|
import org.briarproject.api.identity.LocalAuthor;
|
||||||
import org.briarproject.api.introduction.IntroducerProtocolState;
|
|
||||||
import org.briarproject.api.introduction.IntroductionManager;
|
import org.briarproject.api.introduction.IntroductionManager;
|
||||||
import org.briarproject.api.introduction.IntroductionMessage;
|
import org.briarproject.api.introduction.IntroductionMessage;
|
||||||
import org.briarproject.api.introduction.IntroductionRequest;
|
import org.briarproject.api.introduction.IntroductionRequest;
|
||||||
@@ -45,9 +49,6 @@ import org.briarproject.api.sync.ValidationManager.State;
|
|||||||
import org.briarproject.api.system.Clock;
|
import org.briarproject.api.system.Clock;
|
||||||
import org.briarproject.contact.ContactModule;
|
import org.briarproject.contact.ContactModule;
|
||||||
import org.briarproject.crypto.CryptoModule;
|
import org.briarproject.crypto.CryptoModule;
|
||||||
import org.briarproject.introduction.IntroductionGroupFactory;
|
|
||||||
import org.briarproject.introduction.IntroductionModule;
|
|
||||||
import org.briarproject.introduction.MessageSender;
|
|
||||||
import org.briarproject.lifecycle.LifecycleModule;
|
import org.briarproject.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.properties.PropertiesModule;
|
import org.briarproject.properties.PropertiesModule;
|
||||||
import org.briarproject.sync.SyncModule;
|
import org.briarproject.sync.SyncModule;
|
||||||
@@ -61,11 +62,13 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -75,11 +78,16 @@ import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
|||||||
import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
|
import static org.briarproject.TestPluginsModule.TRANSPORT_ID;
|
||||||
import static org.briarproject.api.clients.MessageQueueManager.QUEUE_STATE_KEY;
|
import static org.briarproject.api.clients.MessageQueueManager.QUEUE_STATE_KEY;
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MAC;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MAC_KEY;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.NONCE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
import static org.briarproject.api.introduction.IntroductionConstants.SIGNATURE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
@@ -89,6 +97,7 @@ import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class IntroductionIntegrationTest extends BriarTestCase {
|
public class IntroductionIntegrationTest extends BriarTestCase {
|
||||||
|
|
||||||
@@ -123,12 +132,19 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
private final String INTRODUCER = "Introducer";
|
private final String INTRODUCER = "Introducer";
|
||||||
private final String INTRODUCEE1 = "Introducee1";
|
private final String INTRODUCEE1 = "Introducee1";
|
||||||
private final String INTRODUCEE2 = "Introducee2";
|
private final String INTRODUCEE2 = "Introducee2";
|
||||||
|
private IntroducerListener listener0;
|
||||||
|
private IntroduceeListener listener1;
|
||||||
|
private IntroduceeListener listener2;
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(IntroductionIntegrationTest.class.getName());
|
Logger.getLogger(IntroductionIntegrationTest.class.getName());
|
||||||
|
|
||||||
private IntroductionIntegrationTestComponent t0, t1, t2;
|
private IntroductionIntegrationTestComponent t0, t1, t2;
|
||||||
|
|
||||||
|
interface StateVisitor {
|
||||||
|
boolean visit(BdfDictionary response);
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
IntroductionIntegrationTestComponent component =
|
IntroductionIntegrationTestComponent component =
|
||||||
@@ -172,21 +188,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroductionSession() throws Exception {
|
public void testIntroductionSession() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, true);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -258,21 +264,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroductionSessionFirstDecline() throws Exception {
|
public void testIntroductionSessionFirstDecline() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(false, true);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, false);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -335,21 +331,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroductionSessionSecondDecline() throws Exception {
|
public void testIntroductionSessionSecondDecline() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, false);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, false);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -407,21 +393,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroductionSessionDelayedFirstDecline() throws Exception {
|
public void testIntroductionSessionDelayedFirstDecline() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(false, false);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, false);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, false);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -470,19 +446,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroductionToSameContact() throws Exception {
|
public void testIntroductionToSameContact() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, false);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -521,9 +489,6 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
TestUtils.getRandomBytes(123));
|
TestUtils.getRandomBytes(123));
|
||||||
identityManager1.addLocalAuthor(author2);
|
identityManager1.addLocalAuthor(author2);
|
||||||
|
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
|
||||||
|
|
||||||
// Add introducees' authors as contacts
|
// Add introducees' authors as contacts
|
||||||
contactId1 = contactManager0.addContact(author1,
|
contactId1 = contactManager0.addContact(author1,
|
||||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||||
@@ -543,12 +508,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
author2.getId(), master, clock.currentTimeMillis(), false,
|
author2.getId(), master, clock.currentTimeMillis(), false,
|
||||||
true, true
|
true, true
|
||||||
);
|
);
|
||||||
|
|
||||||
// listen to events
|
// listen to events
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
addListeners(true, false);
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
// Add Transport Properties
|
||||||
t1.getEventBus().addListener(listener1);
|
TransportPropertyManager tpm0 = t0.getTransportPropertyManager();
|
||||||
|
TransportPropertyManager tpm1 = t1.getTransportPropertyManager();
|
||||||
|
TransportProperties tp = new TransportProperties(
|
||||||
|
Collections.singletonMap("key", "value"));
|
||||||
|
tpm0.mergeLocalProperties(TRANSPORT_ID, tp);
|
||||||
|
deliverMessage(sync0, contactId01, sync1, contactId1, "0 to 11");
|
||||||
|
deliverMessage(sync0, contactId02, sync1, contactId2, "0 to 12");
|
||||||
|
tpm1.mergeLocalProperties(TRANSPORT_ID, tp);
|
||||||
|
deliverMessage(sync1, contactId1, sync0, contactId01, "1 to 01");
|
||||||
|
deliverMessage(sync1, contactId2, sync0, contactId02, "1 to 02");
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
@@ -612,21 +585,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testSessionIdReuse() throws Exception {
|
public void testSessionIdReuse() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, true);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -690,21 +653,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroducerRemovedCleanup() throws Exception {
|
public void testIntroducerRemovedCleanup() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, true);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -757,21 +710,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
public void testIntroduceesRemovedCleanup() throws Exception {
|
public void testIntroduceesRemovedCleanup() throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
// Add Identities And Contacts
|
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, true);
|
||||||
// Add Transport Properties
|
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -834,22 +777,15 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private void testModifiedResponse(StateVisitor visitor)
|
||||||
public void testModifiedResponse() throws Exception {
|
throws Exception {
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
try {
|
try {
|
||||||
addDefaultIdentities();
|
addDefaultIdentities();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
|
addListeners(true, true);
|
||||||
addTransportProperties();
|
addTransportProperties();
|
||||||
|
|
||||||
// listen to events
|
|
||||||
IntroducerListener listener0 = new IntroducerListener();
|
|
||||||
t0.getEventBus().addListener(listener0);
|
|
||||||
IntroduceeListener listener1 = new IntroduceeListener(1, true);
|
|
||||||
t1.getEventBus().addListener(listener1);
|
|
||||||
IntroduceeListener listener2 = new IntroduceeListener(2, true);
|
|
||||||
t2.getEventBus().addListener(listener2);
|
|
||||||
|
|
||||||
// make introduction
|
// make introduction
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
Contact introducee1 = contactManager0.getContact(contactId1);
|
Contact introducee1 = contactManager0.getContact(contactId1);
|
||||||
@@ -867,28 +803,19 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
eventWaiter.await(TIMEOUT, 1);
|
eventWaiter.await(TIMEOUT, 1);
|
||||||
|
|
||||||
// get response to be forwarded
|
// get response to be forwarded
|
||||||
MessageId responseId = null;
|
Entry<MessageId, BdfDictionary> resp =
|
||||||
BdfDictionary response = null;
|
getMessageFor(introducee2, TYPE_RESPONSE);
|
||||||
Group g2 = introductionGroupFactory
|
MessageId responseId = resp.getKey();
|
||||||
.createIntroductionGroup(introducee2);
|
BdfDictionary response = resp.getValue();
|
||||||
ClientHelper clientHelper0 = t0.getClientHelper();
|
|
||||||
Map<MessageId, BdfDictionary> map =
|
|
||||||
clientHelper0.getMessageMetadataAsDictionary(g2.getId());
|
|
||||||
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
|
||||||
if (entry.getValue().getLong(TYPE) == TYPE_RESPONSE) {
|
|
||||||
responseId = entry.getKey();
|
|
||||||
response = entry.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertTrue(responseId != null && response != null);
|
|
||||||
|
|
||||||
// adapt outgoing message queue to removed message
|
// adapt outgoing message queue to removed message
|
||||||
|
ClientHelper clientHelper0 = t0.getClientHelper();
|
||||||
|
Group g2 = introductionGroupFactory
|
||||||
|
.createIntroductionGroup(introducee2);
|
||||||
decreaseOutgoingMessageCounter(clientHelper0, g2.getId(), 1);
|
decreaseOutgoingMessageCounter(clientHelper0, g2.getId(), 1);
|
||||||
|
|
||||||
// modify response by changing transport properties
|
// allow visitor to modify response
|
||||||
BdfDictionary tp = response.getDictionary(TRANSPORT);
|
boolean earlyAbort = visitor.visit(response);
|
||||||
tp.put("fakeId", BdfDictionary.of(new BdfEntry("fake", "fake")));
|
|
||||||
response.put(TRANSPORT, tp);
|
|
||||||
|
|
||||||
// replace original response with modified one
|
// replace original response with modified one
|
||||||
MessageSender sender0 = t0.getMessageSender();
|
MessageSender sender0 = t0.getMessageSender();
|
||||||
@@ -910,11 +837,7 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
// sync first ACK and its forward
|
// sync first ACK and forward it
|
||||||
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
|
||||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
|
||||||
|
|
||||||
// sync second ACK and forward it
|
|
||||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
@@ -931,37 +854,167 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
assertEquals(1, contacts2.size());
|
assertEquals(1, contacts2.size());
|
||||||
|
|
||||||
// sync abort message to introducer
|
// sync introducee2's ack and following abort
|
||||||
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
deliverMessage(sync2, contactId2, sync0, contactId0, 2, "2 to 0");
|
||||||
|
|
||||||
// ensure introducer got the abort
|
// ensure introducer got the abort
|
||||||
SessionId sessionId = new SessionId(response.getRaw(SESSION_ID));
|
assertTrue(listener0.aborted);
|
||||||
BdfDictionary state =
|
|
||||||
clientHelper0.getMessageMetadataAsDictionary(sessionId);
|
|
||||||
assertEquals(IntroducerProtocolState.ERROR.getValue(),
|
|
||||||
state.getLong(STATE).intValue());
|
|
||||||
|
|
||||||
// sync abort messages to introducees
|
// sync abort messages to introducees
|
||||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
deliverMessage(sync0, contactId0, sync1, contactId1, 2, "0 to 1");
|
||||||
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
// although aborted, introducee1 keeps the contact,
|
if (earlyAbort) {
|
||||||
// so introducer can not make contacts disappear by sending abort
|
assertTrue(listener1.aborted);
|
||||||
Collection<Contact> contacts1;
|
assertTrue(listener2.aborted);
|
||||||
DatabaseComponent db1 = t1.getDatabaseComponent();
|
} else {
|
||||||
txn = db1.startTransaction(true);
|
assertTrue(listener2.aborted);
|
||||||
try {
|
// when aborted late, introducee1 keeps the contact,
|
||||||
contacts1 = db1.getContacts(txn);
|
// so introducer can not make contacts disappear by aborting
|
||||||
txn.setComplete();
|
Collection<Contact> contacts1;
|
||||||
} finally {
|
DatabaseComponent db1 = t1.getDatabaseComponent();
|
||||||
db1.endTransaction(txn);
|
txn = db1.startTransaction(true);
|
||||||
|
try {
|
||||||
|
contacts1 = db1.getContacts(txn);
|
||||||
|
txn.setComplete();
|
||||||
|
} finally {
|
||||||
|
db1.endTransaction(txn);
|
||||||
|
}
|
||||||
|
assertEquals(2, contacts1.size());
|
||||||
}
|
}
|
||||||
assertEquals(2, contacts1.size());
|
|
||||||
} finally {
|
} finally {
|
||||||
stopLifecycles();
|
stopLifecycles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifiedTransportProperties() throws Exception {
|
||||||
|
testModifiedResponse(new StateVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(BdfDictionary response) {
|
||||||
|
BdfDictionary tp = response.getDictionary(TRANSPORT, null);
|
||||||
|
tp.put("fakeId",
|
||||||
|
BdfDictionary.of(new BdfEntry("fake", "fake")));
|
||||||
|
response.put(TRANSPORT, tp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifiedTimestamp() throws Exception {
|
||||||
|
testModifiedResponse(new StateVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(BdfDictionary response) {
|
||||||
|
long timestamp = response.getLong(TIME, 0L);
|
||||||
|
response.put(TIME, timestamp + 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifiedEphemeralPublicKey() throws Exception {
|
||||||
|
testModifiedResponse(new StateVisitor() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(BdfDictionary response) {
|
||||||
|
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||||
|
response.put(E_PUBLIC_KEY, keyPair.getPublic().getEncoded());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModifiedEphemeralPublicKeyWithFakeMac()
|
||||||
|
throws Exception {
|
||||||
|
// initialize a real introducee manager
|
||||||
|
MessageSender messageSender = t2.getMessageSender();
|
||||||
|
DatabaseComponent db = t2.getDatabaseComponent();
|
||||||
|
ClientHelper clientHelper = t2.getClientHelper();
|
||||||
|
TransportPropertyManager tpManager = t2.getTransportPropertyManager();
|
||||||
|
ContactManager contactManager = t2.getContactManager();
|
||||||
|
IdentityManager identityManager = t2.getIdentityManager();
|
||||||
|
IntroduceeManager manager2 =
|
||||||
|
new IntroduceeManager(messageSender, db, clientHelper, clock,
|
||||||
|
crypto, tpManager, authorFactory, contactManager,
|
||||||
|
identityManager, introductionGroupFactory);
|
||||||
|
|
||||||
|
// create keys
|
||||||
|
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
|
||||||
|
KeyPair eKeyPair1 = crypto.generateAgreementKeyPair();
|
||||||
|
byte[] ePublicKeyBytes1 = eKeyPair1.getPublic().getEncoded();
|
||||||
|
KeyPair eKeyPair2 = crypto.generateAgreementKeyPair();
|
||||||
|
byte[] ePublicKeyBytes2 = eKeyPair2.getPublic().getEncoded();
|
||||||
|
|
||||||
|
// Nonce 1
|
||||||
|
SecretKey secretKey =
|
||||||
|
crypto.deriveMasterSecret(ePublicKeyBytes2, eKeyPair1, true);
|
||||||
|
byte[] nonce1 = crypto.deriveSignatureNonce(secretKey, true);
|
||||||
|
|
||||||
|
// Signature 1
|
||||||
|
Signature signature = crypto.getSignature();
|
||||||
|
signature.initSign(keyPair1.getPrivate());
|
||||||
|
signature.update(nonce1);
|
||||||
|
byte[] sig1 = signature.sign();
|
||||||
|
|
||||||
|
// MAC 1
|
||||||
|
SecretKey macKey1 = crypto.deriveMacKey(secretKey, true);
|
||||||
|
BdfDictionary tp1 = BdfDictionary.of(new BdfEntry("fake", "fake"));
|
||||||
|
long time1 = clock.currentTimeMillis();
|
||||||
|
BdfList toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
|
||||||
|
ePublicKeyBytes1, tp1, time1);
|
||||||
|
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||||
|
byte[] mac1 = crypto.mac(macKey1, toMac);
|
||||||
|
|
||||||
|
// create only relevant part of state for introducee2
|
||||||
|
BdfDictionary state = new BdfDictionary();
|
||||||
|
state.put(PUBLIC_KEY, keyPair1.getPublic().getEncoded());
|
||||||
|
state.put(TRANSPORT, tp1);
|
||||||
|
state.put(TIME, time1);
|
||||||
|
state.put(E_PUBLIC_KEY, ePublicKeyBytes1);
|
||||||
|
state.put(MAC, mac1);
|
||||||
|
state.put(MAC_KEY, macKey1.getBytes());
|
||||||
|
state.put(NONCE, nonce1);
|
||||||
|
state.put(SIGNATURE, sig1);
|
||||||
|
|
||||||
|
// MAC and signature verification should pass
|
||||||
|
manager2.verifyMac(state);
|
||||||
|
manager2.verifySignature(state);
|
||||||
|
|
||||||
|
// replace ephemeral key pair and recalculate matching keys and nonce
|
||||||
|
KeyPair eKeyPair1f = crypto.generateAgreementKeyPair();
|
||||||
|
byte[] ePublicKeyBytes1f = eKeyPair1f.getPublic().getEncoded();
|
||||||
|
secretKey =
|
||||||
|
crypto.deriveMasterSecret(ePublicKeyBytes2, eKeyPair1f, true);
|
||||||
|
nonce1 = crypto.deriveSignatureNonce(secretKey, true);
|
||||||
|
|
||||||
|
// recalculate MAC
|
||||||
|
macKey1 = crypto.deriveMacKey(secretKey, true);
|
||||||
|
toMacList = BdfList.of(keyPair1.getPublic().getEncoded(),
|
||||||
|
ePublicKeyBytes1f, tp1, time1);
|
||||||
|
toMac = clientHelper.toByteArray(toMacList);
|
||||||
|
mac1 = crypto.mac(macKey1, toMac);
|
||||||
|
|
||||||
|
// update state with faked information
|
||||||
|
state.put(E_PUBLIC_KEY, ePublicKeyBytes1f);
|
||||||
|
state.put(MAC, mac1);
|
||||||
|
state.put(MAC_KEY, macKey1.getBytes());
|
||||||
|
state.put(NONCE, nonce1);
|
||||||
|
|
||||||
|
// MAC verification should still pass
|
||||||
|
manager2.verifyMac(state);
|
||||||
|
|
||||||
|
// Signature can not be verified, because we don't have private
|
||||||
|
// long-term key to fake it
|
||||||
|
try {
|
||||||
|
manager2.verifySignature(state);
|
||||||
|
fail();
|
||||||
|
} catch(GeneralSecurityException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws InterruptedException {
|
public void tearDown() throws InterruptedException {
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
@@ -990,16 +1043,33 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
lifecycleManager2.waitForShutdown();
|
lifecycleManager2.waitForShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTransportProperties() throws DbException {
|
private void addTransportProperties()
|
||||||
|
throws DbException, IOException, TimeoutException {
|
||||||
TransportPropertyManager tpm0 = t0.getTransportPropertyManager();
|
TransportPropertyManager tpm0 = t0.getTransportPropertyManager();
|
||||||
TransportPropertyManager tpm1 = t1.getTransportPropertyManager();
|
TransportPropertyManager tpm1 = t1.getTransportPropertyManager();
|
||||||
TransportPropertyManager tpm2 = t2.getTransportPropertyManager();
|
TransportPropertyManager tpm2 = t2.getTransportPropertyManager();
|
||||||
|
|
||||||
TransportProperties tp = new TransportProperties(
|
TransportProperties tp = new TransportProperties(
|
||||||
Collections.singletonMap("key", "value"));
|
Collections.singletonMap("key", "value"));
|
||||||
|
|
||||||
tpm0.mergeLocalProperties(TRANSPORT_ID, tp);
|
tpm0.mergeLocalProperties(TRANSPORT_ID, tp);
|
||||||
|
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||||
|
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
|
||||||
|
|
||||||
tpm1.mergeLocalProperties(TRANSPORT_ID, tp);
|
tpm1.mergeLocalProperties(TRANSPORT_ID, tp);
|
||||||
|
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||||
|
|
||||||
tpm2.mergeLocalProperties(TRANSPORT_ID, tp);
|
tpm2.mergeLocalProperties(TRANSPORT_ID, tp);
|
||||||
|
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners(boolean accept1, boolean accept2) {
|
||||||
|
// listen to events
|
||||||
|
listener0 = new IntroducerListener();
|
||||||
|
t0.getEventBus().addListener(listener0);
|
||||||
|
listener1 = new IntroduceeListener(1, accept1);
|
||||||
|
t1.getEventBus().addListener(listener1);
|
||||||
|
listener2 = new IntroduceeListener(2, accept2);
|
||||||
|
t2.getEventBus().addListener(listener2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDefaultIdentities() throws DbException {
|
private void addDefaultIdentities() throws DbException {
|
||||||
@@ -1046,13 +1116,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||||
SyncSessionFactory toSync, ContactId toId)
|
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||||
throws IOException, TimeoutException {
|
throws IOException, TimeoutException {
|
||||||
deliverMessage(fromSync, fromId, toSync, toId, null);
|
deliverMessage(fromSync, fromId, toSync, toId, 1, debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||||
SyncSessionFactory toSync, ContactId toId, @Nullable String debug)
|
SyncSessionFactory toSync, ContactId toId)
|
||||||
|
throws IOException, TimeoutException {
|
||||||
|
deliverMessage(fromSync, fromId, toSync, toId, 1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||||
|
SyncSessionFactory toSync, ContactId toId, int num,
|
||||||
|
@Nullable String debug)
|
||||||
throws IOException, TimeoutException {
|
throws IOException, TimeoutException {
|
||||||
|
|
||||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||||
@@ -1072,8 +1149,8 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
sessionTo.run();
|
sessionTo.run();
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
// wait for message to actually arrive
|
// wait for [num] message(s) to actually arrive
|
||||||
msgWaiter.await(TIMEOUT, 1);
|
msgWaiter.await(TIMEOUT, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDefaultUiMessages() throws DbException {
|
private void assertDefaultUiMessages() throws DbException {
|
||||||
@@ -1176,15 +1253,12 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||||
ContactId c =
|
ContactId c =
|
||||||
((IntroductionResponseReceivedEvent) e).getContactId();
|
((IntroductionResponseReceivedEvent) e).getContactId();
|
||||||
try {
|
if (c.equals(contactId1)) {
|
||||||
if (c.equals(contactId1)) {
|
response1Received = true;
|
||||||
response1Received = true;
|
} else if (c.equals(contactId2)) {
|
||||||
} else if (c.equals(contactId2)) {
|
response2Received = true;
|
||||||
response2Received = true;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
eventWaiter.resume();
|
|
||||||
}
|
}
|
||||||
|
eventWaiter.resume();
|
||||||
} else if (e instanceof IntroductionAbortedEvent) {
|
} else if (e instanceof IntroductionAbortedEvent) {
|
||||||
aborted = true;
|
aborted = true;
|
||||||
eventWaiter.resume();
|
eventWaiter.resume();
|
||||||
@@ -1202,6 +1276,23 @@ public class IntroductionIntegrationTest extends BriarTestCase {
|
|||||||
clientHelper.mergeGroupMetadata(g, gD);
|
clientHelper.mergeGroupMetadata(g, gD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Entry<MessageId, BdfDictionary> getMessageFor(Contact contact,
|
||||||
|
long type) throws FormatException, DbException {
|
||||||
|
Entry<MessageId, BdfDictionary> response = null;
|
||||||
|
Group g = introductionGroupFactory
|
||||||
|
.createIntroductionGroup(contact);
|
||||||
|
ClientHelper clientHelper0 = t0.getClientHelper();
|
||||||
|
Map<MessageId, BdfDictionary> map =
|
||||||
|
clientHelper0.getMessageMetadataAsDictionary(g.getId());
|
||||||
|
for (Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
||||||
|
if (entry.getValue().getLong(TYPE) == type) {
|
||||||
|
response = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(response != null);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
private void injectEagerSingletons(
|
private void injectEagerSingletons(
|
||||||
IntroductionIntegrationTestComponent component) {
|
IntroductionIntegrationTestComponent component) {
|
||||||
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.briarproject;
|
package org.briarproject.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.TestDatabaseModule;
|
||||||
|
import org.briarproject.TestPluginsModule;
|
||||||
|
import org.briarproject.TestSeedProviderModule;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
import org.briarproject.api.contact.ContactManager;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
@@ -16,9 +19,6 @@ import org.briarproject.data.DataModule;
|
|||||||
import org.briarproject.db.DatabaseModule;
|
import org.briarproject.db.DatabaseModule;
|
||||||
import org.briarproject.event.EventModule;
|
import org.briarproject.event.EventModule;
|
||||||
import org.briarproject.identity.IdentityModule;
|
import org.briarproject.identity.IdentityModule;
|
||||||
import org.briarproject.introduction.IntroductionGroupFactory;
|
|
||||||
import org.briarproject.introduction.IntroductionModule;
|
|
||||||
import org.briarproject.introduction.MessageSender;
|
|
||||||
import org.briarproject.lifecycle.LifecycleModule;
|
import org.briarproject.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.properties.PropertiesModule;
|
import org.briarproject.properties.PropertiesModule;
|
||||||
import org.briarproject.sync.SyncModule;
|
import org.briarproject.sync.SyncModule;
|
||||||
@@ -288,93 +288,40 @@ class IntroduceeManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("Adding contact in inactive state");
|
|
||||||
|
|
||||||
// get all keys
|
|
||||||
KeyParser keyParser = cryptoComponent.getAgreementKeyParser();
|
|
||||||
byte[] publicKeyBytes;
|
|
||||||
PublicKey publicKey;
|
|
||||||
PrivateKey privateKey;
|
|
||||||
try {
|
|
||||||
publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
|
||||||
publicKey = keyParser
|
|
||||||
.parsePublicKey(publicKeyBytes);
|
|
||||||
privateKey = keyParser.parsePrivateKey(
|
|
||||||
localState.getRaw(OUR_PRIVATE_KEY));
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) {
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
// we can not continue without the keys
|
|
||||||
throw new RuntimeException("Our own ephemeral key is invalid");
|
|
||||||
}
|
|
||||||
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
|
||||||
byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY);
|
|
||||||
|
|
||||||
// figure out who takes which role by comparing public keys
|
// figure out who takes which role by comparing public keys
|
||||||
|
byte[] publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||||
|
byte[] theirEphemeralKey = localState.getRaw(E_PUBLIC_KEY);
|
||||||
int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes),
|
int comp = Bytes.COMPARATOR.compare(new Bytes(publicKeyBytes),
|
||||||
new Bytes(theirEphemeralKey));
|
new Bytes(theirEphemeralKey));
|
||||||
boolean alice = comp < 0;
|
boolean alice = comp < 0;
|
||||||
|
|
||||||
// The master secret is derived from the local ephemeral key pair
|
// get our local author
|
||||||
// and the remote ephemeral public key
|
|
||||||
SecretKey secretKey;
|
|
||||||
try {
|
|
||||||
secretKey = cryptoComponent
|
|
||||||
.deriveMasterSecret(theirEphemeralKey, keyPair, alice);
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
// we can not continue without the shared secret
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive two nonces and a MAC key from the secret master key
|
|
||||||
byte[] ourNonce =
|
|
||||||
cryptoComponent.deriveSignatureNonce(secretKey, alice);
|
|
||||||
byte[] theirNonce =
|
|
||||||
cryptoComponent.deriveSignatureNonce(secretKey, !alice);
|
|
||||||
SecretKey macKey = cryptoComponent.deriveMacKey(secretKey, alice);
|
|
||||||
SecretKey theirMacKey =
|
|
||||||
cryptoComponent.deriveMacKey(secretKey, !alice);
|
|
||||||
|
|
||||||
// Save the other nonce and MAC key for the verification
|
|
||||||
localState.put(NONCE, theirNonce);
|
|
||||||
localState.put(MAC_KEY, theirMacKey.getBytes());
|
|
||||||
|
|
||||||
// Sign our nonce with our long-term identity public key
|
|
||||||
AuthorId localAuthorId =
|
AuthorId localAuthorId =
|
||||||
new AuthorId(localState.getRaw(LOCAL_AUTHOR_ID));
|
new AuthorId(localState.getRaw(LOCAL_AUTHOR_ID));
|
||||||
LocalAuthor author =
|
LocalAuthor author =
|
||||||
identityManager.getLocalAuthor(txn, localAuthorId);
|
identityManager.getLocalAuthor(txn, localAuthorId);
|
||||||
Signature signature = cryptoComponent.getSignature();
|
|
||||||
KeyParser sigParser = cryptoComponent.getSignatureKeyParser();
|
SecretKey secretKey;
|
||||||
|
byte[] privateKeyBytes = localState.getRaw(OUR_PRIVATE_KEY);
|
||||||
try {
|
try {
|
||||||
PrivateKey privKey =
|
// derive secret master key
|
||||||
sigParser.parsePrivateKey(author.getPrivateKey());
|
secretKey =
|
||||||
signature.initSign(privKey);
|
deriveSecretKey(publicKeyBytes, privateKeyBytes, alice,
|
||||||
|
theirEphemeralKey);
|
||||||
|
// derive MAC keys and nonces, sign our nonce and calculate MAC
|
||||||
|
deriveMacKeysAndNonces(localState, author, secretKey, alice);
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
// we can not continue without the signature
|
// we can not continue without the signature
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
signature.update(ourNonce);
|
|
||||||
byte[] sig = signature.sign();
|
LOG.info("Adding contact in inactive state");
|
||||||
|
|
||||||
// The agreed timestamp is the minimum of the peers' timestamps
|
// The agreed timestamp is the minimum of the peers' timestamps
|
||||||
long ourTime = localState.getLong(OUR_TIME);
|
long ourTime = localState.getLong(OUR_TIME);
|
||||||
long theirTime = localState.getLong(TIME);
|
long theirTime = localState.getLong(TIME);
|
||||||
long timestamp = Math.min(ourTime, theirTime);
|
long timestamp = Math.min(ourTime, theirTime);
|
||||||
|
|
||||||
// Calculate a MAC over identity public key, ephemeral public key,
|
|
||||||
// transport properties and timestamp.
|
|
||||||
BdfDictionary tp = localState.getDictionary(OUR_TRANSPORT);
|
|
||||||
BdfList toSignList = BdfList.of(author.getPublicKey(),
|
|
||||||
publicKeyBytes, tp, ourTime);
|
|
||||||
byte[] toSign = clientHelper.toByteArray(toSignList);
|
|
||||||
byte[] mac = cryptoComponent.mac(macKey, toSign);
|
|
||||||
|
|
||||||
// Add MAC and signature to localState, so it can be included in ACK
|
|
||||||
localState.put(OUR_MAC, mac);
|
|
||||||
localState.put(OUR_SIGNATURE, sig);
|
|
||||||
|
|
||||||
// Add the contact to the database as inactive
|
// Add the contact to the database as inactive
|
||||||
Author remoteAuthor = authorFactory
|
Author remoteAuthor = authorFactory
|
||||||
.createAuthor(localState.getString(NAME),
|
.createAuthor(localState.getString(NAME),
|
||||||
@@ -411,51 +358,15 @@ class IntroduceeManager {
|
|||||||
if (task == TASK_ACTIVATE_CONTACT) {
|
if (task == TASK_ACTIVATE_CONTACT) {
|
||||||
if (!localState.getBoolean(EXISTS) &&
|
if (!localState.getBoolean(EXISTS) &&
|
||||||
localState.containsKey(ADDED_CONTACT_ID)) {
|
localState.containsKey(ADDED_CONTACT_ID)) {
|
||||||
|
|
||||||
LOG.info("Verifying Signature...");
|
|
||||||
|
|
||||||
byte[] nonce = localState.getRaw(NONCE);
|
|
||||||
byte[] sig = localState.getRaw(SIGNATURE);
|
|
||||||
byte[] keyBytes = localState.getRaw(PUBLIC_KEY);
|
|
||||||
try {
|
try {
|
||||||
// Parse the public key
|
LOG.info("Verifying Signature...");
|
||||||
KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
|
verifySignature(localState);
|
||||||
PublicKey key = keyParser.parsePublicKey(keyBytes);
|
LOG.info("Verifying MAC...");
|
||||||
// Verify the signature
|
verifyMac(localState);
|
||||||
Signature signature = cryptoComponent.getSignature();
|
|
||||||
signature.initVerify(key);
|
|
||||||
signature.update(nonce);
|
|
||||||
if (!signature.verify(sig)) {
|
|
||||||
LOG.warning("Invalid nonce signature in ACK");
|
|
||||||
throw new GeneralSecurityException();
|
|
||||||
}
|
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
// we can not continue without verifying the signature
|
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("Verifying MAC...");
|
|
||||||
|
|
||||||
// get MAC and MAC key from session state
|
|
||||||
byte[] mac = localState.getRaw(MAC);
|
|
||||||
byte[] macKeyBytes = localState.getRaw(MAC_KEY);
|
|
||||||
SecretKey macKey = new SecretKey(macKeyBytes);
|
|
||||||
|
|
||||||
// get MAC data and calculate a new MAC with stored key
|
|
||||||
byte[] pubKey = localState.getRaw(PUBLIC_KEY);
|
|
||||||
byte[] ePubKey = localState.getRaw(E_PUBLIC_KEY);
|
|
||||||
BdfDictionary tp = localState.getDictionary(TRANSPORT);
|
|
||||||
long timestamp = localState.getLong(TIME);
|
|
||||||
BdfList toSignList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
|
||||||
byte[] toSign = clientHelper.toByteArray(toSignList);
|
|
||||||
byte[] calculatedMac = cryptoComponent.mac(macKey, toSign);
|
|
||||||
if (!Arrays.equals(mac, calculatedMac)) {
|
|
||||||
LOG.warning("Received ACK with invalid MAC");
|
|
||||||
throw new DbException();
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.info("Activating Contact...");
|
LOG.info("Activating Contact...");
|
||||||
|
|
||||||
ContactId contactId = new ContactId(
|
ContactId contactId = new ContactId(
|
||||||
@@ -483,11 +394,122 @@ class IntroduceeManager {
|
|||||||
contactManager.removeContact(txn, contactId);
|
contactManager.removeContact(txn, contactId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey deriveSecretKey(byte[] publicKeyBytes,
|
||||||
|
byte[] privateKeyBytes, boolean alice, byte[] theirPublicKey)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
// parse the local ephemeral key pair
|
||||||
|
KeyParser keyParser = cryptoComponent.getAgreementKeyParser();
|
||||||
|
PublicKey publicKey;
|
||||||
|
PrivateKey privateKey;
|
||||||
|
try {
|
||||||
|
publicKey = keyParser.parsePublicKey(publicKeyBytes);
|
||||||
|
privateKey = keyParser.parsePrivateKey(privateKeyBytes);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) {
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Our own ephemeral key is invalid");
|
||||||
|
}
|
||||||
|
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||||
|
|
||||||
|
// The master secret is derived from the local ephemeral key pair
|
||||||
|
// and the remote ephemeral public key
|
||||||
|
return cryptoComponent
|
||||||
|
.deriveMasterSecret(theirPublicKey, keyPair, alice);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives nonces, signs our nonce and calculates MAC
|
||||||
|
*
|
||||||
|
* Derives two nonces and two mac keys from the secret master key.
|
||||||
|
* The other introducee's nonce and MAC key are added to the localState.
|
||||||
|
*
|
||||||
|
* Our nonce is signed with the local author's long-term private key.
|
||||||
|
* The signature is added to the localState.
|
||||||
|
*
|
||||||
|
* Calculates a MAC and stores it in the localState.
|
||||||
|
*/
|
||||||
|
private void deriveMacKeysAndNonces(BdfDictionary localState,
|
||||||
|
LocalAuthor author, SecretKey secretKey, boolean alice)
|
||||||
|
throws FormatException, GeneralSecurityException {
|
||||||
|
// Derive two nonces and a MAC key from the secret master key
|
||||||
|
byte[] ourNonce =
|
||||||
|
cryptoComponent.deriveSignatureNonce(secretKey, alice);
|
||||||
|
byte[] theirNonce =
|
||||||
|
cryptoComponent.deriveSignatureNonce(secretKey, !alice);
|
||||||
|
SecretKey macKey = cryptoComponent.deriveMacKey(secretKey, alice);
|
||||||
|
SecretKey theirMacKey = cryptoComponent.deriveMacKey(secretKey, !alice);
|
||||||
|
|
||||||
|
// Save the other nonce and MAC key for the verification
|
||||||
|
localState.put(NONCE, theirNonce);
|
||||||
|
localState.put(MAC_KEY, theirMacKey.getBytes());
|
||||||
|
|
||||||
|
// Sign our nonce with our long-term identity public key
|
||||||
|
Signature signature = cryptoComponent.getSignature();
|
||||||
|
KeyParser sigParser = cryptoComponent.getSignatureKeyParser();
|
||||||
|
PrivateKey privKey = sigParser.parsePrivateKey(author.getPrivateKey());
|
||||||
|
signature.initSign(privKey);
|
||||||
|
signature.update(ourNonce);
|
||||||
|
byte[] sig = signature.sign();
|
||||||
|
|
||||||
|
// Calculate a MAC over identity public key, ephemeral public key,
|
||||||
|
// transport properties and timestamp.
|
||||||
|
byte[] publicKeyBytes = localState.getRaw(OUR_PUBLIC_KEY);
|
||||||
|
BdfDictionary tp = localState.getDictionary(OUR_TRANSPORT);
|
||||||
|
long ourTime = localState.getLong(OUR_TIME);
|
||||||
|
BdfList toMacList = BdfList.of(author.getPublicKey(),
|
||||||
|
publicKeyBytes, tp, ourTime);
|
||||||
|
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||||
|
byte[] mac = cryptoComponent.mac(macKey, toMac);
|
||||||
|
|
||||||
|
// Add MAC and signature to localState, so it can be included in ACK
|
||||||
|
localState.put(OUR_MAC, mac);
|
||||||
|
localState.put(OUR_SIGNATURE, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifySignature(BdfDictionary localState)
|
||||||
|
throws FormatException, GeneralSecurityException {
|
||||||
|
byte[] nonce = localState.getRaw(NONCE);
|
||||||
|
byte[] sig = localState.getRaw(SIGNATURE);
|
||||||
|
byte[] keyBytes = localState.getRaw(PUBLIC_KEY);
|
||||||
|
|
||||||
|
// Parse the public key
|
||||||
|
KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
|
||||||
|
PublicKey key = keyParser.parsePublicKey(keyBytes);
|
||||||
|
// Verify the signature
|
||||||
|
Signature signature = cryptoComponent.getSignature();
|
||||||
|
signature.initVerify(key);
|
||||||
|
signature.update(nonce);
|
||||||
|
if (!signature.verify(sig)) {
|
||||||
|
LOG.warning("Invalid nonce signature in ACK");
|
||||||
|
throw new GeneralSecurityException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyMac(BdfDictionary localState)
|
||||||
|
throws FormatException, GeneralSecurityException {
|
||||||
|
// get MAC and MAC key from session state
|
||||||
|
byte[] mac = localState.getRaw(MAC);
|
||||||
|
byte[] macKeyBytes = localState.getRaw(MAC_KEY);
|
||||||
|
SecretKey macKey = new SecretKey(macKeyBytes);
|
||||||
|
|
||||||
|
// get MAC data and calculate a new MAC with stored key
|
||||||
|
byte[] pubKey = localState.getRaw(PUBLIC_KEY);
|
||||||
|
byte[] ePubKey = localState.getRaw(E_PUBLIC_KEY);
|
||||||
|
BdfDictionary tp = localState.getDictionary(TRANSPORT);
|
||||||
|
long timestamp = localState.getLong(TIME);
|
||||||
|
BdfList toMacList = BdfList.of(pubKey, ePubKey, tp, timestamp);
|
||||||
|
byte[] toMac = clientHelper.toByteArray(toMacList);
|
||||||
|
byte[] calculatedMac = cryptoComponent.mac(macKey, toMac);
|
||||||
|
if (!Arrays.equals(mac, calculatedMac)) {
|
||||||
|
LOG.warning("Received ACK with invalid MAC");
|
||||||
|
throw new GeneralSecurityException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void abort(Transaction txn, BdfDictionary state) {
|
public void abort(Transaction txn, BdfDictionary state) {
|
||||||
|
|
||||||
IntroduceeEngine engine = new IntroduceeEngine();
|
IntroduceeEngine engine = new IntroduceeEngine();
|
||||||
BdfDictionary localAction = new BdfDictionary();
|
BdfDictionary localAction = new BdfDictionary();
|
||||||
localAction.put(TYPE, TYPE_ABORT);
|
localAction.put(TYPE, TYPE_ABORT);
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import org.briarproject.api.contact.Contact;
|
|||||||
import org.briarproject.api.contact.ContactId;
|
import org.briarproject.api.contact.ContactId;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
import org.briarproject.api.contact.ContactManager;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.api.crypto.Signature;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfEntry;
|
import org.briarproject.api.data.BdfEntry;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
@@ -33,11 +37,14 @@ import org.jmock.Mockery;
|
|||||||
import org.jmock.lib.legacy.ClassImposteriser;
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||||
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.ADDED_CONTACT_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1;
|
||||||
@@ -46,9 +53,13 @@ import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_K
|
|||||||
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER;
|
import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MAC;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MAC_KEY;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
import static org.briarproject.api.introduction.IntroductionConstants.NAME;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.NONCE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
|
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
|
||||||
@@ -56,15 +67,21 @@ import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUT
|
|||||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.SIGNATURE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
import static org.briarproject.api.introduction.IntroductionConstants.STATE;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
import static org.briarproject.api.introduction.IntroductionConstants.TIME;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
|
||||||
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
|
||||||
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
|
||||||
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||||
|
import static org.hamcrest.Matchers.array;
|
||||||
|
import static org.hamcrest.Matchers.samePropertyValuesAs;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class IntroduceeManagerTest extends BriarTestCase {
|
public class IntroduceeManagerTest extends BriarTestCase {
|
||||||
|
|
||||||
@@ -221,6 +238,152 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
assertFalse(txn.isComplete());
|
assertFalse(txn.isComplete());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDetectReplacedEphemeralPublicKey()
|
||||||
|
throws DbException, FormatException, GeneralSecurityException {
|
||||||
|
|
||||||
|
// TODO MR !237 should use its new default initialization method here
|
||||||
|
final BdfDictionary msg = new BdfDictionary();
|
||||||
|
msg.put(TYPE, TYPE_RESPONSE);
|
||||||
|
msg.put(GROUP_ID, introductionGroup1.getId());
|
||||||
|
msg.put(SESSION_ID, sessionId);
|
||||||
|
msg.put(MESSAGE_ID, message1.getId());
|
||||||
|
msg.put(MESSAGE_TIME, time);
|
||||||
|
msg.put(NAME, introducee2.getAuthor().getName());
|
||||||
|
msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||||
|
final BdfDictionary state =
|
||||||
|
initializeSessionState(txn, introductionGroup1.getId(), msg);
|
||||||
|
|
||||||
|
// prepare state for incoming ACK
|
||||||
|
state.put(STATE, IntroduceeProtocolState.AWAIT_ACK.ordinal());
|
||||||
|
state.put(ADDED_CONTACT_ID, 2);
|
||||||
|
final byte[] nonce = TestUtils.getRandomBytes(42);
|
||||||
|
state.put(NONCE, nonce);
|
||||||
|
state.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey());
|
||||||
|
|
||||||
|
// create incoming ACK message
|
||||||
|
final byte[] mac = TestUtils.getRandomBytes(MAC_LENGTH);
|
||||||
|
final byte[] sig = TestUtils.getRandomBytes(MAX_SIGNATURE_LENGTH);
|
||||||
|
BdfDictionary ack = BdfDictionary.of(
|
||||||
|
new BdfEntry(TYPE, TYPE_ACK),
|
||||||
|
new BdfEntry(SESSION_ID, sessionId),
|
||||||
|
new BdfEntry(GROUP_ID, introductionGroup1.getId()),
|
||||||
|
new BdfEntry(MAC, mac),
|
||||||
|
new BdfEntry(SIGNATURE, sig)
|
||||||
|
);
|
||||||
|
|
||||||
|
final KeyParser keyParser = context.mock(KeyParser.class);
|
||||||
|
final PublicKey publicKey = context.mock(PublicKey.class);
|
||||||
|
final Signature signature = context.mock(Signature.class);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(cryptoComponent).getSignatureKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
oneOf(keyParser)
|
||||||
|
.parsePublicKey(introducee2.getAuthor().getPublicKey());
|
||||||
|
will(returnValue(publicKey));
|
||||||
|
oneOf(cryptoComponent).getSignature();
|
||||||
|
will(returnValue(signature));
|
||||||
|
oneOf(signature).initVerify(publicKey);
|
||||||
|
oneOf(signature).update(nonce);
|
||||||
|
oneOf(signature).verify(sig);
|
||||||
|
will(returnValue(false));
|
||||||
|
}});
|
||||||
|
|
||||||
|
try {
|
||||||
|
introduceeManager.incomingMessage(txn, state, ack);
|
||||||
|
fail();
|
||||||
|
} catch (DbException e) {
|
||||||
|
// expected
|
||||||
|
assertTrue(e.getCause() instanceof GeneralSecurityException);
|
||||||
|
}
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
assertFalse(txn.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignatureVerification()
|
||||||
|
throws FormatException, DbException, GeneralSecurityException {
|
||||||
|
|
||||||
|
final byte[] publicKeyBytes = introducee2.getAuthor().getPublicKey();
|
||||||
|
final byte[] nonce = TestUtils.getRandomBytes(MAC_LENGTH);
|
||||||
|
final byte[] sig = TestUtils.getRandomBytes(MAC_LENGTH);
|
||||||
|
|
||||||
|
BdfDictionary state = new BdfDictionary();
|
||||||
|
state.put(PUBLIC_KEY, publicKeyBytes);
|
||||||
|
state.put(NONCE, nonce);
|
||||||
|
state.put(SIGNATURE, sig);
|
||||||
|
|
||||||
|
final KeyParser keyParser = context.mock(KeyParser.class);
|
||||||
|
final Signature signature = context.mock(Signature.class);
|
||||||
|
final PublicKey publicKey = context.mock(PublicKey.class);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(cryptoComponent).getSignatureKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
oneOf(keyParser).parsePublicKey(publicKeyBytes);
|
||||||
|
will(returnValue(publicKey));
|
||||||
|
oneOf(cryptoComponent).getSignature();
|
||||||
|
will(returnValue(signature));
|
||||||
|
oneOf(signature).initVerify(publicKey);
|
||||||
|
oneOf(signature).update(nonce);
|
||||||
|
oneOf(signature).verify(sig);
|
||||||
|
will(returnValue(true));
|
||||||
|
}});
|
||||||
|
introduceeManager.verifySignature(state);
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMacVerification()
|
||||||
|
throws FormatException, DbException, GeneralSecurityException {
|
||||||
|
|
||||||
|
final byte[] publicKeyBytes = introducee2.getAuthor().getPublicKey();
|
||||||
|
final BdfDictionary tp = BdfDictionary.of(new BdfEntry("fake", "fake"));
|
||||||
|
final byte[] ePublicKeyBytes =
|
||||||
|
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
final byte[] mac = TestUtils.getRandomBytes(MAC_LENGTH);
|
||||||
|
final SecretKey macKey = TestUtils.getSecretKey();
|
||||||
|
|
||||||
|
// move state to where it would be after an ACK arrived
|
||||||
|
BdfDictionary state = new BdfDictionary();
|
||||||
|
state.put(PUBLIC_KEY, publicKeyBytes);
|
||||||
|
state.put(TRANSPORT, tp);
|
||||||
|
state.put(TIME, time);
|
||||||
|
state.put(E_PUBLIC_KEY, ePublicKeyBytes);
|
||||||
|
state.put(MAC, mac);
|
||||||
|
state.put(MAC_KEY, macKey.getBytes());
|
||||||
|
|
||||||
|
final byte[] signBytes = TestUtils.getRandomBytes(42);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).toByteArray(
|
||||||
|
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
|
||||||
|
will(returnValue(signBytes));
|
||||||
|
//noinspection unchecked
|
||||||
|
oneOf(cryptoComponent).mac(with(samePropertyValuesAs(macKey)),
|
||||||
|
with(array(equal(signBytes))));
|
||||||
|
will(returnValue(mac));
|
||||||
|
}});
|
||||||
|
introduceeManager.verifyMac(state);
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// now produce wrong MAC
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).toByteArray(
|
||||||
|
BdfList.of(publicKeyBytes, ePublicKeyBytes, tp, time));
|
||||||
|
will(returnValue(signBytes));
|
||||||
|
//noinspection unchecked
|
||||||
|
oneOf(cryptoComponent).mac(with(samePropertyValuesAs(macKey)),
|
||||||
|
with(array(equal(signBytes))));
|
||||||
|
will(returnValue(TestUtils.getRandomBytes(MAC_LENGTH)));
|
||||||
|
}});
|
||||||
|
try {
|
||||||
|
introduceeManager.verifyMac(state);
|
||||||
|
fail();
|
||||||
|
} catch(GeneralSecurityException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
}
|
||||||
|
|
||||||
private BdfDictionary initializeSessionState(final Transaction txn,
|
private BdfDictionary initializeSessionState(final Transaction txn,
|
||||||
final GroupId groupId, final BdfDictionary msg)
|
final GroupId groupId, final BdfDictionary msg)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
@@ -230,7 +393,7 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
final BdfDictionary groupMetadata = BdfDictionary.of(
|
final BdfDictionary groupMetadata = BdfDictionary.of(
|
||||||
new BdfEntry(CONTACT, introducee1.getId().getInt())
|
new BdfEntry(CONTACT, introducee1.getId().getInt())
|
||||||
);
|
);
|
||||||
final boolean contactExists = true;
|
final boolean contactExists = false;
|
||||||
final BdfDictionary state = new BdfDictionary();
|
final BdfDictionary state = new BdfDictionary();
|
||||||
state.put(STORAGE_ID, localStateMessage.getId());
|
state.put(STORAGE_ID, localStateMessage.getId());
|
||||||
state.put(STATE, AWAIT_REQUEST.getValue());
|
state.put(STATE, AWAIT_REQUEST.getValue());
|
||||||
@@ -241,7 +404,7 @@ public class IntroduceeManagerTest extends BriarTestCase {
|
|||||||
state.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
|
state.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes());
|
||||||
state.put(NOT_OUR_RESPONSE, localStateMessage.getId());
|
state.put(NOT_OUR_RESPONSE, localStateMessage.getId());
|
||||||
state.put(ANSWERED, false);
|
state.put(ANSWERED, false);
|
||||||
state.put(EXISTS, true);
|
state.put(EXISTS, contactExists);
|
||||||
state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId());
|
state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId());
|
||||||
state.put(REMOTE_AUTHOR_IS_US, false);
|
state.put(REMOTE_AUTHOR_IS_US, false);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user