mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-22 15:49:53 +01:00
Messages and Validator for new Introduction Client
This commit is contained in:
@@ -16,4 +16,10 @@ public interface CryptoConstants {
|
|||||||
* The maximum length of a signature in bytes.
|
* The maximum length of a signature in bytes.
|
||||||
*/
|
*/
|
||||||
int MAX_SIGNATURE_BYTES = 64;
|
int MAX_SIGNATURE_BYTES = 64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of a MAC in bytes.
|
||||||
|
*/
|
||||||
|
int MAC_BYTES = SecretKey.LENGTH;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.sync.Group;
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
|
||||||
|
|
||||||
public abstract class ValidatorTestCase extends BrambleMockTestCase {
|
public abstract class ValidatorTestCase extends BrambleMockTestCase {
|
||||||
|
|
||||||
@@ -24,10 +27,23 @@ public abstract class ValidatorTestCase extends BrambleMockTestCase {
|
|||||||
protected final Group group = getGroup(getClientId());
|
protected final Group group = getGroup(getClientId());
|
||||||
protected final GroupId groupId = group.getId();
|
protected final GroupId groupId = group.getId();
|
||||||
protected final byte[] descriptor = group.getDescriptor();
|
protected final byte[] descriptor = group.getDescriptor();
|
||||||
protected final MessageId messageId = new MessageId(getRandomId());
|
protected final Message message = getMessage(groupId);
|
||||||
protected final long timestamp = 1234567890 * 1000L;
|
protected final MessageId messageId = message.getId();
|
||||||
protected final byte[] raw = getRandomBytes(123);
|
protected final long timestamp = message.getTimestamp();
|
||||||
protected final Message message =
|
protected final byte[] raw = message.getRaw();
|
||||||
new Message(messageId, groupId, timestamp, raw);
|
protected final Author author = getAuthor();
|
||||||
|
protected final BdfList authorList = BdfList.of(
|
||||||
|
author.getFormatVersion(),
|
||||||
|
author.getName(),
|
||||||
|
author.getPublicKey()
|
||||||
|
);
|
||||||
|
|
||||||
}
|
protected void expectParseAuthor(BdfList authorList, Author author)
|
||||||
|
throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).parseAndValidateAuthor(authorList);
|
||||||
|
will(returnValue(author));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.briarproject.briar.api.introduction2;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||||
|
|
||||||
|
public interface IntroductionConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum length of the introducer's optional message to the
|
||||||
|
* introducees in UTF-8 bytes.
|
||||||
|
*/
|
||||||
|
int MAX_REQUEST_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class AbortMessage extends IntroductionMessage {
|
||||||
|
|
||||||
|
private final SessionId sessionId;
|
||||||
|
|
||||||
|
protected AbortMessage(MessageId messageId, GroupId groupId, long timestamp,
|
||||||
|
@Nullable MessageId previousMessageId, SessionId sessionId) {
|
||||||
|
super(messageId, groupId, timestamp, previousMessageId);
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class AcceptMessage extends IntroductionMessage {
|
||||||
|
|
||||||
|
private final SessionId sessionId;
|
||||||
|
private final byte[] ephemeralPublicKey;
|
||||||
|
private final Map<TransportId, TransportProperties> transportProperties;
|
||||||
|
|
||||||
|
protected AcceptMessage(MessageId messageId, GroupId groupId,
|
||||||
|
long timestamp, @Nullable MessageId previousMessageId,
|
||||||
|
SessionId sessionId,
|
||||||
|
byte[] ephemeralPublicKey,
|
||||||
|
Map<TransportId, TransportProperties> transportProperties) {
|
||||||
|
super(messageId, groupId, timestamp, previousMessageId);
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.ephemeralPublicKey = ephemeralPublicKey;
|
||||||
|
this.transportProperties = transportProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEphemeralPublicKey() {
|
||||||
|
return ephemeralPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<TransportId, TransportProperties> getTransportProperties() {
|
||||||
|
return transportProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class ActivateMessage extends IntroductionMessage {
|
||||||
|
|
||||||
|
private final SessionId sessionId;
|
||||||
|
|
||||||
|
protected ActivateMessage(MessageId messageId, GroupId groupId,
|
||||||
|
long timestamp, MessageId previousMessageId, SessionId sessionId) {
|
||||||
|
super(messageId, groupId, timestamp, previousMessageId);
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class AuthMessage extends IntroductionMessage {
|
||||||
|
|
||||||
|
private final SessionId sessionId;
|
||||||
|
private final byte[] mac, signature;
|
||||||
|
|
||||||
|
protected AuthMessage(MessageId messageId, GroupId groupId,
|
||||||
|
long timestamp, MessageId previousMessageId, SessionId sessionId,
|
||||||
|
byte[] mac, byte[] signature) {
|
||||||
|
super(messageId, groupId, timestamp, previousMessageId);
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.mac = mac;
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMac() {
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class DeclineMessage extends IntroductionMessage {
|
||||||
|
|
||||||
|
private final SessionId sessionId;
|
||||||
|
|
||||||
|
protected DeclineMessage(MessageId messageId, GroupId groupId,
|
||||||
|
long timestamp, @Nullable MessageId previousMessageId,
|
||||||
|
SessionId sessionId) {
|
||||||
|
super(messageId, groupId, timestamp, previousMessageId);
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionId getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
abstract class IntroductionMessage {
|
||||||
|
|
||||||
|
private final MessageId messageId;
|
||||||
|
private final GroupId groupId;
|
||||||
|
private final long timestamp;
|
||||||
|
@Nullable
|
||||||
|
private final MessageId previousMessageId;
|
||||||
|
|
||||||
|
IntroductionMessage(MessageId messageId, GroupId groupId,
|
||||||
|
long timestamp, @Nullable MessageId previousMessageId) {
|
||||||
|
this.messageId = messageId;
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.previousMessageId = previousMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageId getMessageId() {
|
||||||
|
return messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupId getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
MessageId getPreviousMessageId() {
|
||||||
|
return previousMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.bramble.api.sync.ValidationManager;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class IntroductionModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
IntroductionValidator introductionValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
IntroductionValidator provideValidator(ValidationManager validationManager,
|
||||||
|
MessageEncoder messageEncoder, MetadataEncoder metadataEncoder,
|
||||||
|
ClientHelper clientHelper, Clock clock) {
|
||||||
|
|
||||||
|
IntroductionValidator introductionValidator =
|
||||||
|
new IntroductionValidator(messageEncoder, clientHelper,
|
||||||
|
metadataEncoder, clock);
|
||||||
|
validationManager.registerMessageValidator(CLIENT_ID,
|
||||||
|
introductionValidator);
|
||||||
|
|
||||||
|
return introductionValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
|
import org.briarproject.bramble.api.client.BdfMessageContext;
|
||||||
|
import org.briarproject.bramble.api.client.BdfMessageValidator;
|
||||||
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||||
|
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
||||||
|
import static org.briarproject.briar.api.introduction2.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.ACCEPT;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.AUTH;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.REQUEST;
|
||||||
|
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class IntroductionValidator extends BdfMessageValidator {
|
||||||
|
|
||||||
|
private final MessageEncoder messageEncoder;
|
||||||
|
|
||||||
|
IntroductionValidator(MessageEncoder messageEncoder,
|
||||||
|
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||||
|
Clock clock) {
|
||||||
|
super(clientHelper, metadataEncoder, clock);
|
||||||
|
this.messageEncoder = messageEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||||
|
BdfList body) throws FormatException {
|
||||||
|
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case REQUEST:
|
||||||
|
return validateRequestMessage(m, body);
|
||||||
|
case ACCEPT:
|
||||||
|
return validateAcceptMessage(m, body);
|
||||||
|
case AUTH:
|
||||||
|
return validateAuthMessage(m, body);
|
||||||
|
case DECLINE:
|
||||||
|
case ACTIVATE:
|
||||||
|
case ABORT:
|
||||||
|
return validateOtherMessage(type, m, body);
|
||||||
|
default:
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfMessageContext validateRequestMessage(Message m, BdfList body)
|
||||||
|
throws FormatException {
|
||||||
|
checkSize(body, 4);
|
||||||
|
|
||||||
|
byte[] previousMessageId = body.getOptionalRaw(1);
|
||||||
|
checkLength(previousMessageId, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
BdfList authorList = body.getList(2);
|
||||||
|
clientHelper.parseAndValidateAuthor(authorList);
|
||||||
|
|
||||||
|
String msg = body.getOptionalString(3);
|
||||||
|
checkLength(msg, 1, MAX_REQUEST_MESSAGE_LENGTH);
|
||||||
|
|
||||||
|
BdfDictionary meta = messageEncoder
|
||||||
|
.encodeRequestMetadata(REQUEST, m.getTimestamp(), false,
|
||||||
|
false, false, false, false);
|
||||||
|
if (previousMessageId == null) {
|
||||||
|
return new BdfMessageContext(meta);
|
||||||
|
} else {
|
||||||
|
MessageId dependency = new MessageId(previousMessageId);
|
||||||
|
return new BdfMessageContext(meta,
|
||||||
|
Collections.singletonList(dependency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfMessageContext validateAcceptMessage(Message m, BdfList body)
|
||||||
|
throws FormatException {
|
||||||
|
checkSize(body, 5);
|
||||||
|
|
||||||
|
byte[] sessionIdBytes = body.getRaw(1);
|
||||||
|
checkLength(sessionIdBytes, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
byte[] previousMessageId = body.getRaw(2);
|
||||||
|
checkLength(previousMessageId, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
byte[] ephemeralPublicKey = body.getRaw(3);
|
||||||
|
checkLength(ephemeralPublicKey, 0, MAX_PUBLIC_KEY_LENGTH);
|
||||||
|
|
||||||
|
BdfDictionary transportProperties = body.getDictionary(4);
|
||||||
|
if (transportProperties.size() < 1) throw new FormatException();
|
||||||
|
for (String tId : transportProperties.keySet()) {
|
||||||
|
checkLength(tId, 1, MAX_TRANSPORT_ID_LENGTH);
|
||||||
|
BdfDictionary tProps = transportProperties.getDictionary(tId);
|
||||||
|
clientHelper.parseAndValidateTransportProperties(tProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionId sessionId = new SessionId(sessionIdBytes);
|
||||||
|
BdfDictionary meta = messageEncoder
|
||||||
|
.encodeMetadata(ACCEPT, sessionId, m.getTimestamp(), false,
|
||||||
|
false, false);
|
||||||
|
MessageId dependency = new MessageId(previousMessageId);
|
||||||
|
return new BdfMessageContext(meta,
|
||||||
|
Collections.singletonList(dependency));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfMessageContext validateAuthMessage(Message m, BdfList body)
|
||||||
|
throws FormatException {
|
||||||
|
checkSize(body, 5);
|
||||||
|
|
||||||
|
byte[] sessionIdBytes = body.getRaw(1);
|
||||||
|
checkLength(sessionIdBytes, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
byte[] previousMessageId = body.getRaw(2);
|
||||||
|
checkLength(previousMessageId, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
byte[] mac = body.getRaw(3);
|
||||||
|
checkLength(mac, MAC_BYTES);
|
||||||
|
|
||||||
|
byte[] signature = body.getRaw(4);
|
||||||
|
checkLength(signature, 1, MAX_SIGNATURE_BYTES);
|
||||||
|
|
||||||
|
SessionId sessionId = new SessionId(sessionIdBytes);
|
||||||
|
BdfDictionary meta = messageEncoder
|
||||||
|
.encodeMetadata(AUTH, sessionId, m.getTimestamp(), false, false,
|
||||||
|
false);
|
||||||
|
MessageId dependency = new MessageId(previousMessageId);
|
||||||
|
return new BdfMessageContext(meta,
|
||||||
|
Collections.singletonList(dependency));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfMessageContext validateOtherMessage(MessageType type,
|
||||||
|
Message m, BdfList body) throws FormatException {
|
||||||
|
checkSize(body, 3);
|
||||||
|
|
||||||
|
byte[] sessionIdBytes = body.getRaw(1);
|
||||||
|
checkLength(sessionIdBytes, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
byte[] previousMessageId = body.getRaw(2);
|
||||||
|
checkLength(previousMessageId, UniqueId.LENGTH);
|
||||||
|
|
||||||
|
SessionId sessionId = new SessionId(sessionIdBytes);
|
||||||
|
BdfDictionary meta = messageEncoder
|
||||||
|
.encodeMetadata(type, sessionId, m.getTimestamp(), false, false,
|
||||||
|
false);
|
||||||
|
MessageId dependency = new MessageId(previousMessageId);
|
||||||
|
return new BdfMessageContext(meta,
|
||||||
|
Collections.singletonList(dependency));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
|
||||||
|
interface MessageEncoder {
|
||||||
|
|
||||||
|
BdfDictionary encodeRequestMetadata(MessageType type,
|
||||||
|
long timestamp, boolean local, boolean read, boolean visible,
|
||||||
|
boolean available, boolean accepted);
|
||||||
|
|
||||||
|
BdfDictionary encodeMetadata(MessageType type, SessionId sessionId,
|
||||||
|
long timestamp, boolean local, boolean read, boolean visible);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
enum MessageType {
|
||||||
|
|
||||||
|
REQUEST(0), ACCEPT(1), DECLINE(2), AUTH(3), ACTIVATE(4), ABORT(5);
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
MessageType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MessageType fromValue(int value) throws FormatException {
|
||||||
|
for (MessageType m : values()) if (m.value == value) return m;
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class RequestMessage extends IntroductionMessage {
|
||||||
|
|
||||||
|
private final Author author;
|
||||||
|
@Nullable
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
protected RequestMessage(MessageId messageId, GroupId groupId,
|
||||||
|
long timestamp, @Nullable MessageId previousMessageId,
|
||||||
|
Author author, @Nullable String message) {
|
||||||
|
super(messageId, groupId, timestamp, previousMessageId);
|
||||||
|
this.author = author;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Author getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
package org.briarproject.briar.introduction2;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.client.BdfMessageContext;
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.data.BdfEntry;
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.test.ValidatorTestCase;
|
||||||
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.ABORT;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.ACCEPT;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.ACTIVATE;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.AUTH;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.DECLINE;
|
||||||
|
import static org.briarproject.briar.introduction2.MessageType.REQUEST;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class IntroductionValidatorTest extends ValidatorTestCase {
|
||||||
|
|
||||||
|
private final MessageEncoder messageEncoder =
|
||||||
|
context.mock(MessageEncoder.class);
|
||||||
|
private final IntroductionValidator validator =
|
||||||
|
new IntroductionValidator(messageEncoder, clientHelper,
|
||||||
|
metadataEncoder, clock);
|
||||||
|
|
||||||
|
private final SessionId sessionId = new SessionId(getRandomId());
|
||||||
|
private final MessageId previousMsgId = new MessageId(getRandomId());
|
||||||
|
private final String text =
|
||||||
|
getRandomString(MAX_INTRODUCTION_MESSAGE_LENGTH);
|
||||||
|
private final BdfDictionary meta = new BdfDictionary();
|
||||||
|
private final BdfDictionary transportProperties = BdfDictionary.of(
|
||||||
|
new BdfEntry("transportId", new BdfDictionary())
|
||||||
|
);
|
||||||
|
private final byte[] mac = getRandomBytes(MAC_BYTES);
|
||||||
|
private final byte[] signature = getRandomBytes(MAX_SIGNATURE_BYTES);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction REQUEST
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsRequest() throws Exception {
|
||||||
|
BdfList body = BdfList.of(REQUEST.getValue(), previousMsgId.getBytes(),
|
||||||
|
authorList, text);
|
||||||
|
|
||||||
|
expectParseAuthor(authorList, author);
|
||||||
|
expectEncodeRequestMetadata();
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, previousMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsRequestWithPreviousMsgIdNull() throws Exception {
|
||||||
|
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList, text);
|
||||||
|
|
||||||
|
expectParseAuthor(authorList, author);
|
||||||
|
expectEncodeRequestMetadata();
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsRequestWithMessageNull() throws Exception {
|
||||||
|
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList, null);
|
||||||
|
|
||||||
|
expectParseAuthor(authorList, author);
|
||||||
|
expectEncodeRequestMetadata();
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortBodyForRequest() throws Exception {
|
||||||
|
BdfList body = BdfList.of(REQUEST.getValue(), null, authorList);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongBodyForRequest() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(REQUEST.getValue(), null, authorList, text, null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsRawMessageForRequest() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(REQUEST.getValue(), null, authorList, getRandomId());
|
||||||
|
expectParseAuthor(authorList, author);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsStringMessageIdForRequest() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(REQUEST.getValue(), "NoMessageId", authorList, null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction ACCEPT
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsAccept() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||||
|
transportProperties);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
|
transportProperties.getDictionary("transportId"));
|
||||||
|
}});
|
||||||
|
expectEncodeMetadata(ACCEPT);
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, previousMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortBodyForAccept() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(),
|
||||||
|
getRandomBytes(MAX_PUBLIC_KEY_LENGTH));
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongBodyForAccept() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||||
|
transportProperties, null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidSessionIdForAccept() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(ACCEPT.getValue(), null, previousMsgId.getBytes(),
|
||||||
|
getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||||
|
transportProperties);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidPreviousMsgIdForAccept() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
|
||||||
|
null, getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||||
|
transportProperties);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongPublicKeyForAccept() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(),
|
||||||
|
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), transportProperties);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsEmptyTransportPropertiesForAccept()
|
||||||
|
throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACCEPT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(),
|
||||||
|
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), new BdfDictionary());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction DECLINE
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsDecline() throws Exception {
|
||||||
|
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes());
|
||||||
|
|
||||||
|
expectEncodeMetadata(DECLINE);
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, previousMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortBodyForDecline() throws Exception {
|
||||||
|
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongBodyForDecline() throws Exception {
|
||||||
|
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidSessionIdForDecline() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(DECLINE.getValue(), null, previousMsgId.getBytes());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidPreviousMsgIdForDecline() throws Exception {
|
||||||
|
BdfList body = BdfList.of(DECLINE.getValue(), sessionId.getBytes(),
|
||||||
|
null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction AUTH
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), mac, signature);
|
||||||
|
|
||||||
|
expectEncodeMetadata(AUTH);
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, previousMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortBodyForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), mac);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongBodyForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), mac, signature, null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortMacForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), getRandomBytes(MAC_BYTES - 1),
|
||||||
|
signature);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongMacForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(),
|
||||||
|
getRandomBytes(MAC_BYTES + 1), signature);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidMacForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), null, signature);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortSignatureForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), mac, getRandomBytes(0));
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongSignatureForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), mac,
|
||||||
|
getRandomBytes(MAX_SIGNATURE_BYTES + 1));
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidSignatureForAuth() throws Exception {
|
||||||
|
BdfList body = BdfList.of(AUTH.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), mac, null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction ACTIVATE
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsActivate() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes());
|
||||||
|
|
||||||
|
expectEncodeMetadata(ACTIVATE);
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, previousMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortBodyForActivate() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongBodyForActivate() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidSessionIdForActivate() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(ACTIVATE.getValue(), null, previousMsgId.getBytes());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidPreviousMsgIdForActivate() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ACTIVATE.getValue(), sessionId.getBytes(),
|
||||||
|
null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction ABORT
|
||||||
|
//
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsAbort() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes());
|
||||||
|
|
||||||
|
expectEncodeMetadata(ABORT);
|
||||||
|
BdfMessageContext messageContext =
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
|
||||||
|
assertExpectedContext(messageContext, previousMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooShortBodyForAbort() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsTooLongBodyForAbort() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
|
||||||
|
previousMsgId.getBytes(), null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidSessionIdForAbort() throws Exception {
|
||||||
|
BdfList body =
|
||||||
|
BdfList.of(ABORT.getValue(), null, previousMsgId.getBytes());
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsInvalidPreviousMsgIdForAbort() throws Exception {
|
||||||
|
BdfList body = BdfList.of(ABORT.getValue(), sessionId.getBytes(),
|
||||||
|
null);
|
||||||
|
validator.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Introduction Helper Methods
|
||||||
|
//
|
||||||
|
|
||||||
|
private void expectEncodeRequestMetadata() {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(messageEncoder)
|
||||||
|
.encodeRequestMetadata(REQUEST, message.getTimestamp(),
|
||||||
|
false, false,
|
||||||
|
false, false, false);
|
||||||
|
will(returnValue(meta));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectEncodeMetadata(MessageType type) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(messageEncoder)
|
||||||
|
.encodeMetadata(type, sessionId, message.getTimestamp(),
|
||||||
|
false, false, false);
|
||||||
|
will(returnValue(meta));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertExpectedContext(BdfMessageContext c,
|
||||||
|
@Nullable MessageId dependency) {
|
||||||
|
assertEquals(meta, c.getDictionary());
|
||||||
|
if (dependency == null) {
|
||||||
|
assertEquals(0, c.getDependencies().size());
|
||||||
|
} else {
|
||||||
|
assertEquals(dependency, c.getDependencies().iterator().next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -368,14 +368,6 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
|
|||||||
.getBoolean(KEY_INITIAL_JOIN_MSG));
|
.getBoolean(KEY_INITIAL_JOIN_MSG));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectParseAuthor(BdfList authorList, Author author)
|
|
||||||
throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(clientHelper).parseAndValidateAuthor(authorList);
|
|
||||||
will(returnValue(author));
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expectRejectAuthor(BdfList authorList) throws Exception {
|
private void expectRejectAuthor(BdfList authorList) throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(clientHelper).parseAndValidateAuthor(authorList);
|
oneOf(clientHelper).parseAndValidateAuthor(authorList);
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public abstract class SharingValidatorTest extends ValidatorTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assertExpectedContext(BdfMessageContext messageContext,
|
void assertExpectedContext(BdfMessageContext messageContext,
|
||||||
@Nullable MessageId previousMsgId) throws FormatException {
|
@Nullable MessageId previousMsgId) {
|
||||||
Collection<MessageId> dependencies = messageContext.getDependencies();
|
Collection<MessageId> dependencies = messageContext.getDependencies();
|
||||||
if (previousMsgId == null) {
|
if (previousMsgId == null) {
|
||||||
assertTrue(dependencies.isEmpty());
|
assertTrue(dependencies.isEmpty());
|
||||||
|
|||||||
Reference in New Issue
Block a user