Implement getHandshakeLink().

This commit is contained in:
akwizgran
2019-05-22 13:33:36 +01:00
parent 643270e247
commit af8b7f1130
6 changed files with 110 additions and 17 deletions

View File

@@ -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

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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());
}
} }

View File

@@ -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);
} }

View File

@@ -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
} }
}); });