mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Implement getHandshakeLink().
This commit is contained in:
@@ -33,23 +33,18 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
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.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.UNKNOWN;
|
||||
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.util.StringUtils.getRandomBase32String;
|
||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class ContactManagerImpl implements ContactManager {
|
||||
|
||||
private static final String REMOTE_CONTACT_LINK =
|
||||
"briar://" + getRandomBase32String(BASE32_LINK_BYTES);
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final KeyManager keyManager;
|
||||
private final IdentityManager identityManager;
|
||||
@@ -120,9 +115,10 @@ class ContactManagerImpl implements ContactManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandshakeLink() {
|
||||
// TODO replace with real implementation
|
||||
return REMOTE_CONTACT_LINK;
|
||||
public String getHandshakeLink() throws DbException {
|
||||
KeyPair keyPair = db.transactionWithResult(true,
|
||||
identityManager::getHandshakeKeys);
|
||||
return pendingContactFactory.createHandshakeLink(keyPair.getPublic());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.contact;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
|
||||
interface PendingContactFactory {
|
||||
|
||||
@@ -15,4 +16,9 @@ interface PendingContactFactory {
|
||||
*/
|
||||
PendingContact createPendingContact(String link, String alias)
|
||||
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.LINK_REGEX;
|
||||
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 {
|
||||
|
||||
@@ -41,18 +42,31 @@ class PendingContactFactoryImpl implements PendingContactFactory {
|
||||
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 {
|
||||
Matcher matcher = LINK_REGEX.matcher(link);
|
||||
if (!matcher.find()) throw new FormatException();
|
||||
// Discard 'briar://' and anything before or after the link
|
||||
link = matcher.group(2);
|
||||
byte[] base32 = Base32.decode(link, false);
|
||||
if (base32.length != RAW_LINK_BYTES) throw new AssertionError();
|
||||
byte version = base32[0];
|
||||
byte[] raw = Base32.decode(link, false);
|
||||
if (raw.length != RAW_LINK_BYTES) throw new AssertionError();
|
||||
byte version = raw[0];
|
||||
if (version != FORMAT_VERSION)
|
||||
throw new UnsupportedVersionException(version < FORMAT_VERSION);
|
||||
byte[] publicKeyBytes = new byte[base32.length - 1];
|
||||
arraycopy(base32, 1, publicKeyBytes, 0, publicKeyBytes.length);
|
||||
byte[] publicKeyBytes = new byte[raw.length - 1];
|
||||
arraycopy(raw, 1, publicKeyBytes, 0, publicKeyBytes.length);
|
||||
try {
|
||||
KeyParser parser = crypto.getAgreementKeyParser();
|
||||
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.ContactId;
|
||||
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.db.DatabaseComponent;
|
||||
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.DbExpectations;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -25,16 +27,20 @@ import java.util.Random;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
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.AuthorInfo.Status.OURSELVES;
|
||||
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.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.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -42,7 +48,6 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final Mockery context = new Mockery();
|
||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
private final KeyManager keyManager = context.mock(KeyManager.class);
|
||||
private final IdentityManager identityManager =
|
||||
@@ -196,7 +201,6 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
|
||||
oneOf(identityManager).getLocalAuthor(txn);
|
||||
will(returnValue(localAuthor));
|
||||
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.ID_LABEL;
|
||||
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.test.TestUtils.getAgreementPublicKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@@ -110,6 +114,57 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
||||
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() {
|
||||
return encodeLink(FORMAT_VERSION);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class AddContactViewModel extends AndroidViewModel {
|
||||
handshakeLink.postValue(contactManager.getHandshakeLink());
|
||||
} catch (DbException 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
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user