Messages and Validator for new Introduction Client

parent 218b2f7f
......@@ -16,4 +16,10 @@ public interface CryptoConstants {
* The maximum length of a signature in bytes.
*/
int MAX_SIGNATURE_BYTES = 64;
/**
* The length of a MAC in bytes.
*/
int MAC_BYTES = SecretKey.LENGTH;
}
package org.briarproject.bramble.test;
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.identity.Author;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
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.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getMessage;
public abstract class ValidatorTestCase extends BrambleMockTestCase {
......@@ -24,10 +27,23 @@ public abstract class ValidatorTestCase extends BrambleMockTestCase {
protected final Group group = getGroup(getClientId());
protected final GroupId groupId = group.getId();
protected final byte[] descriptor = group.getDescriptor();
protected final MessageId messageId = new MessageId(getRandomId());
protected final long timestamp = 1234567890 * 1000L;
protected final byte[] raw = getRandomBytes(123);
protected final Message message =
new Message(messageId, groupId, timestamp, raw);
protected final Message message = getMessage(groupId);
protected final MessageId messageId = message.getId();
protected final long timestamp = message.getTimestamp();
protected final byte[] raw = message.getRaw();
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));
}});
}
}
\ No newline at end of file
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;
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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));
}
}
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);
}
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();
}
}
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;
}
}
......@@ -368,14 +368,6 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
.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 {
context.checking(new Expectations() {{
oneOf(clientHelper).parseAndValidateAuthor(authorList);
......
......@@ -150,7 +150,7 @@ public abstract class SharingValidatorTest extends ValidatorTestCase {
}
void assertExpectedContext(BdfMessageContext messageContext,
@Nullable MessageId previousMsgId) throws FormatException {
@Nullable MessageId previousMsgId) {
Collection<MessageId> dependencies = messageContext.getDependencies();
if (previousMsgId == null) {
assertTrue(dependencies.isEmpty());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment