Initial implementation of social backup client.

This commit is contained in:
akwizgran
2021-02-23 15:48:19 +00:00
parent f160efb0e7
commit 513e696238
42 changed files with 1207 additions and 4 deletions

View File

@@ -1,12 +1,11 @@
package org.briarproject.bramble.crypto;
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
@NotNullByDefault
interface AuthenticatedCipher {
public interface AuthenticatedCipher {
/**
* Initializes this cipher for encryption or decryption with a key and an

View File

@@ -26,6 +26,11 @@ public interface IdentityManager {
*/
void registerIdentity(Identity i);
/**
* Returns the cached local identity or loads it from the database.
*/
Identity getIdentity(Transaction txn) throws DbException;
/**
* Returns the cached local identity or loads it from the database.
*/

View File

@@ -74,6 +74,13 @@ public interface TransportPropertyManager {
TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException;
/**
* Returns the remote transport properties for the given contact and
* transport.
*/
TransportProperties getRemoteProperties(Transaction txn, ContactId c,
TransportId t) throws DbException;
/**
* Merges the given properties with the existing local properties for the
* given transport.

View File

@@ -6,6 +6,7 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyPair;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamDecrypter;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamDecrypter;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypter;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypter;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.DataLengthException;

View File

@@ -118,6 +118,11 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
return cached.getLocalAuthor();
}
@Override
public Identity getIdentity(Transaction txn) throws DbException {
return getCachedIdentity(txn);
}
@Override
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
return getCachedIdentity(txn).getLocalAuthor();

View File

@@ -294,7 +294,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException {
return db.transactionWithResult(true, txn ->
getRemoteProperties(txn, db.getContact(txn, c), t));
getRemoteProperties(txn, c, t));
}
@Override
public TransportProperties getRemoteProperties(Transaction txn,
ContactId c, TransportId t) throws DbException {
return getRemoteProperties(txn, db.getContact(txn, c), t);
}
@Override

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.util.StringUtils;

View File

@@ -0,0 +1,11 @@
package org.briarproject.briar.api.socialbackup;
import org.briarproject.bramble.api.db.DbException;
/**
* Thrown when an attempt is made to create a social account backup but a
* backup already exists. This exception may occur due to concurrent updates
* and does not indicate a database error.
*/
public class BackupExistsException extends DbException {
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.briar.api.socialbackup;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.List;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class BackupMetadata {
private final SecretKey secret;
private final List<Author> custodians;
private final int threshold, version;
public BackupMetadata(SecretKey secret, List<Author> custodians,
int threshold, int version) {
this.secret = secret;
this.custodians = custodians;
this.threshold = threshold;
this.version = version;
}
public SecretKey getSecret() {
return secret;
}
public List<Author> getCustodians() {
return custodians;
}
public int getThreshold() {
return threshold;
}
public int getVersion() {
return version;
}
}

View File

@@ -0,0 +1,37 @@
package org.briarproject.briar.api.socialbackup;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class Shard {
private final byte[] secretId, shard;
private final int numShards, threshold;
public Shard(byte[] secretId, int numShards, int threshold,
byte[] shard) {
this.secretId = secretId;
this.numShards = numShards;
this.threshold = threshold;
this.shard = shard;
}
public byte[] getSecretId() {
return secretId;
}
public int getNumShards() {
return numShards;
}
public int getThreshold() {
return threshold;
}
public byte[] getShard() {
return shard;
}
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.briar.api.socialbackup;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import java.util.List;
import javax.annotation.Nullable;
@NotNullByDefault
public interface SocialBackupManager {
/**
* The unique ID of the social backup client.
*/
ClientId CLIENT_ID = new ClientId("pw.darkcrystal.backup");
/**
* The current major version of the social backup client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the social backup client.
*/
int MINOR_VERSION = 0;
/**
* Returns the metadata for this device's backup, or null if no backup has
* been created.
*/
@Nullable
BackupMetadata getBackupMetadata(Transaction txn) throws DbException;
/**
* Creates a backup for this device using the given custodians and
* threshold. The encrypted backup and a shard of the backup key will be
* sent to each custodian.
*
* @throws BackupExistsException If a backup already exists
*/
void createBackup(Transaction txn, List<ContactId> custodianIds,
int threshold) throws DbException;
}

View File

@@ -10,6 +10,7 @@ import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
import org.briarproject.briar.privategroup.invitation.GroupInvitationModule;
import org.briarproject.briar.sharing.SharingModule;
import org.briarproject.briar.socialbackup.SocialBackupModule;
public interface BriarCoreEagerSingletons {
@@ -33,6 +34,8 @@ public interface BriarCoreEagerSingletons {
void inject(SharingModule.EagerSingletons init);
void inject(SocialBackupModule.EagerSingletons init);
class Helper {
public static void injectEagerSingletons(BriarCoreEagerSingletons c) {
@@ -46,6 +49,7 @@ public interface BriarCoreEagerSingletons {
c.inject(new SharingModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons());
c.inject(new IntroductionModule.EagerSingletons());
c.inject(new SocialBackupModule.EagerSingletons());
}
}
}

View File

@@ -13,6 +13,7 @@ import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
import org.briarproject.briar.privategroup.invitation.GroupInvitationModule;
import org.briarproject.briar.sharing.SharingModule;
import org.briarproject.briar.socialbackup.SocialBackupModule;
import org.briarproject.briar.test.TestModule;
import dagger.Module;
@@ -31,6 +32,7 @@ import dagger.Module;
MessagingModule.class,
PrivateGroupModule.class,
SharingModule.class,
SocialBackupModule.class,
TestModule.class
})
public class BriarCoreModule {

View File

@@ -0,0 +1,11 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.BackupMetadata;
@NotNullByDefault
interface BackupMetadataEncoder {
BdfDictionary encodeBackupMetadata(BackupMetadata backupMetadata);
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.BackupMetadata;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_CUSTODIANS;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_SECRET;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_THRESHOLD;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_VERSION;
@Immutable
@NotNullByDefault
class BackupMetadataEncoderImpl implements BackupMetadataEncoder {
private final ClientHelper clientHelper;
@Inject
BackupMetadataEncoderImpl(ClientHelper clientHelper) {
this.clientHelper = clientHelper;
}
@Override
public BdfDictionary encodeBackupMetadata(BackupMetadata backupMetadata) {
BdfList custodians = new BdfList();
for (Author custodian : backupMetadata.getCustodians()) {
custodians.add(clientHelper.toList(custodian));
}
BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_SECRET, backupMetadata.getSecret().getBytes());
meta.put(GROUP_KEY_CUSTODIANS, custodians);
meta.put(GROUP_KEY_THRESHOLD, backupMetadata.getThreshold());
meta.put(GROUP_KEY_VERSION, backupMetadata.getVersion());
return meta;
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.BackupMetadata;
import javax.annotation.Nullable;
@NotNullByDefault
interface BackupMetadataParser {
@Nullable
BackupMetadata parseBackupMetadata(BdfDictionary meta)
throws FormatException;
}

View File

@@ -0,0 +1,51 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.BackupMetadata;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_CUSTODIANS;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_SECRET;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_THRESHOLD;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.GROUP_KEY_VERSION;
@Immutable
@NotNullByDefault
class BackupMetadataParserImpl implements BackupMetadataParser {
private final ClientHelper clientHelper;
@Inject
BackupMetadataParserImpl(ClientHelper clientHelper) {
this.clientHelper = clientHelper;
}
@Nullable
@Override
public BackupMetadata parseBackupMetadata(BdfDictionary meta)
throws FormatException {
if (meta.isEmpty()) return null;
SecretKey secret = new SecretKey(meta.getRaw(GROUP_KEY_SECRET));
BdfList bdfCustodians = meta.getList(GROUP_KEY_CUSTODIANS);
List<Author> custodians = new ArrayList<>(bdfCustodians.size());
for (int i = 0; i < bdfCustodians.size(); i++) {
BdfList author = bdfCustodians.getList(i);
custodians.add(clientHelper.parseAndValidateAuthor(author));
}
int threshold = meta.getLong(GROUP_KEY_THRESHOLD).intValue();
int version = meta.getLong(GROUP_KEY_VERSION).intValue();
return new BackupMetadata(secret, custodians, threshold, version);
}
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class BackupPayload extends Bytes {
BackupPayload(byte[] payload) {
super(payload);
}
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.util.List;
import java.util.Map;
@NotNullByDefault
interface BackupPayloadEncoder {
BackupPayload encodeBackupPayload(SecretKey secret, Identity identity,
List<Contact> contacts,
List<Map<TransportId, TransportProperties>> properties,
int version);
}

View File

@@ -0,0 +1,94 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Provider;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.AUTH_TAG_BYTES;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.NONCE_BYTES;
@Immutable
@NotNullByDefault
class BackupPayloadEncoderImpl implements BackupPayloadEncoder {
private final ClientHelper clientHelper;
private final Provider<AuthenticatedCipher> cipherProvider;
private final SecureRandom secureRandom;
@Inject
BackupPayloadEncoderImpl(ClientHelper clientHelper,
Provider<AuthenticatedCipher> cipherProvider,
SecureRandom secureRandom) {
this.clientHelper = clientHelper;
this.cipherProvider = cipherProvider;
this.secureRandom = secureRandom;
}
@Override
public BackupPayload encodeBackupPayload(SecretKey secret,
Identity identity, List<Contact> contacts,
List<Map<TransportId, TransportProperties>> properties,
int version) {
if (contacts.size() != properties.size()) {
throw new IllegalArgumentException();
}
// Encode the local identity
BdfList identityData = new BdfList();
LocalAuthor localAuthor = identity.getLocalAuthor();
identityData.add(clientHelper.toList(localAuthor));
identityData.add(localAuthor.getPrivateKey().getEncoded());
identityData.add(identity.getHandshakePublicKey().getEncoded());
identityData.add(identity.getHandshakePrivateKey().getEncoded());
// Encode the contacts
BdfList contactData = new BdfList();
for (int i = 0; i < contacts.size(); i++) {
Contact contact = contacts.get(i);
Map<TransportId, TransportProperties> props = properties.get(i);
BdfList data = new BdfList();
data.add(clientHelper.toList(contact.getAuthor()));
data.add(contact.getAlias());
PublicKey pub = requireNonNull(contact.getHandshakePublicKey());
data.add(pub.getEncoded());
data.add(clientHelper.toDictionary(props));
contactData.add(data);
}
// Encode and encrypt the payload
BdfList backup = new BdfList();
backup.add(version);
backup.add(identityData);
backup.add(contactData);
try {
byte[] plaintext = clientHelper.toByteArray(backup);
byte[] ciphertext = new byte[plaintext.length + AUTH_TAG_BYTES];
byte[] nonce = new byte[NONCE_BYTES];
secureRandom.nextBytes(nonce);
AuthenticatedCipher cipher = cipherProvider.get();
cipher.init(true, secret, nonce);
int encrypted = cipher.process(plaintext, 0, plaintext.length,
ciphertext, 0);
if (encrypted != ciphertext.length) throw new AssertionError();
return new BackupPayload(ciphertext);
} catch (FormatException | GeneralSecurityException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,13 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.Shard;
import java.util.List;
@NotNullByDefault
interface DarkCrystal {
List<Shard> createShards(SecretKey secret, int shards, int threshold);
}

View File

@@ -0,0 +1,36 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.Shard;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.inject.Inject;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.SECRET_ID_BYTES;
@NotNullByDefault
class DarkCrystalStub implements DarkCrystal {
@Inject
DarkCrystalStub() {
}
@Override
public List<Shard> createShards(SecretKey secret, int numShards,
int threshold) {
Random random = new Random();
byte[] secretId = new byte[SECRET_ID_BYTES];
random.nextBytes(secretId);
List<Shard> shards = new ArrayList<>(numShards);
for (int i = 0; i < numShards; i++) {
byte[] shard = new byte[123];
random.nextBytes(shard);
shards.add(new Shard(secretId, numShards, threshold, shard));
}
return shards;
}
}

View File

@@ -0,0 +1,12 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.Shard;
@NotNullByDefault
interface MessageEncoder {
byte[] encodeShardMessage(Shard shard);
byte[] encodeBackupMessage(int version, BackupPayload payload);
}

View File

@@ -0,0 +1,55 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.Shard;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.socialbackup.MessageType.BACKUP;
import static org.briarproject.briar.socialbackup.MessageType.SHARD;
@Immutable
@NotNullByDefault
class MessageEncoderImpl implements MessageEncoder {
private final ClientHelper clientHelper;
@Inject
MessageEncoderImpl(ClientHelper clientHelper) {
this.clientHelper = clientHelper;
}
@Override
public byte[] encodeShardMessage(Shard shard) {
BdfList body = BdfList.of(
SHARD.getValue(),
shard.getSecretId(),
shard.getNumShards(),
shard.getThreshold(),
shard.getShard()
);
return encodeBody(body);
}
@Override
public byte[] encodeBackupMessage(int version, BackupPayload payload) {
BdfList body = BdfList.of(
BACKUP.getValue(),
version,
payload.getBytes()
);
return encodeBody(body);
}
private byte[] encodeBody(BdfList body) {
try {
return clientHelper.toByteArray(body);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,13 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.Shard;
@NotNullByDefault
interface MessageParser {
Shard parseShardMessage(byte[] body) throws FormatException;
BackupPayload parseBackupMessage(byte[] body) throws FormatException;
}

View File

@@ -0,0 +1,41 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.socialbackup.Shard;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class MessageParserImpl implements MessageParser {
private final ClientHelper clientHelper;
@Inject
MessageParserImpl(ClientHelper clientHelper) {
this.clientHelper = clientHelper;
}
@Override
public Shard parseShardMessage(byte[] body) throws FormatException {
BdfList list = clientHelper.toList(body);
// Message type, secret ID, num shards, threshold, shard
byte[] secretId = list.getRaw(1);
int numShards = list.getLong(2).intValue();
int threshold = list.getLong(3).intValue();
byte[] shard = list.getRaw(4);
return new Shard(secretId, numShards, threshold, shard);
}
@Override
public BackupPayload parseBackupMessage(byte[] body)
throws FormatException {
BdfList list = clientHelper.toList(body);
// Message type, backup payload
return new BackupPayload(list.getRaw(1));
}
}

View File

@@ -0,0 +1,28 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
enum MessageType {
SHARD(0), BACKUP(1);
private final int value;
MessageType(int value) {
this.value = value;
}
int getValue() {
return value;
}
static MessageType fromValue(int value) throws FormatException {
for (MessageType m : values()) if (m.value == value) return m;
throw new FormatException();
}
}

View File

@@ -0,0 +1,35 @@
package org.briarproject.briar.socialbackup;
interface SocialBackupConstants {
// Group metadata keys
String GROUP_KEY_SECRET = "secret";
String GROUP_KEY_CUSTODIANS = "custodians";
String GROUP_KEY_THRESHOLD = "threshold";
String GROUP_KEY_VERSION = "version";
// Message metadata keys
String MSG_KEY_MESSAGE_TYPE = "messageType";
String MSG_KEY_LOCAL = "local";
String MSG_KEY_VERSION = "version";
/**
* The length of the authenticated cipher's nonce in bytes.
*/
int NONCE_BYTES = 24;
/**
* The length of the authenticated cipher's authentication tag in bytes.
*/
int AUTH_TAG_BYTES = 16;
/**
* The length of the secret ID in bytes.
*/
int SECRET_ID_BYTES = 32;
/**
* The maximum length of a shard in bytes.
*/
int MAX_SHARD_BYTES = 1024;
}

View File

@@ -0,0 +1,287 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfIncomingMessageHook;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.socialbackup.BackupExistsException;
import org.briarproject.briar.api.socialbackup.BackupMetadata;
import org.briarproject.briar.api.socialbackup.Shard;
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.Collections.singletonMap;
import static org.briarproject.briar.socialbackup.MessageType.BACKUP;
import static org.briarproject.briar.socialbackup.MessageType.SHARD;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_VERSION;
@NotNullByDefault
class SocialBackupManagerImpl extends BdfIncomingMessageHook
implements SocialBackupManager, OpenDatabaseHook, ContactHook,
ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final TransportPropertyManager transportPropertyManager;
private final ContactGroupFactory contactGroupFactory;
private final BackupMetadataParser backupMetadataParser;
private final BackupMetadataEncoder backupMetadataEncoder;
private final BackupPayloadEncoder backupPayloadEncoder;
private final MessageEncoder messageEncoder;
private final IdentityManager identityManager;
private final ContactManager contactManager;
private final CryptoComponent crypto;
private final DarkCrystal darkCrystal;
private final Clock clock;
private final Group localGroup;
@Inject
SocialBackupManagerImpl(
DatabaseComponent db,
ClientHelper clientHelper,
MetadataParser metadataParser,
ClientVersioningManager clientVersioningManager,
TransportPropertyManager transportPropertyManager,
ContactGroupFactory contactGroupFactory,
BackupMetadataParser backupMetadataParser,
BackupMetadataEncoder backupMetadataEncoder,
BackupPayloadEncoder backupPayloadEncoder,
MessageEncoder messageEncoder,
IdentityManager identityManager,
ContactManager contactManager,
CryptoComponent crypto,
DarkCrystal darkCrystal,
Clock clock) {
super(db, clientHelper, metadataParser);
this.clientVersioningManager = clientVersioningManager;
this.transportPropertyManager = transportPropertyManager;
this.contactGroupFactory = contactGroupFactory;
this.backupMetadataParser = backupMetadataParser;
this.backupMetadataEncoder = backupMetadataEncoder;
this.backupPayloadEncoder = backupPayloadEncoder;
this.messageEncoder = messageEncoder;
this.identityManager = identityManager;
this.contactManager = contactManager;
this.crypto = crypto;
this.darkCrystal = darkCrystal;
localGroup =
contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
this.clock = clock;
}
@Override
public void onDatabaseOpened(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup);
// Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override
public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group g = getContactGroup(c);
db.addGroup(txn, g);
// Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// TODO: Add the contact to our backup, if any
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c));
// TODO: Remove the contact from our backup, if any
}
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
@Override
protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary meta) throws DbException, FormatException {
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
if (type == SHARD) {
// TODO: Add the shard to our backup, if any
} else if (type == BACKUP) {
// Keep the newest version of the backup, delete any older versions
int version = meta.getLong(MSG_KEY_VERSION).intValue();
BdfDictionary query = BdfDictionary.of(
new BdfEntry(MSG_KEY_MESSAGE_TYPE, BACKUP.getValue()),
new BdfEntry(MSG_KEY_LOCAL, false));
Map<MessageId, BdfDictionary> results =
clientHelper.getMessageMetadataAsDictionary(txn,
m.getGroupId(), query);
if (results.size() > 1) throw new DbException();
for (Entry<MessageId, BdfDictionary> e : results.entrySet()) {
MessageId prevId = e.getKey();
BdfDictionary prevMeta = e.getValue();
int prevVersion = prevMeta.getLong(MSG_KEY_VERSION).intValue();
if (version > prevVersion) {
// This backup is newer - delete the previous backup
db.deleteMessage(txn, prevId);
db.deleteMessageMetadata(txn, prevId);
} else {
// We've already received a newer backup - delete this one
db.deleteMessage(txn, m.getId());
db.deleteMessageMetadata(txn, m.getId());
}
}
}
return false;
}
@Nullable
@Override
public BackupMetadata getBackupMetadata(Transaction txn)
throws DbException {
try {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn,
localGroup.getId());
return backupMetadataParser.parseBackupMetadata(meta);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public void createBackup(Transaction txn, List<ContactId> custodianIds,
int threshold) throws DbException {
if (getBackupMetadata(txn) != null) throw new BackupExistsException();
// Load the contacts
List<Contact> custodians = new ArrayList<>(custodianIds.size());
for (ContactId custodianId : custodianIds) {
custodians.add(contactManager.getContact(txn, custodianId));
}
// Create the encrypted backup payload
SecretKey secret = crypto.generateSecretKey();
BackupPayload payload = createBackupPayload(txn, secret, 0);
// Create the shards
List<Shard> shards = darkCrystal.createShards(secret,
custodians.size(), threshold);
try {
// Send the shard and backup messages to the custodians
for (int i = 0; i < custodians.size(); i++) {
Contact custodian = custodians.get(i);
Shard shard = shards.get(i);
sendShardMessage(txn, custodian, shard);
sendBackupMessage(txn, custodian, 0, payload);
}
// Store the backup metadata
List<Author> authors = new ArrayList<>(custodians.size());
for (Contact custodian : custodians) {
authors.add(custodian.getAuthor());
}
BackupMetadata backupMetadata =
new BackupMetadata(secret, authors, threshold, 0);
BdfDictionary meta =
backupMetadataEncoder.encodeBackupMetadata(backupMetadata);
clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c);
}
private BackupPayload createBackupPayload(Transaction txn,
SecretKey secret, int version) throws DbException {
Identity identity = identityManager.getIdentity(txn);
Collection<Contact> contacts = contactManager.getContacts(txn);
List<Contact> eligible = new ArrayList<>();
List<Map<TransportId, TransportProperties>> properties =
new ArrayList<>();
// Include all contacts whose handshake public keys we know
for (Contact c : contacts) {
if (c.getHandshakePublicKey() != null) {
eligible.add(c);
properties.add(getTransportProperties(txn, c.getId()));
// TODO: Include shard received from contact, if any
}
}
return backupPayloadEncoder.encodeBackupPayload(secret, identity,
eligible, properties, version);
}
private Map<TransportId, TransportProperties> getTransportProperties(
Transaction txn, ContactId c) throws DbException {
// TODO: Include filtered properties for other transports
TransportProperties p = transportPropertyManager
.getRemoteProperties(txn, c, TorConstants.ID);
return singletonMap(TorConstants.ID, p);
}
private void sendShardMessage(Transaction txn, Contact custodian,
Shard shard) throws DbException, FormatException {
GroupId g = getContactGroup(custodian).getId();
long timestamp = clock.currentTimeMillis();
byte[] body = messageEncoder.encodeShardMessage(shard);
Message m = clientHelper.createMessage(g, timestamp, body);
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_MESSAGE_TYPE, SHARD.getValue()),
new BdfEntry(MSG_KEY_LOCAL, true));
clientHelper.addLocalMessage(txn, m, meta, true, false);
}
private void sendBackupMessage(Transaction txn, Contact custodian,
int version, BackupPayload payload)
throws DbException, FormatException {
GroupId g = getContactGroup(custodian).getId();
long timestamp = clock.currentTimeMillis();
byte[] body = messageEncoder.encodeBackupMessage(version, payload);
Message m = clientHelper.createMessage(g, timestamp, body);
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_MESSAGE_TYPE, BACKUP.getValue()),
new BdfEntry(MSG_KEY_LOCAL, true),
new BdfEntry(MSG_KEY_VERSION, version));
clientHelper.addLocalMessage(txn, m, meta, true, false);
}
}

View File

@@ -0,0 +1,96 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.briar.api.socialbackup.SocialBackupManager.CLIENT_ID;
import static org.briarproject.briar.api.socialbackup.SocialBackupManager.MAJOR_VERSION;
import static org.briarproject.briar.api.socialbackup.SocialBackupManager.MINOR_VERSION;
@Module
public class SocialBackupModule {
public static class EagerSingletons {
@Inject
SocialBackupManager socialBackupManager;
@Inject
SocialBackupValidator socialBackupValidator;
}
@Provides
@Singleton
SocialBackupManager socialBackupManager(
LifecycleManager lifecycleManager,
ContactManager contactManager,
ValidationManager validationManager,
ClientVersioningManager clientVersioningManager,
SocialBackupManagerImpl socialBackupManager) {
lifecycleManager.registerOpenDatabaseHook(socialBackupManager);
contactManager.registerContactHook(socialBackupManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
MAJOR_VERSION, socialBackupManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, socialBackupManager);
return socialBackupManager;
}
@Provides
@Singleton
SocialBackupValidator socialBackupValidator(
ValidationManager validationManager,
ClientHelper clientHelper,
MetadataEncoder metadataEncoder,
Clock clock) {
SocialBackupValidator validator =
new SocialBackupValidator(clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator;
}
@Provides
BackupMetadataParser backupMetadataParser(
BackupMetadataParserImpl backupMetadataParser) {
return backupMetadataParser;
}
@Provides
BackupMetadataEncoder backupMetadataEncoder(
BackupMetadataEncoderImpl backupMetadataEncoder) {
return backupMetadataEncoder;
}
@Provides
BackupPayloadEncoder backupPayloadEncoder(
BackupPayloadEncoderImpl backupPayloadEncoder) {
return backupPayloadEncoder;
}
@Provides
MessageEncoder messageEncoder(MessageEncoderImpl messageEncoder) {
return messageEncoder;
}
@Provides
MessageParser messageParser(MessageParserImpl messageParser) {
return messageParser;
}
@Provides
DarkCrystal darkCrystal(DarkCrystalStub darkCrystal) {
// TODO: Replace this with a real implementation
return darkCrystal;
}
}

View File

@@ -0,0 +1,80 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.BdfMessageValidator;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.socialbackup.MessageType.BACKUP;
import static org.briarproject.briar.socialbackup.MessageType.SHARD;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MAX_SHARD_BYTES;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.MSG_KEY_VERSION;
import static org.briarproject.briar.socialbackup.SocialBackupConstants.SECRET_ID_BYTES;
@Immutable
@NotNullByDefault
class SocialBackupValidator extends BdfMessageValidator {
@Inject
SocialBackupValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) {
super(clientHelper, metadataEncoder, clock);
}
@Override
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
if (type == SHARD) return validateShardMessage(body);
else if (type == BACKUP) return validateBackupMessage(body);
else throw new AssertionError();
}
private BdfMessageContext validateShardMessage(BdfList body)
throws FormatException {
// Message type, secret ID, num shards, threshold, shard
checkSize(body, 5);
byte[] secretId = body.getRaw(1);
checkLength(secretId, SECRET_ID_BYTES);
int numShards = body.getLong(2).intValue();
if (numShards < 2) throw new FormatException();
int threshold = body.getLong(3).intValue();
if (threshold < 2) throw new FormatException();
if (threshold > numShards) throw new FormatException();
byte[] shard = body.getRaw(4);
checkLength(shard, 1, MAX_SHARD_BYTES);
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_MESSAGE_TYPE, SHARD.getValue()),
new BdfEntry(MSG_KEY_LOCAL, false));
return new BdfMessageContext(meta);
}
private BdfMessageContext validateBackupMessage(BdfList body)
throws FormatException {
// Message type, version, backup payload
checkSize(body, 3);
int version = body.getLong(1).intValue();
if (version < 0) throw new FormatException();
byte[] payload = body.getRaw(2);
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_MESSAGE_TYPE, BACKUP.getValue()),
new BdfEntry(MSG_KEY_LOCAL, false),
new BdfEntry(MSG_KEY_VERSION, version));
return new BdfMessageContext(meta);
}
}

View File

@@ -0,0 +1,75 @@
package org.briarproject.briar.socialbackup;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.test.TestDatabaseConfigModule;
import org.briarproject.briar.api.socialbackup.BackupMetadata;
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
import org.briarproject.briar.test.BriarIntegrationTest;
import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class SocialBackupIntegrationTest
extends BriarIntegrationTest<BriarIntegrationTestComponent> {
private SocialBackupManager socialBackupManager0;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
socialBackupManager0 = c0.getSocialBackupManager();
}
@Override
protected void createComponents() {
BriarIntegrationTestComponent component =
DaggerBriarIntegrationTestComponent.builder().build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(component);
component.inject(this);
c0 = DaggerBriarIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(t0Dir))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c0);
c1 = DaggerBriarIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(t1Dir))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c1);
c2 = DaggerBriarIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(t2Dir))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c2);
}
@Test
public void testCreateBackup() throws Exception {
// Create the backup
db0.transaction(false, txn -> {
assertNull(socialBackupManager0.getBackupMetadata(txn));
socialBackupManager0.createBackup(txn,
asList(contactId1From0, contactId2From0), 2);
BackupMetadata backupMetadata =
socialBackupManager0.getBackupMetadata(txn);
assertNotNull(backupMetadata);
List<Author> expected = asList(contact1From0.getAuthor(),
contact2From0.getAuthor());
assertEquals(expected, backupMetadata.getCustodians());
assertEquals(2, backupMetadata.getThreshold());
assertEquals(0, backupMetadata.getVersion());
});
// Sync the shard and backup messages to the contacts
sync0To1(2, true);
sync0To2(2, true);
}
}

View File

@@ -27,6 +27,7 @@ import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.forum.ForumModule;
@@ -36,6 +37,7 @@ import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
import org.briarproject.briar.privategroup.invitation.GroupInvitationModule;
import org.briarproject.briar.sharing.SharingModule;
import org.briarproject.briar.socialbackup.SocialBackupModule;
import javax.inject.Singleton;
@@ -70,6 +72,8 @@ public interface BriarIntegrationTestComponent
void inject(SharingModule.EagerSingletons init);
void inject(SocialBackupModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
@@ -116,6 +120,8 @@ public interface BriarIntegrationTestComponent
ConnectionManager getConnectionManager();
SocialBackupManager getSocialBackupManager();
class Helper {
public static void injectEagerSingletons(
@@ -131,6 +137,7 @@ public interface BriarIntegrationTestComponent
c.inject(new MessagingModule.EagerSingletons());
c.inject(new PrivateGroupModule.EagerSingletons());
c.inject(new SharingModule.EagerSingletons());
c.inject(new SocialBackupModule.EagerSingletons());
}
}
}