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

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());