diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java index 15be93c50..b03eb316a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java @@ -619,6 +619,12 @@ public interface DatabaseComponent { void addMessageDependencies(Transaction txn, Message dependent, Collection dependencies) throws DbException; + /** + * Sets the handshake key pair for the local pseudonym with the given ID. + */ + void setHandshakeKeyPair(Transaction txn, AuthorId local, byte[] publicKey, + byte[] privateKey) throws DbException; + /** * Sets the reordering window for the given transport key set in the given * time period. diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java index cdad152ad..5652a1e89 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java @@ -685,6 +685,12 @@ interface Database { void setGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) throws DbException; + /** + * Sets the handshake key pair for the local pseudonym with the given ID. + */ + void setHandshakeKeyPair(T txn, AuthorId local, byte[] publicKey, + byte[] privateKey) throws DbException; + /** * Marks the given message as shared. */ diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java index 17c3395b1..ea7973a48 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java @@ -1035,6 +1035,16 @@ class DatabaseComponentImpl implements DatabaseComponent { } } + @Override + public void setHandshakeKeyPair(Transaction transaction, AuthorId local, + byte[] publicKey, byte[] privateKey) throws DbException { + if (transaction.isReadOnly()) throw new IllegalArgumentException(); + T txn = unbox(transaction); + if (!db.containsLocalAuthor(txn, local)) + throw new NoSuchLocalAuthorException(); + db.setHandshakeKeyPair(txn, local, publicKey, privateKey); + } + @Override public void setReorderingWindow(Transaction transaction, TransportKeySetId k, TransportId t, long timePeriod, long base, diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index 020805c59..e87c0b34d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -1681,10 +1681,18 @@ abstract class JdbcDatabase implements Database { byte[] handshakePublicKey = rs.getBytes(5); byte[] handshakePrivateKey = rs.getBytes(6); long created = rs.getLong(7); - LocalAuthor localAuthor = new LocalAuthor(a, formatVersion, name, - publicKey, privateKey, handshakePublicKey, - handshakePrivateKey, created); if (rs.next()) throw new DbStateException(); + LocalAuthor localAuthor; + if (handshakePublicKey == null) { + if (handshakePrivateKey != null) throw new DbStateException(); + localAuthor = new LocalAuthor(a, formatVersion, name, + publicKey, privateKey, created); + } else { + if (handshakePrivateKey == null) throw new DbStateException(); + localAuthor = new LocalAuthor(a, formatVersion, name, + publicKey, privateKey, handshakePublicKey, + handshakePrivateKey, created); + } rs.close(); ps.close(); return localAuthor; @@ -3180,6 +3188,27 @@ abstract class JdbcDatabase implements Database { } } + @Override + public void setHandshakeKeyPair(Connection txn, AuthorId local, + byte[] publicKey, byte[] privateKey) throws DbException { + PreparedStatement ps = null; + try { + String sql = "UPDATE localAuthors" + + " SET handshakePublicKey = ?, handshakePrivateKey = ?" + + " WHERE authorId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, publicKey); + ps.setBytes(2, privateKey); + ps.setBytes(3, local.getBytes()); + int affected = ps.executeUpdate(); + if (affected < 0 || affected > 1) throw new DbStateException(); + ps.close(); + } catch (SQLException e) { + tryToClose(ps, LOG, WARNING); + throw new DbException(e); + } + } + @Override public void setMessageShared(Connection txn, MessageId m) throws DbException { diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java index b74260e32..c7cccf1bf 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java @@ -65,6 +65,7 @@ import java.util.concurrent.atomic.AtomicReference; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; +import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; @@ -79,6 +80,7 @@ import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getMessage; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getTransportId; @@ -436,12 +438,12 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { throws Exception { context.checking(new Expectations() {{ // Check whether the pseudonym is in the DB (which it's not) - exactly(3).of(database).startTransaction(); + exactly(4).of(database).startTransaction(); will(returnValue(txn)); - exactly(3).of(database).containsLocalAuthor(txn, + exactly(4).of(database).containsLocalAuthor(txn, localAuthor.getId()); will(returnValue(false)); - exactly(3).of(database).abortTransaction(txn); + exactly(4).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, eventBus, eventExecutor, shutdownManager); @@ -470,6 +472,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { } catch (NoSuchLocalAuthorException expected) { // Expected } + + try { + byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES); + byte[] privateKey = getRandomBytes(123); + db.transaction(false, transaction -> + db.setHandshakeKeyPair(transaction, localAuthor.getId(), + publicKey, privateKey)); + fail(); + } catch (NoSuchLocalAuthorException expected) { + // Expected + } } @Test diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java index 6daece520..0dacd18af 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java @@ -57,6 +57,7 @@ import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static java.util.concurrent.TimeUnit.SECONDS; import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; +import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES; import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; @@ -76,6 +77,7 @@ import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getPendingContact; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getTestDirectory; @@ -2237,6 +2239,29 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { db.close(); } + @Test + public void testSetHandshakeKeyPair() throws Exception { + assertNull(localAuthor.getHandshakePublicKey()); + assertNull(localAuthor.getHandshakePrivateKey()); + byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES); + byte[] privateKey = getRandomBytes(123); + + Database db = open(false); + Connection txn = db.startTransaction(); + + db.addLocalAuthor(txn, localAuthor); + LocalAuthor retrieved = db.getLocalAuthor(txn, localAuthor.getId()); + assertNull(retrieved.getHandshakePublicKey()); + assertNull(retrieved.getHandshakePrivateKey()); + db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey); + retrieved = db.getLocalAuthor(txn, localAuthor.getId()); + assertArrayEquals(publicKey, retrieved.getHandshakePublicKey()); + assertArrayEquals(privateKey, retrieved.getHandshakePrivateKey()); + + db.commitTransaction(txn); + db.close(); + } + private Database open(boolean resume) throws Exception { return open(resume, new TestMessageFactory(), new SystemClock()); }