Removed restricted groups (may be restored after beta testing).

This commit is contained in:
akwizgran
2013-09-27 15:11:04 +01:00
parent 1e5e067df7
commit b94954544d
31 changed files with 219 additions and 630 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -140,7 +140,6 @@ OnItemClickListener {
long now = System.currentTimeMillis();
for(GroupStatus s : db.getAvailableGroups()) {
Group g = s.getGroup();
if(g.isRestricted()) continue;
if(s.isSubscribed()) {
try {
Collection<GroupMessageHeader> headers =
@@ -242,11 +241,8 @@ OnItemClickListener {
public void eventOccurred(DatabaseEvent e) {
if(e instanceof GroupMessageAddedEvent) {
Group g = ((GroupMessageAddedEvent) e).getGroup();
if(!g.isRestricted()) {
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
loadHeaders(g);
}
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
loadHeaders(((GroupMessageAddedEvent) e).getGroup());
} else if(e instanceof MessageExpiredEvent) {
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
loadHeaders();
@@ -255,18 +251,12 @@ OnItemClickListener {
LOG.info("Remote subscriptions changed, reloading");
loadAvailable();
} else if(e instanceof SubscriptionAddedEvent) {
Group g = ((SubscriptionAddedEvent) e).getGroup();
if(!g.isRestricted()) {
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
loadHeaders();
}
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
loadHeaders();
} else if(e instanceof SubscriptionRemovedEvent) {
Group g = ((SubscriptionRemovedEvent) e).getGroup();
if(!g.isRestricted()) {
// Reload the group, expecting NoSuchSubscriptionException
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
loadHeaders(g);
}
// Reload the group, expecting NoSuchSubscriptionException
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
loadHeaders(((SubscriptionRemovedEvent) e).getGroup());
}
}
@@ -317,10 +307,8 @@ OnItemClickListener {
lifecycleManager.waitForDatabase();
int available = 0;
long now = System.currentTimeMillis();
for(GroupStatus s : db.getAvailableGroups()) {
if(!s.getGroup().isRestricted() && !s.isSubscribed())
available++;
}
for(GroupStatus s : db.getAvailableGroups())
if(!s.isSubscribed()) available++;
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Loading available took " + duration + " ms");

View File

@@ -5,11 +5,8 @@ import static java.util.logging.Level.WARNING;
import static net.sf.briar.android.groups.ManageGroupsItem.NONE;
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -78,13 +75,10 @@ implements DatabaseListener, OnItemClickListener {
try {
lifecycleManager.waitForDatabase();
long now = System.currentTimeMillis();
List<GroupStatus> available = new ArrayList<GroupStatus>();
for(GroupStatus s : db.getAvailableGroups())
if(!s.getGroup().isRestricted()) available.add(s);
Collection<GroupStatus> available = db.getAvailableGroups();
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
available = Collections.unmodifiableList(available);
displayAvailableGroups(available);
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
@@ -124,17 +118,11 @@ implements DatabaseListener, OnItemClickListener {
LOG.info("Remote subscriptions changed, reloading");
loadAvailableGroups();
} else if(e instanceof SubscriptionAddedEvent) {
Group g = ((SubscriptionAddedEvent) e).getGroup();
if(g.isRestricted()) {
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
loadAvailableGroups();
}
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
loadAvailableGroups();
} else if(e instanceof SubscriptionRemovedEvent) {
Group g = ((SubscriptionRemovedEvent) e).getGroup();
if(g.isRestricted()) {
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
loadAvailableGroups();
}
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
loadAvailableGroups();
}
}

View File

@@ -11,10 +11,7 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -213,14 +210,12 @@ implements OnItemSelectedListener, OnClickListener {
public void run() {
try {
lifecycleManager.waitForDatabase();
List<Group> groups = new ArrayList<Group>();
long now = System.currentTimeMillis();
for(Group g : db.getSubscriptions())
if(!g.isRestricted()) groups.add(g);
Collection<Group> groups = db.getSubscriptions();
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Loading groups took " + duration + " ms");
displayGroups(Collections.unmodifiableList(groups));
displayGroups(groups);
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);

View File

@@ -17,7 +17,6 @@ import net.sf.briar.api.messaging.Ack;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.LocalGroup;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Offer;
@@ -62,12 +61,6 @@ public interface DatabaseComponent {
/** Stores a pseudonym that the user can use to sign messages. */
void addLocalAuthor(LocalAuthor a) throws DbException;
/**
* Stores a restricted group to which the user can post messages. Storing
* a group does not create a subscription to it.
*/
void addLocalGroup(LocalGroup g) throws DbException;
/** Stores a locally generated group message. */
void addLocalGroupMessage(Message m) throws DbException;
@@ -194,9 +187,6 @@ public interface DatabaseComponent {
/** Returns all pseudonyms that the user can use to sign messages. */
Collection<LocalAuthor> getLocalAuthors() throws DbException;
/** Returns all restricted groups to which the user can post messages. */
Collection<LocalGroup> getLocalGroups() throws DbException;
/** Returns the local transport properties for all transports. */
Map<TransportId, TransportProperties> getLocalProperties()
throws DbException;

View File

@@ -5,12 +5,12 @@ public class Group {
private final GroupId id;
private final String name;
private final byte[] publicKey;
private final byte[] salt;
public Group(GroupId id, String name, byte[] publicKey) {
public Group(GroupId id, String name, byte[] salt) {
this.id = id;
this.name = name;
this.publicKey = publicKey;
this.salt = salt;
}
/** Returns the group's unique identifier. */
@@ -23,18 +23,12 @@ public class Group {
return name;
}
/** Returns true if the group is restricted. */
public boolean isRestricted() {
return publicKey != null;
}
/**
* If the group is restricted, returns the public key used to verify the
* signatures on all messages sent to the group. If the group is
* unrestricted, returns null.
* Returns the salt used to distinguish the group from other groups with
* the same name.
*/
public byte[] getPublicKey() {
return publicKey;
public byte[] getSalt() {
return salt;
}
@Override

View File

@@ -4,13 +4,9 @@ import java.io.IOException;
public interface GroupFactory {
/** Creates an unrestricted group. */
/** Creates a group with the given name and a random salt. */
Group createGroup(String name) throws IOException;
/** Creates a restricted group. */
Group createGroup(String name, byte[] publicKey) throws IOException;
/** Creates a restricted group to which the local user can post messages. */
LocalGroup createLocalGroup(String name, byte[] publicKey,
byte[] privateKey) throws IOException;
/** Creates a group with the given name and salt. */
Group createGroup(String name, byte[] salt) throws IOException;
}

View File

@@ -1,18 +0,0 @@
package net.sf.briar.api.messaging;
/** A restricted group to which the local user can post messages. */
public class LocalGroup extends Group {
private final byte[] privateKey;
public LocalGroup(GroupId id, String name, byte[] publicKey,
byte[] privateKey) {
super(id, name, publicKey);
this.privateKey = privateKey;
}
/** Returns the private key used to sign all messages sent to the group. */
public byte[] getPrivateKey() {
return privateKey;
}
}

View File

@@ -12,24 +12,13 @@ public interface MessageFactory {
Message createPrivateMessage(MessageId parent, String contentType,
byte[] body) throws IOException, GeneralSecurityException;
/** Creates an anonymous message to an unrestricted group. */
/** Creates an anonymous group message. */
Message createAnonymousMessage(MessageId parent, Group group,
String contentType, byte[] body) throws IOException,
GeneralSecurityException;
/** Creates an anonymous message to a restricted group. */
Message createAnonymousMessage(MessageId parent, Group group,
PrivateKey groupKey, String contentType, byte[] body)
throws IOException, GeneralSecurityException;
/** Creates a pseudonymous message to an unrestricted group. */
/** Creates a pseudonymous group message. */
Message createPseudonymousMessage(MessageId parent, Group group,
Author author, PrivateKey authorKey, String contentType,
Author author, PrivateKey privateKey, String contentType,
byte[] body) throws IOException, GeneralSecurityException;
/** Creates a pseudonymous message to a restricted group. */
Message createPseudonymousMessage(MessageId parent, Group group,
PrivateKey groupKey, Author author, PrivateKey authorKey,
String contentType, byte[] body) throws IOException,
GeneralSecurityException;
}

View File

@@ -17,6 +17,9 @@ public interface MessagingConstants {
/** The maximum length of a group's name in UTF-8 bytes. */
int MAX_GROUP_NAME_LENGTH = 50;
/** The length of a group's random salt in bytes. */
int GROUP_SALT_LENGTH = 32;
/**
* The maximum length of a message body in bytes. To allow for future
* changes in the protocol, this is smaller than the maximum packet length
@@ -31,7 +34,7 @@ public interface MessagingConstants {
int MAX_SUBJECT_LENGTH = 100;
/** The length of a message's random salt in bytes. */
int SALT_LENGTH = 8;
int MESSAGE_SALT_LENGTH = 32;
/**
* The timestamp of the oldest message in the database is rounded using

View File

@@ -10,13 +10,12 @@ public class UnverifiedMessage {
private final Author author;
private final String contentType, subject;
private final long timestamp;
private final byte[] raw, authorSig, groupSig;
private final int bodyStart, bodyLength, signedByAuthor, signedByGroup;
private final byte[] raw, signature;
private final int bodyStart, bodyLength, signedLength;
public UnverifiedMessage(MessageId parent, Group group, Author author,
String contentType, String subject, long timestamp, byte[] raw,
byte[] authorSig, byte[] groupSig, int bodyStart, int bodyLength,
int signedByAuthor, int signedByGroup) {
byte[] signature, int bodyStart, int bodyLength, int signedLength) {
this.parent = parent;
this.group = group;
this.author = author;
@@ -24,12 +23,10 @@ public class UnverifiedMessage {
this.subject = subject;
this.timestamp = timestamp;
this.raw = raw;
this.authorSig = authorSig;
this.groupSig = groupSig;
this.signature = signature;
this.bodyStart = bodyStart;
this.bodyLength = bodyLength;
this.signedByAuthor = signedByAuthor;
this.signedByGroup = signedByGroup;
this.signedLength = signedLength;
}
/**
@@ -83,16 +80,8 @@ public class UnverifiedMessage {
/**
* Returns the author's signature, or null if this is an anonymous message.
*/
public byte[] getAuthorSignature() {
return authorSig;
}
/**
* Returns the group's signature, or null if this is a private message or
* a message belonging to an unrestricted group.
*/
public byte[] getGroupSignature() {
return groupSig;
public byte[] getSignature() {
return signature;
}
/** Returns the offset of the message body within the serialised message. */
@@ -109,15 +98,7 @@ public class UnverifiedMessage {
* Returns the length in bytes of the data covered by the author's
* signature.
*/
public int getLengthSignedByAuthor() {
return signedByAuthor;
}
/**
* Returns the length in bytes of the data covered by the group's
* signature.
*/
public int getLengthSignedByGroup() {
return signedByGroup;
public int getSignedLength() {
return signedLength;
}
}

View File

@@ -18,7 +18,6 @@ import net.sf.briar.api.db.PrivateMessageHeader;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.LocalGroup;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
@@ -112,14 +111,6 @@ interface Database<T> {
*/
void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
/**
* Stores a restricted group to which the user can post messages. Storing
* a group does not create a subscription to it.
* <p>
* Locking: identity write.
*/
void addLocalGroup(T txn, LocalGroup g) throws DbException;
/**
* Records a received message as needing to be acknowledged.
* <p>
@@ -192,14 +183,6 @@ interface Database<T> {
*/
boolean containsContact(T txn, ContactId c) throws DbException;
/**
* Returns true if the database contains the given restricted group to
* which the user can post messages.
* <p>
* Locking: identity read.
*/
boolean containsLocalGroup(T txn, GroupId g) throws DbException;
/**
* Returns true if the database contains the given message.
* <p>
@@ -301,6 +284,14 @@ interface Database<T> {
*/
MessageId getGroupMessageParent(T txn, MessageId m) throws DbException;
/**
* Returns the IDs of all group messages posted by the given author.
* <p>
* Locking: message read.
*/
Collection<MessageId> getGroupMessages(T txn, AuthorId a)
throws DbException;
/**
* Returns the time at which a connection to each contact was last opened
* or closed.
@@ -323,13 +314,6 @@ interface Database<T> {
*/
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
/**
* Returns all restricted groups to which the user can post messages.
* <p>
* Locking: identity read.
*/
Collection<LocalGroup> getLocalGroups(T txn) throws DbException;
/**
* Returns the local transport properties for all transports.
* <p>
@@ -556,15 +540,6 @@ interface Database<T> {
*/
Map<GroupId, Integer> getUnreadMessageCounts(T txn) throws DbException;
/**
* Returns the IDs of all messages posted by the given author to
* unrestricted groups.
* <p>
* Locking: message read.
*/
Collection<MessageId> getUnrestrictedGroupMessages(T txn, AuthorId a)
throws DbException;
/**
* Returns the contacts to which the given group is visible.
* <p>
@@ -631,13 +606,6 @@ interface Database<T> {
*/
void removeContact(T txn, ContactId c) throws DbException;
/**
* Removes the given restricted group to which the user can post messages.
* <p>
* Locking: identity write.
*/
void removeLocalGroup(T txn, GroupId g) throws DbException;
/**
* Removes a message (and all associated state) from the database.
* <p>

View File

@@ -65,7 +65,6 @@ import net.sf.briar.api.messaging.Ack;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.LocalGroup;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Offer;
@@ -285,22 +284,6 @@ DatabaseCleaner.Callback {
}
}
public void addLocalGroup(LocalGroup g) throws DbException {
identityLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
db.addLocalGroup(txn, g);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
identityLock.writeLock().unlock();
}
}
public void addLocalGroupMessage(Message m) throws DbException {
boolean added = false;
contactLock.readLock().lock();
@@ -362,13 +345,9 @@ DatabaseCleaner.Callback {
if(!c.equals(sender)) db.addStatus(txn, c, id, false);
}
// Calculate and store the message's sendability
if(m.getGroup().isRestricted()) {
db.setSendability(txn, id, 1);
} else {
int sendability = calculateSendability(txn, m);
db.setSendability(txn, id, sendability);
if(sendability > 0) updateAncestorSendability(txn, id, true);
}
int sendability = calculateSendability(txn, m);
db.setSendability(txn, id, sendability);
if(sendability > 0) updateAncestorSendability(txn, id, true);
// Count the bytes stored
synchronized(spaceLock) {
bytesStoredSinceLastCheck += m.getSerialised().length;
@@ -1066,23 +1045,6 @@ DatabaseCleaner.Callback {
}
}
public Collection<LocalGroup> getLocalGroups() throws DbException {
identityLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
Collection<LocalGroup> groups = db.getLocalGroups(txn);
db.commitTransaction(txn);
return groups;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
identityLock.readLock().unlock();
}
}
public Map<TransportId, TransportProperties> getLocalProperties()
throws DbException {
transportLock.readLock().lock();
@@ -1964,8 +1926,8 @@ DatabaseCleaner.Callback {
}
/**
* Updates the sendability of all messages posted by the given author to
* unrestricted groups, and the ancestors of those messages if necessary.
* Updates the sendability of all group messages posted by the given
* author, and the ancestors of those messages if necessary.
* <p>
* Locking: message write.
* @param increment true if the user's rating for the author has changed
@@ -1973,7 +1935,7 @@ DatabaseCleaner.Callback {
*/
private void updateAuthorSendability(T txn, AuthorId a, boolean increment)
throws DbException {
for(MessageId id : db.getUnrestrictedGroupMessages(txn, a)) {
for(MessageId id : db.getGroupMessages(txn, a)) {
int sendability = db.getSendability(txn, id);
if(increment) {
db.setSendability(txn, id, sendability + 1);
@@ -2125,8 +2087,6 @@ DatabaseCleaner.Callback {
throw new NoSuchSubscriptionException();
affected = db.getVisibility(txn, id);
db.removeSubscription(txn, id);
if(db.containsLocalGroup(txn, id))
db.removeLocalGroup(txn, id);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);

View File

@@ -43,7 +43,6 @@ import net.sf.briar.api.db.PrivateMessageHeader;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.LocalGroup;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
@@ -71,15 +70,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " privateKey BINARY NOT NULL,"
+ " PRIMARY KEY (authorId))";
// Locking: identity
private static final String CREATE_LOCAL_GROUPS =
"CREATE TABLE localGroups"
+ " (groupId HASH NOT NULL,"
+ " name VARCHAR NOT NULL,"
+ " publicKey BINARY NOT NULL,"
+ " privateKey BINARY NOT NULL,"
+ " PRIMARY KEY (groupId))";
// Locking: contact
// Dependents: message, retention, subscription, transport, window
private static final String CREATE_CONTACTS =
@@ -104,7 +94,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE groups"
+ " (groupId HASH NOT NULL,"
+ " name VARCHAR NOT NULL,"
+ " publicKey BINARY," // Null for unrestricted groups
+ " salt BINARY NOT NULL,"
+ " visibleToAll BOOLEAN NOT NULL,"
+ " PRIMARY KEY (groupId))";
@@ -126,7 +116,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (contactId INT NOT NULL,"
+ " groupId HASH NOT NULL," // Not a foreign key
+ " name VARCHAR NOT NULL,"
+ " publicKey BINARY," // Null for unrestricted groups
+ " salt BINARY NOT NULL,"
+ " PRIMARY KEY (contactId, groupId),"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
@@ -406,7 +396,6 @@ abstract class JdbcDatabase implements Database<Connection> {
try {
s = txn.createStatement();
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
s.executeUpdate(insertTypeNames(CREATE_LOCAL_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR);
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
@@ -750,26 +739,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void addLocalGroup(Connection txn, LocalGroup g) throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO localGroups"
+ " (groupId, name, publicKey, privateKey)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes());
ps.setString(2, g.getName());
ps.setBytes(3, g.getPublicKey());
ps.setBytes(4, g.getPrivateKey());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void addMessageToAck(Connection txn, ContactId c, MessageId m)
throws DbException {
PreparedStatement ps = null;
@@ -916,13 +885,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
if(count > MAX_SUBSCRIPTIONS) throw new DbStateException();
if(count == MAX_SUBSCRIPTIONS) return false;
sql = "INSERT INTO groups (groupId, name, publicKey, visibleToAll)"
sql = "INSERT INTO groups (groupId, name, salt, visibleToAll)"
+ " VALUES (?, ?, ?, FALSE)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes());
ps.setString(2, g.getName());
if(g.isRestricted()) ps.setBytes(3, g.getPublicKey());
else ps.setNull(3, BINARY);
ps.setBytes(3, g.getSalt());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
@@ -1059,27 +1027,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public boolean containsLocalGroup(Connection txn, GroupId g)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM localGroups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
boolean found = rs.next();
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return found;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public boolean containsMessage(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
@@ -1172,8 +1119,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null;
try {
// Add all subscribed groups to the list
String sql = "SELECT groupId, name, publicKey, visibleToAll"
+ " FROM groups";
String sql = "SELECT groupId, name, salt, visibleToAll FROM groups";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
List<GroupStatus> groups = new ArrayList<GroupStatus>();
@@ -1182,24 +1128,23 @@ abstract class JdbcDatabase implements Database<Connection> {
GroupId id = new GroupId(rs.getBytes(1));
subscribed.add(id);
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
Group group = new Group(id, name, publicKey);
byte[] salt = rs.getBytes(3);
Group group = new Group(id, name, salt);
boolean visibleToAll = rs.getBoolean(4);
groups.add(new GroupStatus(group, true, visibleToAll));
}
rs.close();
ps.close();
// Add all contact groups to the list, unless already added
sql = "SELECT DISTINCT groupId, name, publicKey"
+ " FROM contactGroups";
sql = "SELECT DISTINCT groupId, name, salt FROM contactGroups";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()) {
GroupId id = new GroupId(rs.getBytes(1));
if(subscribed.contains(id)) continue;
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
Group group = new Group(id, name, publicKey);
byte[] salt = rs.getBytes(3);
Group group = new Group(id, name, salt);
groups.add(new GroupStatus(group, false, false));
}
rs.close();
@@ -1340,16 +1285,16 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT name, publicKey FROM groups WHERE groupId = ?";
String sql = "SELECT name, salt FROM groups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
String name = rs.getString(1);
byte[] publicKey = rs.getBytes(2);
byte[] salt = rs.getBytes(2);
rs.close();
ps.close();
return new Group(g, name, publicKey);
return new Group(g, name, salt);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -1439,6 +1384,31 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<MessageId> getGroupMessages(Connection txn,
AuthorId a) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId"
+ " FROM messages AS m"
+ " JOIN groups AS g"
+ " ON m.groupId = g.groupId"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Map<ContactId, Long> getLastConnected(Connection txn)
throws DbException {
PreparedStatement ps = null;
@@ -1512,34 +1482,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<LocalGroup> getLocalGroups(Connection txn)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, name, publicKey, privateKey"
+ " FROM localGroups";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
List<LocalGroup> groups = new ArrayList<LocalGroup>();
while(rs.next()) {
GroupId groupId = new GroupId(rs.getBytes(1));
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
byte[] privateKey = rs.getBytes(4);
groups.add(new LocalGroup(groupId, name, publicKey,
privateKey));
}
rs.close();
ps.close();
return Collections.unmodifiableList(groups);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Map<TransportId, TransportProperties> getLocalProperties(
Connection txn) throws DbException {
PreparedStatement ps = null;
@@ -2223,7 +2165,8 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public boolean getStarredFlag(Connection txn, MessageId m) throws DbException {
public boolean getStarredFlag(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
@@ -2249,15 +2192,15 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, name, publicKey FROM groups";
String sql = "SELECT groupId, name, salt FROM groups";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
List<Group> subs = new ArrayList<Group>();
while(rs.next()) {
GroupId groupId = new GroupId(rs.getBytes(1));
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
subs.add(new Group(groupId, name, publicKey));
byte[] salt = rs.getBytes(3);
subs.add(new Group(groupId, name, salt));
}
rs.close();
ps.close();
@@ -2274,7 +2217,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, name, publicKey FROM contactGroups"
String sql = "SELECT groupId, name, salt FROM contactGroups"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
@@ -2283,8 +2226,8 @@ abstract class JdbcDatabase implements Database<Connection> {
while(rs.next()) {
GroupId groupId = new GroupId(rs.getBytes(1));
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
subs.add(new Group(groupId, name, publicKey));
byte[] salt = rs.getBytes(3);
subs.add(new Group(groupId, name, salt));
}
rs.close();
ps.close();
@@ -2336,7 +2279,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT g.groupId, name, publicKey,"
String sql = "SELECT g.groupId, name, salt,"
+ " localVersion, txCount"
+ " FROM groups AS g"
+ " JOIN groupVisibilities AS vis"
@@ -2356,8 +2299,8 @@ abstract class JdbcDatabase implements Database<Connection> {
while(rs.next()) {
GroupId groupId = new GroupId(rs.getBytes(1));
String name = rs.getString(2);
byte[] key = rs.getBytes(3);
subs.add(new Group(groupId, name, key));
byte[] salt = rs.getBytes(3);
subs.add(new Group(groupId, name, salt));
version = rs.getLong(4);
txCount = rs.getInt(5);
}
@@ -2564,32 +2507,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<MessageId> getUnrestrictedGroupMessages(Connection txn,
AuthorId a) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId"
+ " FROM messages AS m"
+ " JOIN groups AS g"
+ " ON m.groupId = g.groupId"
+ " WHERE authorId = ?"
+ " AND publicKey IS NULL";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<ContactId> getVisibility(Connection txn, GroupId g)
throws DbException {
PreparedStatement ps = null;
@@ -2808,21 +2725,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void removeLocalGroup(Connection txn, GroupId g) throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM localGroups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void removeMessage(Connection txn, MessageId m) throws DbException {
PreparedStatement ps = null;
try {
@@ -3395,15 +3297,14 @@ abstract class JdbcDatabase implements Database<Connection> {
// Store the new subscriptions, if any
if(subs.isEmpty()) return true;
sql = "INSERT INTO contactGroups"
+ " (contactId, groupId, name, publicKey)"
+ " (contactId, groupId, name, salt)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
for(Group g : subs) {
ps.setBytes(2, g.getId().getBytes());
ps.setString(3, g.getName());
if(g.isRestricted()) ps.setBytes(4, g.getPublicKey());
else ps.setNull(4, BINARY);
ps.setBytes(4, g.getSalt());
ps.addBatch();
}
int[] batchAffected = ps.executeBatch();

View File

@@ -1,5 +1,6 @@
package net.sf.briar.messaging;
import static net.sf.briar.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
import static net.sf.briar.api.messaging.Types.GROUP;
import java.io.ByteArrayOutputStream;
@@ -10,7 +11,6 @@ import net.sf.briar.api.crypto.MessageDigest;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupFactory;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.LocalGroup;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
@@ -28,29 +28,20 @@ class GroupFactoryImpl implements GroupFactory {
}
public Group createGroup(String name) throws IOException {
return createGroup(name, null);
byte[] salt = new byte[GROUP_SALT_LENGTH];
crypto.getSecureRandom().nextBytes(salt);
return createGroup(name, salt);
}
public Group createGroup(String name, byte[] publicKey) throws IOException {
GroupId id = getId(name, publicKey);
return new Group(id, name, publicKey);
}
public LocalGroup createLocalGroup(String name, byte[] publicKey,
byte[] privateKey) throws IOException {
GroupId id = getId(name, publicKey);
return new LocalGroup(id, name, publicKey, privateKey);
}
private GroupId getId(String name, byte[] publicKey) throws IOException {
public Group createGroup(String name, byte[] salt) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
w.writeStructId(GROUP);
w.writeString(name);
if(publicKey == null) w.writeNull();
else w.writeBytes(publicKey);
w.writeBytes(salt);
MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.update(out.toByteArray());
return new GroupId(messageDigest.digest());
GroupId id = new GroupId(messageDigest.digest());
return new Group(id, name, salt);
}
}

View File

@@ -5,7 +5,7 @@ import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.SALT_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MESSAGE_SALT_LENGTH;
import static net.sf.briar.api.messaging.Types.AUTHOR;
import static net.sf.briar.api.messaging.Types.GROUP;
import static net.sf.briar.api.messaging.Types.MESSAGE;
@@ -39,7 +39,7 @@ import com.google.inject.Inject;
class MessageFactoryImpl implements MessageFactory {
private final Signature authorSignature, groupSignature;
private final Signature signature;
private final SecureRandom random;
private final MessageDigest messageDigest;
private final WriterFactory writerFactory;
@@ -49,8 +49,7 @@ class MessageFactoryImpl implements MessageFactory {
@Inject
MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory,
Clock clock) {
authorSignature = crypto.getSignature();
groupSignature = crypto.getSignature();
signature = crypto.getSignature();
random = crypto.getSecureRandom();
messageDigest = crypto.getMessageDigest();
this.writerFactory = writerFactory;
@@ -60,46 +59,27 @@ class MessageFactoryImpl implements MessageFactory {
public Message createPrivateMessage(MessageId parent, String contentType,
byte[] body) throws IOException, GeneralSecurityException {
return createMessage(parent, null, null, null, null, contentType, body);
return createMessage(parent, null, null, null, contentType, body);
}
public Message createAnonymousMessage(MessageId parent, Group group,
String contentType, byte[] body) throws IOException,
GeneralSecurityException {
return createMessage(parent, group, null, null, null, contentType,
body);
}
public Message createAnonymousMessage(MessageId parent, Group group,
PrivateKey groupKey, String contentType, byte[] body)
throws IOException, GeneralSecurityException {
return createMessage(parent, group, groupKey, null, null, contentType,
body);
return createMessage(parent, group, null, null, contentType, body);
}
public Message createPseudonymousMessage(MessageId parent, Group group,
Author author, PrivateKey authorKey, String contentType,
Author author, PrivateKey privateKey, String contentType,
byte[] body) throws IOException, GeneralSecurityException {
return createMessage(parent, group, null, author, authorKey,
contentType, body);
return createMessage(parent, group, author, privateKey, contentType,
body);
}
public Message createPseudonymousMessage(MessageId parent, Group group,
PrivateKey groupKey, Author author, PrivateKey authorKey,
String contentType, byte[] body) throws IOException,
GeneralSecurityException {
return createMessage(parent, group, groupKey, author, authorKey,
contentType, body);
}
private Message createMessage(MessageId parent, Group group,
PrivateKey groupKey, Author author, PrivateKey authorKey,
String contentType, byte[] body) throws IOException,
GeneralSecurityException {
private Message createMessage(MessageId parent, Group group, Author author,
PrivateKey privateKey, String contentType, byte[] body)
throws IOException, GeneralSecurityException {
// Validate the arguments
if((author == null) != (authorKey == null))
throw new IllegalArgumentException();
if((group == null || !group.isRestricted()) != (groupKey == null))
if((author == null) != (privateKey == null))
throw new IllegalArgumentException();
if(contentType.getBytes("UTF-8").length > MAX_CONTENT_TYPE_LENGTH)
throw new IllegalArgumentException();
@@ -113,17 +93,11 @@ class MessageFactoryImpl implements MessageFactory {
w.addConsumer(counting);
Consumer digestingConsumer = new DigestingConsumer(messageDigest);
w.addConsumer(digestingConsumer);
Consumer authorConsumer = null;
if(authorKey != null) {
authorSignature.initSign(authorKey);
authorConsumer = new SigningConsumer(authorSignature);
w.addConsumer(authorConsumer);
}
Consumer groupConsumer = null;
if(groupKey != null) {
groupSignature.initSign(groupKey);
groupConsumer = new SigningConsumer(groupSignature);
w.addConsumer(groupConsumer);
Consumer signingConsumer = null;
if(privateKey != null) {
signature.initSign(privateKey);
signingConsumer = new SigningConsumer(signature);
w.addConsumer(signingConsumer);
}
// Write the message
w.writeStructId(MESSAGE);
@@ -136,32 +110,22 @@ class MessageFactoryImpl implements MessageFactory {
w.writeString(contentType);
long timestamp = clock.currentTimeMillis();
w.writeInt64(timestamp);
byte[] salt = new byte[SALT_LENGTH];
byte[] salt = new byte[MESSAGE_SALT_LENGTH];
random.nextBytes(salt);
w.writeBytes(salt);
w.writeBytes(body);
int bodyStart = (int) counting.getCount() - body.length;
// Sign the message with the author's private key, if there is one
if(authorKey == null) {
if(privateKey == null) {
w.writeNull();
} else {
w.removeConsumer(authorConsumer);
byte[] sig = authorSignature.sign();
w.removeConsumer(signingConsumer);
byte[] sig = signature.sign();
if(sig.length > MAX_SIGNATURE_LENGTH)
throw new IllegalArgumentException();
w.writeBytes(sig);
}
// Sign the message with the group's private key, if there is one
if(groupKey == null) {
w.writeNull();
} else {
w.removeConsumer(groupConsumer);
byte[] sig = groupSignature.sign();
if(sig.length > MAX_SIGNATURE_LENGTH)
throw new IllegalArgumentException();
w.writeBytes(sig);
}
// Hash the message, including the signatures, to get the message ID
// Hash the message, including the signature, to get the message ID
w.removeConsumer(digestingConsumer);
MessageId id = new MessageId(messageDigest.digest());
// If the content type is text/plain, extract a subject line
@@ -181,8 +145,7 @@ class MessageFactoryImpl implements MessageFactory {
private void writeGroup(Writer w, Group g) throws IOException {
w.writeStructId(GROUP);
w.writeString(g.getName());
if(g.isRestricted()) w.writeBytes(g.getPublicKey());
else w.writeNull();
w.writeBytes(g.getSalt());
}
private void writeAuthor(Writer w, Author a) throws IOException {

View File

@@ -5,7 +5,7 @@ import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.SALT_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.MESSAGE_SALT_LENGTH;
import static net.sf.briar.api.messaging.Types.MESSAGE;
import java.io.IOException;
@@ -67,8 +67,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
long timestamp = r.readInt64();
if(timestamp < 0) throw new FormatException();
// Read the salt
byte[] salt = r.readBytes(SALT_LENGTH);
if(salt.length < SALT_LENGTH) throw new FormatException();
byte[] salt = r.readBytes(MESSAGE_SALT_LENGTH);
if(salt.length < MESSAGE_SALT_LENGTH) throw new FormatException();
// Read the message body
byte[] body = r.readBytes(MAX_BODY_LENGTH);
// If the content type is text/plain, extract a subject line
@@ -84,23 +84,17 @@ class MessageReader implements StructReader<UnverifiedMessage> {
// Record the offset of the body within the message
int bodyStart = (int) counting.getCount() - body.length;
// Record the length of the data covered by the author's signature
int signedByAuthor = (int) counting.getCount();
int signedLength = (int) counting.getCount();
// Read the author's signature, if there is one
byte[] authorSig = null;
byte[] signature = null;
if(author == null) r.readNull();
else authorSig = r.readBytes(MAX_SIGNATURE_LENGTH);
// Record the length of the data covered by the group's signature
int signedByGroup = (int) counting.getCount();
// Read the group's signature, if there is one
byte[] groupSig = null;
if(group == null || !group.isRestricted()) r.readNull();
else groupSig = r.readBytes(MAX_SIGNATURE_LENGTH);
// That's all, folks
else signature = r.readBytes(MAX_SIGNATURE_LENGTH);
// The signature will be verified later
r.removeConsumer(counting);
r.removeConsumer(copying);
byte[] raw = copying.getCopy();
return new UnverifiedMessage(parent, group, author, contentType,
subject, timestamp, raw, authorSig, groupSig, bodyStart,
body.length, signedByAuthor, signedByGroup);
subject, timestamp, raw, signature, bodyStart, body.length,
signedLength);
}
}

View File

@@ -8,7 +8,6 @@ import net.sf.briar.api.crypto.KeyParser;
import net.sf.briar.api.crypto.MessageDigest;
import net.sf.briar.api.crypto.PublicKey;
import net.sf.briar.api.crypto.Signature;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.MessageVerifier;
@@ -31,7 +30,7 @@ class MessageVerifierImpl implements MessageVerifier {
throws GeneralSecurityException {
MessageDigest messageDigest = crypto.getMessageDigest();
Signature signature = crypto.getSignature();
// Hash the message, including the signatures, to get the message ID
// Hash the message, including the signature, to get the message ID
byte[] raw = m.getSerialised();
messageDigest.update(raw);
MessageId id = new MessageId(messageDigest.digest());
@@ -40,20 +39,11 @@ class MessageVerifierImpl implements MessageVerifier {
if(author != null) {
PublicKey k = keyParser.parsePublicKey(author.getPublicKey());
signature.initVerify(k);
signature.update(raw, 0, m.getLengthSignedByAuthor());
if(!signature.verify(m.getAuthorSignature()))
signature.update(raw, 0, m.getSignedLength());
if(!signature.verify(m.getSignature()))
throw new GeneralSecurityException();
}
// Verify the group's signature, if there is one
Group group = m.getGroup();
if(group != null && group.isRestricted()) {
PublicKey k = keyParser.parsePublicKey(group.getPublicKey());
signature.initVerify(k);
signature.update(raw, 0, m.getLengthSignedByGroup());
if(!signature.verify(m.getGroupSignature()))
throw new GeneralSecurityException();
}
return new MessageImpl(id, m.getParent(), group, author,
return new MessageImpl(id, m.getParent(), m.getGroup(), author,
m.getContentType(), m.getSubject(), m.getTimestamp(), raw,
m.getBodyStart(), m.getBodyLength());
}

View File

@@ -133,8 +133,7 @@ class PacketWriterImpl implements PacketWriter {
for(Group g : u.getGroups()) {
w.writeStructId(GROUP);
w.writeString(g.getName());
if(g.isRestricted()) w.writeBytes(g.getPublicKey());
else w.writeNull();
w.writeBytes(g.getSalt());
}
w.writeListEnd();
w.writeInt64(u.getVersion());

View File

@@ -68,8 +68,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
private final ContactId contactId;
private final byte[] secret;
private final Author author;
private final Group group, group1;
private final Message message, message1, message2, message3;
private final Group group;
private final Message message, message1;
private final String authorName = "Alice";
private final String contentType = "text/plain";
private final String messageBody = "Hello world";
@@ -93,33 +93,23 @@ public class ProtocolIntegrationTest extends BriarTestCase {
// Create a shared secret
secret = new byte[32];
new Random().nextBytes(secret);
// Create two groups: one restricted, one unrestricted
// Create a group
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
group = groupFactory.createGroup("Unrestricted group");
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
group1 = groupFactory.createGroup("Restricted group",
groupKeyPair.getPublic().getEncoded());
group = groupFactory.createGroup("Group");
// Create an author
AuthorFactory authorFactory = i.getInstance(AuthorFactory.class);
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
author = authorFactory.createAuthor(authorName,
authorKeyPair.getPublic().getEncoded());
// Create two messages to each group: one anonymous, one pseudonymous
// Create two messages to the group: one anonymous, one pseudonymous
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
message = messageFactory.createAnonymousMessage(null, group,
contentType, messageBody.getBytes("UTF-8"));
message1 = messageFactory.createAnonymousMessage(null, group1,
groupKeyPair.getPrivate(), contentType,
messageBody.getBytes("UTF-8"));
message2 = messageFactory.createPseudonymousMessage(null, group,
message1 = messageFactory.createPseudonymousMessage(null, group,
author, authorKeyPair.getPrivate(), contentType,
messageBody.getBytes("UTF-8"));
message3 = messageFactory.createPseudonymousMessage(null, group1,
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
contentType, messageBody.getBytes("UTF-8"));
messageIds = Arrays.asList(message.getId(), message1.getId(),
message2.getId(), message3.getId());
messageIds = Arrays.asList(message.getId(), message1.getId());
// Create some transport properties
transportId = new TransportId(TestUtils.getRandomId());
transportProperties = new TransportProperties(Collections.singletonMap(
@@ -145,18 +135,14 @@ public class ProtocolIntegrationTest extends BriarTestCase {
writer.writeMessage(message.getSerialised());
writer.writeMessage(message1.getSerialised());
writer.writeMessage(message2.getSerialised());
writer.writeMessage(message3.getSerialised());
writer.writeOffer(new Offer(messageIds));
BitSet requested = new BitSet(4);
BitSet requested = new BitSet(2);
requested.set(1);
requested.set(3);
writer.writeRequest(new Request(requested, 4));
writer.writeRequest(new Request(requested, 2));
SubscriptionUpdate su = new SubscriptionUpdate(
Arrays.asList(group, group1), 1);
SubscriptionUpdate su = new SubscriptionUpdate(Arrays.asList(group), 1);
writer.writeSubscriptionUpdate(su);
TransportUpdate tu = new TransportUpdate(transportId,
@@ -191,12 +177,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
assertTrue(reader.hasMessage());
m = reader.readMessage();
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
assertTrue(reader.hasMessage());
m = reader.readMessage();
checkMessageEquality(message2, messageVerifier.verifyMessage(m));
assertTrue(reader.hasMessage());
m = reader.readMessage();
checkMessageEquality(message3, messageVerifier.verifyMessage(m));
assertFalse(reader.hasMessage());
// Read the offer
assertTrue(reader.hasOffer());
@@ -209,15 +190,13 @@ public class ProtocolIntegrationTest extends BriarTestCase {
BitSet requested = req.getBitmap();
assertFalse(requested.get(0));
assertTrue(requested.get(1));
assertFalse(requested.get(2));
assertTrue(requested.get(3));
// If there are any padding bits, they should all be zero
assertEquals(2, requested.cardinality());
assertEquals(1, requested.cardinality());
// Read the subscription update
assertTrue(reader.hasSubscriptionUpdate());
SubscriptionUpdate su = reader.readSubscriptionUpdate();
assertEquals(Arrays.asList(group, group1), su.getGroups());
assertEquals(Arrays.asList(group), su.getGroups());
assertEquals(1, su.getVersion());
// Read the transport update

View File

@@ -15,7 +15,7 @@ public class PasswordBasedKdfTest extends BriarTestCase {
public void testEncryptionAndDecryption() {
CryptoComponent crypto = new CryptoComponentImpl();
Random random = new Random();
byte[] input = new byte[123];
byte[] input = new byte[1234];
random.nextBytes(input);
char[] password = "password".toCharArray();
byte[] ciphertext = crypto.encryptWithPassword(input, password);
@@ -27,7 +27,7 @@ public class PasswordBasedKdfTest extends BriarTestCase {
public void testInvalidCiphertextReturnsNull() {
CryptoComponent crypto = new CryptoComponentImpl();
Random random = new Random();
byte[] input = new byte[123];
byte[] input = new byte[1234];
random.nextBytes(input);
char[] password = "password".toCharArray();
byte[] ciphertext = crypto.encryptWithPassword(input, password);

View File

@@ -1,5 +1,7 @@
package net.sf.briar.db;
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.messaging.Rating.UNRATED;
@@ -59,8 +61,8 @@ import org.junit.Test;
public abstract class DatabaseComponentTest extends BriarTestCase {
protected final Object txn = new Object();
protected final GroupId groupId, restrictedGroupId;
protected final Group group, restrictedGroup;
protected final GroupId groupId;
protected final Group group;
protected final AuthorId authorId;
protected final Author author;
protected final AuthorId localAuthorId;
@@ -80,15 +82,12 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
public DatabaseComponentTest() {
groupId = new GroupId(TestUtils.getRandomId());
restrictedGroupId = new GroupId(TestUtils.getRandomId());
group = new Group(groupId, "Group name", null);
restrictedGroup = new Group(restrictedGroupId, "Restricted group name",
new byte[60]);
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
authorId = new AuthorId(TestUtils.getRandomId());
author = new Author(authorId, "Alice", new byte[60]);
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
localAuthorId = new AuthorId(TestUtils.getRandomId());
localAuthor = new LocalAuthor(localAuthorId, "Bob", new byte[60],
new byte[60]);
localAuthor = new LocalAuthor(localAuthorId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
messageId = new MessageId(TestUtils.getRandomId());
messageId1 = new MessageId(TestUtils.getRandomId());
privateMessageId = new MessageId(TestUtils.getRandomId());
@@ -142,7 +141,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
// setRating(authorId, GOOD)
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Collections.emptyList()));
oneOf(listener).eventOccurred(with(any(RatingChangedEvent.class)));
// setRating(authorId, GOOD) again
@@ -186,8 +185,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.emptyList()));
oneOf(database).removeSubscription(txn, groupId);
oneOf(database).containsLocalGroup(txn, groupId);
will(returnValue(false));
oneOf(listener).eventOccurred(with(any(
SubscriptionRemovedEvent.class)));
oneOf(listener).eventOccurred(with(any(
@@ -228,59 +225,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
context.assertIsSatisfied();
}
@Test
public void testRestrictedGroupMessagesAreAlwaysSendable()
throws Exception {
final Message groupMessage = new TestMessage(messageId, null,
restrictedGroup, author, contentType, subject, timestamp, raw);
final Message groupMessage1 = new TestMessage(messageId1, null,
restrictedGroup, null, contentType, subject, timestamp, raw);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
context.checking(new Expectations() {{
// addLocalGroupMessage(groupMessage)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsSubscription(txn, restrictedGroupId);
will(returnValue(true));
oneOf(database).addGroupMessage(txn, groupMessage, false);
will(returnValue(true));
oneOf(database).setReadFlag(txn, messageId, true);
oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId)));
oneOf(database).addStatus(txn, contactId, messageId, false);
oneOf(database).setSendability(txn, messageId, 1);
oneOf(database).commitTransaction(txn);
// receiveMessage(groupMessage1)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).containsVisibleSubscription(txn, contactId,
restrictedGroupId);
will(returnValue(true));
oneOf(database).addGroupMessage(txn, groupMessage1, true);
will(returnValue(true));
oneOf(database).addStatus(txn, contactId, messageId1, true);
oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId)));
oneOf(database).setSendability(txn, messageId1, 1);
oneOf(database).addMessageToAck(txn, contactId, messageId1);
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(groupMessage);
db.receiveMessage(contactId, groupMessage1);
context.assertIsSatisfied();
}
@Test
public void testNullParentStopsBackwardInclusion() throws Exception {
Mockery context = new Mockery();
@@ -295,7 +239,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
@@ -327,7 +271,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
@@ -364,7 +308,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));

View File

@@ -1,6 +1,8 @@
package net.sf.briar.db;
import static java.util.concurrent.TimeUnit.SECONDS;
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static net.sf.briar.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.messaging.Rating.UNRATED;
import static org.junit.Assert.assertArrayEquals;
@@ -71,12 +73,12 @@ public class H2DatabaseTest extends BriarTestCase {
public H2DatabaseTest() throws Exception {
groupId = new GroupId(TestUtils.getRandomId());
group = new Group(groupId, "Group name", null);
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
authorId = new AuthorId(TestUtils.getRandomId());
author = new Author(authorId, "Alice", new byte[60]);
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
localAuthorId = new AuthorId(TestUtils.getRandomId());
localAuthor = new LocalAuthor(localAuthorId, "Bob", new byte[60],
new byte[60]);
localAuthor = new LocalAuthor(localAuthorId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
messageId = new MessageId(TestUtils.getRandomId());
messageId1 = new MessageId(TestUtils.getRandomId());
contentType = "text/plain";
@@ -535,38 +537,28 @@ public class H2DatabaseTest extends BriarTestCase {
}
@Test
public void testGetUnrestrictedGroupMessages() throws Exception {
public void testGetGroupMessages() throws Exception {
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Bob", new byte[60]);
Author author1 = new Author(authorId1, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH]);
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new TestMessage(messageId1, null, group, author1,
contentType, subject, timestamp, raw);
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, "Restricted group name",
new byte[60]);
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
Message message2 = new TestMessage(messageId2, null, group1, author,
contentType, subject, timestamp, raw);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Subscribe to an unrestricted group and store two messages
// Subscribe to a group and store two messages
db.addSubscription(txn, group);
db.addGroupMessage(txn, message, false);
db.addGroupMessage(txn, message1, false);
// Subscribe to a restricted group and store a message
db.addSubscription(txn, group1);
db.addGroupMessage(txn, message2, false);
// Check that only the messages in the unrestricted group are retrieved
Collection<MessageId> ids = db.getUnrestrictedGroupMessages(txn,
authorId);
// Check that both messages are retrievable by their authors
Collection<MessageId> ids = db.getGroupMessages(txn, authorId);
Iterator<MessageId> it = ids.iterator();
assertTrue(it.hasNext());
assertEquals(messageId, it.next());
assertFalse(it.hasNext());
ids = db.getUnrestrictedGroupMessages(txn, authorId1);
ids = db.getGroupMessages(txn, authorId1);
it = ids.iterator();
assertTrue(it.hasNext());
assertEquals(messageId1, it.next());
@@ -582,7 +574,8 @@ public class H2DatabaseTest extends BriarTestCase {
MessageId childId2 = new MessageId(TestUtils.getRandomId());
MessageId childId3 = new MessageId(TestUtils.getRandomId());
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, "Group name", null);
Group group1 = new Group(groupId1, "Another group",
new byte[GROUP_SALT_LENGTH]);
Message child1 = new TestMessage(childId1, messageId, group, author,
contentType, subject, timestamp, raw);
Message child2 = new TestMessage(childId2, messageId, group, author,
@@ -1193,7 +1186,8 @@ public class H2DatabaseTest extends BriarTestCase {
public void testGetGroupMessageParentWithParentInAnotherGroup()
throws Exception {
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, "Group name", null);
Group group1 = new Group(groupId1, "Another group",
new byte[GROUP_SALT_LENGTH]);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
@@ -1446,7 +1440,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Subscribe to a couple of groups
db.addSubscription(txn, group);
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, "Group name", null);
Group group1 = new Group(groupId1, "Another group",
new byte[GROUP_SALT_LENGTH]);
db.addSubscription(txn, group1);
// Store two messages in the first group
@@ -1499,7 +1494,8 @@ public class H2DatabaseTest extends BriarTestCase {
List<Group> groups = new ArrayList<Group>();
for(int i = 0; i < 100; i++) {
GroupId id = new GroupId(TestUtils.getRandomId());
groups.add(new Group(id, "Group name", null));
String name = "Group " + i;
groups.add(new Group(id, name, new byte[GROUP_SALT_LENGTH]));
}
Database<Connection> db = open(false);
@@ -1834,7 +1830,8 @@ public class H2DatabaseTest extends BriarTestCase {
public void testGetAvailableGroups() throws Exception {
ContactId contactId1 = new ContactId(2);
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Carol", new byte[60]);
Author author1 = new Author(authorId1, "Carol",
new byte[MAX_PUBLIC_KEY_LENGTH]);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();

View File

@@ -96,7 +96,7 @@ public class ConstantsTest extends BriarTestCase {
byte[] publicKey = keyPair.getPublic().getEncoded();
assertTrue(publicKey.length <= MAX_PUBLIC_KEY_LENGTH);
// Sign some random data and check the length of the signature
byte[] toBeSigned = new byte[1000];
byte[] toBeSigned = new byte[1234];
random.nextBytes(toBeSigned);
sig.initSign(keyPair.getPrivate());
sig.update(toBeSigned);
@@ -120,23 +120,19 @@ public class ConstantsTest extends BriarTestCase {
MessageId parent = new MessageId(TestUtils.getRandomId());
// Create a maximum-length group
String groupName = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
Group group = groupFactory.createGroup(groupName, groupPublic);
Group group = groupFactory.createGroup(groupName);
// Create a maximum-length author
String authorName =
TestUtils.createRandomString(MAX_AUTHOR_NAME_LENGTH);
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
Author author = authorFactory.createAuthor(authorName, authorPublic);
// Create a maximum-length message
PrivateKey groupPrivate =
crypto.generateSignatureKeyPair().getPrivate();
PrivateKey authorPrivate =
crypto.generateSignatureKeyPair().getPrivate();
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
String contentType =
TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH);
byte[] body = new byte[MAX_BODY_LENGTH];
Message message = messageFactory.createPseudonymousMessage(parent,
group, groupPrivate, author, authorPrivate, contentType, body);
group, author, privateKey, contentType, body);
// Check the size of the serialised message
int length = message.getSerialised().length;
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
@@ -181,10 +177,8 @@ public class ConstantsTest extends BriarTestCase {
// Create the maximum number of maximum-length groups
Collection<Group> subs = new ArrayList<Group>();
for(int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
String groupName =
TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
subs.add(groupFactory.createGroup(groupName, groupPublic));
String name = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
subs.add(groupFactory.createGroup(name));
}
// Create a maximum-length subscription update
SubscriptionUpdate u = new SubscriptionUpdate(subs, Long.MAX_VALUE);

View File

@@ -1,5 +1,6 @@
package net.sf.briar.messaging.simplex;
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import java.io.ByteArrayInputStream;
@@ -108,11 +109,12 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Add a local pseudonym for Alice
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
new byte[60], new byte[60]);
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
db.addLocalAuthor(aliceAuthor);
// Add Bob as a contact
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
Author bobAuthor = new Author(bobId, "Bob", new byte[60]);
Author bobAuthor = new Author(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = db.addContact(bobAuthor, aliceId);
// Add the transport and the endpoint
db.addTransport(transportId, LATENCY);
@@ -161,12 +163,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
km.start();
// Add a local pseudonym for Bob
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob", new byte[60],
new byte[60]);
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
db.addLocalAuthor(bobAuthor);
// Add Alice as a contact
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
Author aliceAuthor = new Author(aliceId, "Alice", new byte[60]);
Author aliceAuthor = new Author(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = db.addContact(aliceAuthor, bobId);
// Add the transport and the endpoint
db.addTransport(transportId, LATENCY);

View File

@@ -263,13 +263,13 @@ public class RemovableDrivePluginTest extends BriarTestCase {
assertEquals(0, files[0].length());
// Writing to the output stream should increase the size of the file
OutputStream out = writer.getOutputStream();
out.write(new byte[123]);
out.write(new byte[1234]);
out.flush();
out.close();
// Disposing of the writer should not delete the file
writer.dispose(false);
assertTrue(files[0].exists());
assertEquals(123, files[0].length());
assertEquals(1234, files[0].length());
context.assertIsSatisfied();
}