mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 22:59:54 +01:00
Implement getHandshakeLink().
This commit is contained in:
@@ -33,23 +33,18 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
|
||||||
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
|
||||||
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
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ContactManagerImpl implements ContactManager {
|
class ContactManagerImpl implements ContactManager {
|
||||||
|
|
||||||
private static final String REMOTE_CONTACT_LINK =
|
|
||||||
"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;
|
||||||
@@ -120,9 +115,10 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHandshakeLink() {
|
public String getHandshakeLink() throws DbException {
|
||||||
// TODO replace with real implementation
|
KeyPair keyPair = db.transactionWithResult(true,
|
||||||
return REMOTE_CONTACT_LINK;
|
identityManager::getHandshakeKeys);
|
||||||
|
return pendingContactFactory.createHandshakeLink(keyPair.getPublic());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.contact;
|
|||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
|
||||||
interface PendingContactFactory {
|
interface PendingContactFactory {
|
||||||
|
|
||||||
@@ -15,4 +16,9 @@ interface PendingContactFactory {
|
|||||||
*/
|
*/
|
||||||
PendingContact createPendingContact(String link, String alias)
|
PendingContact createPendingContact(String link, String alias)
|
||||||
throws FormatException;
|
throws FormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a handshake link from the given public key.
|
||||||
|
*/
|
||||||
|
String createHandshakeLink(PublicKey k);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT
|
|||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
|
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.LINK_REGEX;
|
||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||||
|
|
||||||
class PendingContactFactoryImpl implements PendingContactFactory {
|
class PendingContactFactoryImpl implements PendingContactFactory {
|
||||||
|
|
||||||
@@ -41,18 +42,31 @@ class PendingContactFactoryImpl implements PendingContactFactory {
|
|||||||
return new PendingContact(id, publicKey, alias, timestamp);
|
return new PendingContact(id, publicKey, alias, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createHandshakeLink(PublicKey k) {
|
||||||
|
if (!k.getKeyType().equals(KEY_TYPE_AGREEMENT))
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
byte[] encoded = k.getEncoded();
|
||||||
|
if (encoded.length != RAW_LINK_BYTES - 1)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
byte[] raw = new byte[RAW_LINK_BYTES];
|
||||||
|
raw[0] = FORMAT_VERSION;
|
||||||
|
arraycopy(encoded, 0, raw, 1, encoded.length);
|
||||||
|
return "briar://" + Base32.encode(raw).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
private PublicKey parseHandshakeLink(String link) throws FormatException {
|
||||||
Matcher matcher = LINK_REGEX.matcher(link);
|
Matcher matcher = LINK_REGEX.matcher(link);
|
||||||
if (!matcher.find()) throw new FormatException();
|
if (!matcher.find()) throw new FormatException();
|
||||||
// Discard 'briar://' and anything before or after the link
|
// Discard 'briar://' and anything before or after the link
|
||||||
link = matcher.group(2);
|
link = matcher.group(2);
|
||||||
byte[] base32 = Base32.decode(link, false);
|
byte[] raw = Base32.decode(link, false);
|
||||||
if (base32.length != RAW_LINK_BYTES) throw new AssertionError();
|
if (raw.length != RAW_LINK_BYTES) throw new AssertionError();
|
||||||
byte version = base32[0];
|
byte version = raw[0];
|
||||||
if (version != FORMAT_VERSION)
|
if (version != FORMAT_VERSION)
|
||||||
throw new UnsupportedVersionException(version < FORMAT_VERSION);
|
throw new UnsupportedVersionException(version < FORMAT_VERSION);
|
||||||
byte[] publicKeyBytes = new byte[base32.length - 1];
|
byte[] publicKeyBytes = new byte[raw.length - 1];
|
||||||
arraycopy(base32, 1, publicKeyBytes, 0, publicKeyBytes.length);
|
arraycopy(raw, 1, publicKeyBytes, 0, publicKeyBytes.length);
|
||||||
try {
|
try {
|
||||||
KeyParser parser = crypto.getAgreementKeyParser();
|
KeyParser parser = crypto.getAgreementKeyParser();
|
||||||
return parser.parsePublicKey(publicKeyBytes);
|
return parser.parsePublicKey(publicKeyBytes);
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package org.briarproject.bramble.contact;
|
|||||||
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;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -17,7 +20,6 @@ import org.briarproject.bramble.api.transport.KeyManager;
|
|||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -25,16 +27,20 @@ import java.util.Random;
|
|||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
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.test.TestUtils.getAgreementPrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
@@ -42,7 +48,6 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
public class ContactManagerImplTest extends BrambleMockTestCase {
|
public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
private final Mockery context = new Mockery();
|
|
||||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||||
private final KeyManager keyManager = context.mock(KeyManager.class);
|
private final KeyManager keyManager = context.mock(KeyManager.class);
|
||||||
private final IdentityManager identityManager =
|
private final IdentityManager identityManager =
|
||||||
@@ -196,7 +201,6 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
|
|
||||||
context.checking(new DbExpectations() {{
|
context.checking(new DbExpectations() {{
|
||||||
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
|
|
||||||
oneOf(identityManager).getLocalAuthor(txn);
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
will(returnValue(localAuthor));
|
will(returnValue(localAuthor));
|
||||||
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
||||||
@@ -258,4 +262,22 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHandshakeLink() throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, true);
|
||||||
|
PublicKey publicKey = getAgreementPublicKey();
|
||||||
|
PrivateKey privateKey = getAgreementPrivateKey();
|
||||||
|
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||||
|
String link = "briar://" + getRandomBase32String(BASE32_LINK_BYTES);
|
||||||
|
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
|
||||||
|
oneOf(identityManager).getHandshakeKeys(txn);
|
||||||
|
will(returnValue(keyPair));
|
||||||
|
oneOf(pendingContactFactory).createHandshakeLink(publicKey);
|
||||||
|
will(returnValue(link));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(link, contactManager.getHandshakeLink());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,12 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32
|
|||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
|
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.ID_LABEL;
|
||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_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.test.TestUtils.getAgreementPublicKey;
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
@@ -110,6 +114,57 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
assertEquals(timestamp, p.getTimestamp());
|
assertEquals(timestamp, p.getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateHandshakeLinkRejectsInvalidKeyType() {
|
||||||
|
PublicKey invalidPublicKey = context.mock(PublicKey.class);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(invalidPublicKey).getKeyType();
|
||||||
|
will(returnValue(KEY_TYPE_SIGNATURE));
|
||||||
|
}});
|
||||||
|
|
||||||
|
pendingContactFactory.createHandshakeLink(invalidPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateHandshakeLinkRejectsInvalidKeyLength() {
|
||||||
|
PublicKey invalidPublicKey = context.mock(PublicKey.class);
|
||||||
|
byte[] invalidPublicKeyBytes =
|
||||||
|
getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES + 1);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(invalidPublicKey).getKeyType();
|
||||||
|
will(returnValue(KEY_TYPE_AGREEMENT));
|
||||||
|
oneOf(invalidPublicKey).getEncoded();
|
||||||
|
will(returnValue(invalidPublicKeyBytes));
|
||||||
|
}});
|
||||||
|
|
||||||
|
pendingContactFactory.createHandshakeLink(invalidPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateAndParseLink() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(crypto).getAgreementKeyParser();
|
||||||
|
will(returnValue(keyParser));
|
||||||
|
oneOf(keyParser).parsePublicKey(publicKey.getEncoded());
|
||||||
|
will(returnValue(publicKey));
|
||||||
|
oneOf(crypto).hash(ID_LABEL, publicKey.getEncoded());
|
||||||
|
will(returnValue(idBytes));
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(timestamp));
|
||||||
|
}});
|
||||||
|
|
||||||
|
String link = pendingContactFactory.createHandshakeLink(publicKey);
|
||||||
|
PendingContact p =
|
||||||
|
pendingContactFactory.createPendingContact(link, alias);
|
||||||
|
assertArrayEquals(idBytes, p.getId().getBytes());
|
||||||
|
assertArrayEquals(publicKey.getEncoded(),
|
||||||
|
p.getPublicKey().getEncoded());
|
||||||
|
assertEquals(alias, p.getAlias());
|
||||||
|
assertEquals(timestamp, p.getTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
private String encodeLink() {
|
private String encodeLink() {
|
||||||
return encodeLink(FORMAT_VERSION);
|
return encodeLink(FORMAT_VERSION);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class AddContactViewModel extends AndroidViewModel {
|
|||||||
handshakeLink.postValue(contactManager.getHandshakeLink());
|
handshakeLink.postValue(contactManager.getHandshakeLink());
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
// the UI should stay disable in this case,
|
// the UI should stay disabled in this case,
|
||||||
// leaving the user unable to proceed
|
// leaving the user unable to proceed
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user