mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
Implement contact manager methods for pending contacts.
This commit is contained in:
@@ -59,17 +59,18 @@ public interface ContactManager {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the static link that needs to be sent to the contact to be added.
|
* Returns the handshake link that needs to be sent to a contact we want
|
||||||
|
* to add.
|
||||||
*/
|
*/
|
||||||
String getHandshakeLink() throws DbException;
|
String getHandshakeLink() throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests a new contact to be added via the given {@code link}.
|
* Adds a new pending contact identified by the given handshake link.
|
||||||
*
|
*
|
||||||
* @param link The link received from the contact we want to add.
|
* @param link The handshake link received from the contact we want to add.
|
||||||
* @param alias The alias the user has given this contact.
|
* @param alias The alias the user has given this contact.
|
||||||
*/
|
*/
|
||||||
void addPendingContact(String link, String alias)
|
PendingContact addPendingContact(String link, String alias)
|
||||||
throws DbException, FormatException;
|
throws DbException, FormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,10 +79,9 @@ public interface ContactManager {
|
|||||||
Collection<PendingContact> getPendingContacts() throws DbException;
|
Collection<PendingContact> getPendingContacts() throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a {@link PendingContact} that is in state
|
* Removes a {@link PendingContact}.
|
||||||
* {@link PendingContactState FAILED}.
|
|
||||||
*/
|
*/
|
||||||
void removePendingContact(PendingContactId pendingContact) throws DbException;
|
void removePendingContact(PendingContactId p) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given ID.
|
* Returns the contact with the given ID.
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.briarproject.bramble.api.contact;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public interface HandshakeLinkConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the handshake link format.
|
||||||
|
*/
|
||||||
|
int FORMAT_VERSION = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of a base32-encoded handshake link in bytes, excluding the
|
||||||
|
* 'briar://' prefix.
|
||||||
|
*/
|
||||||
|
int BASE32_LINK_BYTES = 53;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of a raw handshake link in bytes, before base32 encoding.
|
||||||
|
*/
|
||||||
|
int RAW_LINK_BYTES = 33;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression for matching handshake links, including or excluding
|
||||||
|
* the 'briar://' prefix.
|
||||||
|
*/
|
||||||
|
Pattern LINK_REGEX =
|
||||||
|
Pattern.compile("(briar://)?([a-z2-7]{" + BASE32_LINK_BYTES + "})");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for hashing handshake public keys to calculate their identifiers.
|
||||||
|
*/
|
||||||
|
String ID_LABEL = "org.briarproject.bramble/HANDSHAKE_KEY_ID";
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ public class Base32 {
|
|||||||
return s.toString();
|
return s.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decode(String s) {
|
public static byte[] decode(String s, boolean strict) {
|
||||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||||
int digitIndex = 0, digitCount = s.length(), currentByte = 0x00;
|
int digitIndex = 0, digitCount = s.length(), currentByte = 0x00;
|
||||||
int byteMask = 0x80, codeMask = 0x10;
|
int byteMask = 0x80, codeMask = 0x10;
|
||||||
@@ -61,7 +61,7 @@ public class Base32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If any extra bits were used for encoding, they should all be zero
|
// If any extra bits were used for encoding, they should all be zero
|
||||||
if (byteMask != 0x80 && currentByte != 0x00)
|
if (strict && byteMask != 0x80 && currentByte != 0x00)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
return b.toByteArray();
|
return b.toByteArray();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,4 +153,13 @@ public class StringUtils {
|
|||||||
return new String(c);
|
return new String(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRandomBase32String(int length) {
|
||||||
|
char[] c = new char[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int character = random.nextInt(32);
|
||||||
|
if (character < 26) c[i] = (char) ('a' + character);
|
||||||
|
else c[i] = (char) ('2' + (character - 26));
|
||||||
|
}
|
||||||
|
return new String(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.contact;
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
@@ -20,19 +21,19 @@ import org.briarproject.bramble.api.transport.KeyManager;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
|
||||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@@ -40,19 +41,22 @@ import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
|||||||
class ContactManagerImpl implements ContactManager {
|
class ContactManagerImpl implements ContactManager {
|
||||||
|
|
||||||
private static final String REMOTE_CONTACT_LINK =
|
private static final String REMOTE_CONTACT_LINK =
|
||||||
"briar://" + getRandomBase32String(LINK_LENGTH);
|
"briar://" + getRandomBase32String(BASE32_LINK_BYTES);
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final KeyManager keyManager;
|
private final KeyManager keyManager;
|
||||||
private final IdentityManager identityManager;
|
private final IdentityManager identityManager;
|
||||||
|
private final PendingContactFactory pendingContactFactory;
|
||||||
private final List<ContactHook> hooks;
|
private final List<ContactHook> hooks;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
|
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
|
||||||
IdentityManager identityManager) {
|
IdentityManager identityManager,
|
||||||
|
PendingContactFactory pendingContactFactory) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.keyManager = keyManager;
|
this.keyManager = keyManager;
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
|
this.pendingContactFactory = pendingContactFactory;
|
||||||
hooks = new CopyOnWriteArrayList<>();
|
hooks = new CopyOnWriteArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,34 +100,23 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
return REMOTE_CONTACT_LINK;
|
return REMOTE_CONTACT_LINK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO replace with real implementation
|
@Override
|
||||||
@SuppressWarnings("SameParameterValue")
|
public PendingContact addPendingContact(String link, String alias)
|
||||||
private static String getRandomBase32String(int length) {
|
throws DbException, FormatException {
|
||||||
Random random = new Random();
|
PendingContact p =
|
||||||
char[] c = new char[length];
|
pendingContactFactory.createPendingContact(link, alias);
|
||||||
for (int i = 0; i < length; i++) {
|
db.transaction(false, txn -> db.addPendingContact(txn, p));
|
||||||
int character = random.nextInt(32);
|
return p;
|
||||||
if (character < 26) c[i] = (char) ('a' + character);
|
|
||||||
else c[i] = (char) ('2' + (character - 26));
|
|
||||||
}
|
|
||||||
return new String(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPendingContact(String link, String alias)
|
public Collection<PendingContact> getPendingContacts() throws DbException {
|
||||||
throws DbException {
|
return db.transactionWithResult(true, db::getPendingContacts);
|
||||||
// TODO replace with real implementation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<PendingContact> getPendingContacts() {
|
public void removePendingContact(PendingContactId p) throws DbException {
|
||||||
// TODO replace with real implementation
|
db.transaction(false, txn -> db.removePendingContact(txn, p));
|
||||||
return emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removePendingContact(PendingContactId id) throws DbException {
|
|
||||||
// TODO replace with real implementation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -28,4 +28,10 @@ public class ContactModule {
|
|||||||
ContactExchangeTaskImpl contactExchangeTask) {
|
ContactExchangeTaskImpl contactExchangeTask) {
|
||||||
return contactExchangeTask;
|
return contactExchangeTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
PendingContactFactory providePendingContactFactory(
|
||||||
|
PendingContactFactoryImpl pendingContactFactory) {
|
||||||
|
return pendingContactFactory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
|
|
||||||
|
interface PendingContactFactory {
|
||||||
|
|
||||||
|
PendingContact createPendingContact(String link, String alias)
|
||||||
|
throws FormatException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContactState;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.util.Base32;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.lang.System.arraycopy;
|
||||||
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
|
||||||
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX;
|
||||||
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
||||||
|
|
||||||
|
class PendingContactFactoryImpl implements PendingContactFactory {
|
||||||
|
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PendingContactFactoryImpl(CryptoComponent crypto, Clock clock) {
|
||||||
|
this.crypto = crypto;
|
||||||
|
this.clock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingContact createPendingContact(String link, String alias)
|
||||||
|
throws FormatException {
|
||||||
|
PublicKey publicKey = parseHandshakeLink(link);
|
||||||
|
PendingContactId id = getPendingContactId(publicKey);
|
||||||
|
long timestamp = clock.currentTimeMillis();
|
||||||
|
return new PendingContact(id, publicKey.getEncoded(), alias,
|
||||||
|
PendingContactState.WAITING_FOR_CONNECTION, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
||||||
|
Matcher matcher = LINK_REGEX.matcher(link);
|
||||||
|
if (!matcher.matches()) throw new FormatException();
|
||||||
|
link = matcher.group(); // Discard anything before or after the link
|
||||||
|
if (link.startsWith("briar://")) link = link.substring(8);
|
||||||
|
byte[] base32 = Base32.decode(link, false);
|
||||||
|
if (base32.length != RAW_LINK_BYTES) throw new AssertionError();
|
||||||
|
if (base32[0] != FORMAT_VERSION) throw new FormatException();
|
||||||
|
byte[] publicKeyBytes = new byte[base32.length - 1];
|
||||||
|
arraycopy(base32, 1, publicKeyBytes, 0, publicKeyBytes.length);
|
||||||
|
try {
|
||||||
|
KeyParser parser = crypto.getAgreementKeyParser();
|
||||||
|
return parser.parsePublicKey(publicKeyBytes);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PendingContactId getPendingContactId(PublicKey publicKey) {
|
||||||
|
byte[] hash = crypto.hash(ID_LABEL, publicKey.getEncoded());
|
||||||
|
return new PendingContactId(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final KeyManager keyManager = context.mock(KeyManager.class);
|
private final KeyManager keyManager = context.mock(KeyManager.class);
|
||||||
private final IdentityManager identityManager =
|
private final IdentityManager identityManager =
|
||||||
context.mock(IdentityManager.class);
|
context.mock(IdentityManager.class);
|
||||||
|
private final PendingContactFactory pendingContactFactory =
|
||||||
|
context.mock(PendingContactFactory.class);
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
private final Author remote = getAuthor();
|
private final Author remote = getAuthor();
|
||||||
private final LocalAuthor localAuthor = getLocalAuthor();
|
private final LocalAuthor localAuthor = getLocalAuthor();
|
||||||
@@ -56,8 +58,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final ContactId contactId = contact.getId();
|
private final ContactId contactId = contact.getId();
|
||||||
|
|
||||||
public ContactManagerImplTest() {
|
public ContactManagerImplTest() {
|
||||||
contactManager =
|
contactManager = new ContactManagerImpl(db, keyManager,
|
||||||
new ContactManagerImpl(db, keyManager, identityManager);
|
identityManager, pendingContactFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user