Store private keys for pseudonyms and restricted groups in the DB.

This commit is contained in:
akwizgran
2013-03-23 17:07:28 +00:00
parent 4e5366509d
commit 85700dc985
7 changed files with 310 additions and 30 deletions

View File

@@ -15,6 +15,8 @@ import net.sf.briar.api.db.PrivateMessageHeader;
import net.sf.briar.api.messaging.AuthorId;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.LocalAuthor;
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.RetentionAck;
@@ -40,6 +42,7 @@ import net.sf.briar.api.transport.TemporarySecret;
* deadlock, locks must be acquired in the following (alphabetical) order:
* <ul>
* <li> contact
* <li> identity
* <li> message
* <li> rating
* <li> retention
@@ -102,6 +105,21 @@ interface Database<T> {
*/
boolean addGroupMessage(T txn, Message m) throws DbException;
/**
* Stores a pseudonym that the user can use to sign messages.
* <p>
* Locking: identity write.
*/
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>
@@ -259,6 +277,20 @@ interface Database<T> {
*/
long getLastConnected(T txn, ContactId c) throws DbException;
/**
* Returns all pseudonyms that the user can use to sign messages.
* <p>
* Locking: identity read.
*/
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 the given transport.
* <p>

View File

@@ -61,6 +61,8 @@ import net.sf.briar.api.messaging.Author;
import net.sf.briar.api.messaging.AuthorId;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.LocalAuthor;
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;
@@ -97,6 +99,8 @@ DatabaseCleaner.Callback {
private final ReentrantReadWriteLock contactLock =
new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock identityLock =
new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock messageLock =
new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock ratingLock =
@@ -426,6 +430,38 @@ DatabaseCleaner.Callback {
return true;
}
public void addLocalAuthor(LocalAuthor a) throws DbException {
identityLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
db.addLocalAuthor(txn, a);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
identityLock.writeLock().unlock();
}
}
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 addSecrets(Collection<TemporarySecret> secrets)
throws DbException {
contactLock.readLock().lock();
@@ -911,6 +947,40 @@ DatabaseCleaner.Callback {
}
}
public Collection<LocalAuthor> getLocalAuthors() throws DbException {
identityLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
Collection<LocalAuthor> authors = db.getLocalAuthors(txn);
db.commitTransaction(txn);
return authors;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
identityLock.readLock().unlock();
}
}
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 TransportProperties getLocalProperties(TransportId t)
throws DbException {
transportLock.readLock().lock();

View File

@@ -41,6 +41,8 @@ import net.sf.briar.api.messaging.Author;
import net.sf.briar.api.messaging.AuthorId;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.LocalAuthor;
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.RetentionAck;
@@ -60,10 +62,28 @@ import net.sf.briar.util.FileUtils;
*/
abstract class JdbcDatabase implements Database<Connection> {
// Locking: identity
private static final String CREATE_LOCAL_AUTHORS =
"CREATE TABLE localAuthors"
+ " (authorId HASH NOT NULL,"
+ " name VARCHAR NOT NULL,"
+ " publicKey BINARY NOT NULL,"
+ " 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 =
"CREATE TABLE contacts "
"CREATE TABLE contacts"
+ " (contactId COUNTER,"
+ " name VARCHAR NOT NULL,"
+ " PRIMARY KEY (contactId))";
@@ -74,7 +94,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE groups"
+ " (groupId HASH NOT NULL,"
+ " name VARCHAR NOT NULL,"
+ " key BINARY," // Null for unrestricted groups
+ " publicKey BINARY," // Null for unrestricted groups
+ " PRIMARY KEY (groupId))";
// Locking: subscription
@@ -95,7 +115,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (contactId INT NOT NULL,"
+ " groupId HASH NOT NULL," // Not a foreign key
+ " name VARCHAR NOT NULL,"
+ " key BINARY," // Null for unrestricted groups
+ " publicKey BINARY," // Null for unrestricted groups
+ " PRIMARY KEY (contactId, groupId),"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
@@ -378,6 +398,8 @@ abstract class JdbcDatabase implements Database<Connection> {
Statement s = null;
try {
s = txn.createStatement();
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
s.executeUpdate(insertTypeNames(CREATE_LOCAL_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
@@ -672,6 +694,48 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void addLocalAuthor(Connection txn, LocalAuthor a)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO localAuthors"
+ " (authorId, name, publicKey, privateKey)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getId().getBytes());
ps.setString(2, a.getName());
ps.setBytes(3, a.getPublicKey());
ps.setBytes(4, a.getPrivateKey());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
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;
@@ -819,7 +883,8 @@ 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, key) VALUES (?, ?, ?)";
sql = "INSERT INTO groups (groupId, name, publicKey)"
+ " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes());
ps.setString(2, g.getName());
@@ -1154,7 +1219,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT name, key FROM groups WHERE groupId = ?";
String sql = "SELECT name, publicKey FROM groups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
@@ -1222,6 +1287,56 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<LocalAuthor> getLocalAuthors(Connection txn)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT authorId, name, publicKey, privateKey"
+ " FROM localAuthors";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
List<LocalAuthor> authors = new ArrayList<LocalAuthor>();
while(rs.next()) {
AuthorId id = new AuthorId(rs.getBytes(1));
authors.add(new LocalAuthor(id, rs.getString(2), rs.getBytes(3),
rs.getBytes(4)));
}
rs.close();
ps.close();
return Collections.unmodifiableList(authors);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
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 id = new GroupId(rs.getBytes(1));
groups.add(new LocalGroup(id, rs.getString(2), rs.getBytes(3),
rs.getBytes(4)));
}
rs.close();
ps.close();
return Collections.unmodifiableList(groups);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public TransportProperties getLocalProperties(Connection txn, TransportId t)
throws DbException {
PreparedStatement ps = null;
@@ -1965,7 +2080,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, name, key FROM groups";
String sql = "SELECT groupId, name, publicKey FROM groups";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
List<Group> subs = new ArrayList<Group>();
@@ -1990,7 +2105,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, name, key FROM contactGroups"
String sql = "SELECT groupId, name, publicKey FROM contactGroups"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
@@ -2052,7 +2167,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT g.groupId, name, key, localVersion, txCount"
String sql = "SELECT g.groupId, name, publicKey,"
+ " localVersion, txCount"
+ " FROM groups AS g"
+ " JOIN groupVisibilities AS vis"
+ " ON g.groupId = vis.groupId"
@@ -3023,7 +3139,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.executeUpdate();
// Store the new subscriptions, if any
if(subs.isEmpty()) return;
sql = "INSERT INTO contactGroups (contactId, groupId, name, key)"
sql = "INSERT INTO contactGroups"
+ " (contactId, groupId, name, publicKey)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());