mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 22:59:54 +01:00
Test BQP implementation
This commit is contained in:
@@ -15,6 +15,8 @@ dependencies {
|
|||||||
compile project(':briar-desktop')
|
compile project(':briar-desktop')
|
||||||
compile "junit:junit:4.12"
|
compile "junit:junit:4.12"
|
||||||
compile "org.jmock:jmock:2.8.1"
|
compile "org.jmock:jmock:2.8.1"
|
||||||
|
compile "org.jmock:jmock-junit4:2.8.1"
|
||||||
|
compile "org.jmock:jmock-legacy:2.8.1"
|
||||||
compile "org.hamcrest:hamcrest-library:1.3"
|
compile "org.hamcrest:hamcrest-library:1.3"
|
||||||
compile "org.hamcrest:hamcrest-core:1.3"
|
compile "org.hamcrest:hamcrest-core:1.3"
|
||||||
}
|
}
|
||||||
@@ -23,6 +25,8 @@ dependencyVerification {
|
|||||||
verify = [
|
verify = [
|
||||||
'junit:junit:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
'junit:junit:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
'org.jmock:jmock:75d4bdaf636879f0215830c5e6ab99407069a625eaffde5d57b32d887b75dc14',
|
'org.jmock:jmock:75d4bdaf636879f0215830c5e6ab99407069a625eaffde5d57b32d887b75dc14',
|
||||||
|
'org.jmock:jmock-junit4:81e3fff46ed56738a6f3f5147525d1d85cda591ce5df007cc193e735cee31113',
|
||||||
|
'org.jmock:jmock-legacy:19c76059eb254775ba884fc8039bc5c7d1700dc68cc55ad3be5b405a2a8a1819',
|
||||||
'org.hamcrest:hamcrest-library:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
'org.hamcrest:hamcrest-library:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
'org.hamcrest:hamcrest-core:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
'org.hamcrest:hamcrest-core:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,394 @@
|
|||||||
|
package org.briarproject.keyagreement;
|
||||||
|
|
||||||
|
import org.briarproject.BriarTestCase;
|
||||||
|
import org.briarproject.TestUtils;
|
||||||
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.api.keyagreement.Payload;
|
||||||
|
import org.briarproject.api.keyagreement.PayloadEncoder;
|
||||||
|
import org.briarproject.util.StringUtils;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.auto.Mock;
|
||||||
|
import org.jmock.integration.junit4.JUnitRuleMockery;
|
||||||
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
public class KeyAgreementProtocolTest extends BriarTestCase {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public JUnitRuleMockery context = new JUnitRuleMockery() {{
|
||||||
|
// So we can mock concrete classes like KeyAgreementTransport
|
||||||
|
setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
|
}};
|
||||||
|
|
||||||
|
private static final byte[] ALICE_PUBKEY = TestUtils.getRandomBytes(32);
|
||||||
|
private static final byte[] ALICE_COMMIT =
|
||||||
|
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||||
|
private static final byte[] ALICE_PAYLOAD =
|
||||||
|
TestUtils.getRandomBytes(COMMIT_LENGTH + 8);
|
||||||
|
|
||||||
|
private static final byte[] BOB_PUBKEY = TestUtils.getRandomBytes(32);
|
||||||
|
private static final byte[] BOB_COMMIT =
|
||||||
|
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||||
|
private static final byte[] BOB_PAYLOAD =
|
||||||
|
TestUtils.getRandomBytes(COMMIT_LENGTH + 19);
|
||||||
|
|
||||||
|
private static final byte[] ALICE_CONFIRM =
|
||||||
|
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||||
|
private static final byte[] BOB_CONFIRM =
|
||||||
|
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
|
private static final byte[] BAD_PUBKEY = TestUtils.getRandomBytes(32);
|
||||||
|
private static final byte[] BAD_COMMIT =
|
||||||
|
TestUtils.getRandomBytes(COMMIT_LENGTH);
|
||||||
|
private static final byte[] BAD_CONFIRM =
|
||||||
|
TestUtils.getRandomBytes(SecretKey.LENGTH);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
KeyAgreementProtocol.Callbacks callbacks;
|
||||||
|
@Mock
|
||||||
|
CryptoComponent crypto;
|
||||||
|
@Mock
|
||||||
|
PayloadEncoder payloadEncoder;
|
||||||
|
@Mock
|
||||||
|
KeyAgreementTransport transport;
|
||||||
|
@Mock
|
||||||
|
PublicKey ourPubKey;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAliceProtocol() throws Exception {
|
||||||
|
// set up
|
||||||
|
final Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||||
|
final Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||||
|
final KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
final SecretKey sharedSecret = TestUtils.createSecretKey();
|
||||||
|
final SecretKey masterSecret = TestUtils.createSecretKey();
|
||||||
|
|
||||||
|
KeyAgreementProtocol protocol =
|
||||||
|
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||||
|
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
|
// expectations
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Helpers
|
||||||
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
|
will(returnValue(ALICE_PAYLOAD));
|
||||||
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
|
will(returnValue(BOB_PAYLOAD));
|
||||||
|
allowing(ourPubKey).getEncoded();
|
||||||
|
will(returnValue(ALICE_PUBKEY));
|
||||||
|
|
||||||
|
// Alice sends her public key
|
||||||
|
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||||
|
|
||||||
|
// Alice receives Bob's public key
|
||||||
|
oneOf(callbacks).connectionWaiting();
|
||||||
|
oneOf(transport).receiveKey();
|
||||||
|
will(returnValue(BOB_PUBKEY));
|
||||||
|
oneOf(callbacks).initialPacketReceived();
|
||||||
|
|
||||||
|
// Alice verifies Bob's public key
|
||||||
|
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
||||||
|
will(returnValue(BOB_COMMIT));
|
||||||
|
|
||||||
|
// Alice computes shared secret
|
||||||
|
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
||||||
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
|
// Alice sends her confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||||
|
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
||||||
|
will(returnValue(ALICE_CONFIRM));
|
||||||
|
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
||||||
|
|
||||||
|
// Alice receives Bob's confirmation record
|
||||||
|
oneOf(transport).receiveConfirm();
|
||||||
|
will(returnValue(BOB_CONFIRM));
|
||||||
|
|
||||||
|
// Alice verifies Bob's confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||||
|
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
||||||
|
will(returnValue(BOB_CONFIRM));
|
||||||
|
|
||||||
|
// Alice computes master secret
|
||||||
|
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
||||||
|
will(returnValue(masterSecret));
|
||||||
|
}});
|
||||||
|
|
||||||
|
// execute
|
||||||
|
assertThat(masterSecret, is(equalTo(protocol.perform())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBobProtocol() throws Exception {
|
||||||
|
// set up
|
||||||
|
final Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||||
|
final Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||||
|
final KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
final SecretKey sharedSecret = TestUtils.createSecretKey();
|
||||||
|
final SecretKey masterSecret = TestUtils.createSecretKey();
|
||||||
|
|
||||||
|
KeyAgreementProtocol protocol =
|
||||||
|
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||||
|
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
|
// expectations
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Helpers
|
||||||
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
|
will(returnValue(BOB_PAYLOAD));
|
||||||
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
|
will(returnValue(ALICE_PAYLOAD));
|
||||||
|
allowing(ourPubKey).getEncoded();
|
||||||
|
will(returnValue(BOB_PUBKEY));
|
||||||
|
|
||||||
|
// Bob receives Alice's public key
|
||||||
|
oneOf(transport).receiveKey();
|
||||||
|
will(returnValue(ALICE_PUBKEY));
|
||||||
|
oneOf(callbacks).initialPacketReceived();
|
||||||
|
|
||||||
|
// Bob verifies Alice's public key
|
||||||
|
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
||||||
|
will(returnValue(ALICE_COMMIT));
|
||||||
|
|
||||||
|
// Bob sends his public key
|
||||||
|
oneOf(transport).sendKey(BOB_PUBKEY);
|
||||||
|
|
||||||
|
// Bob computes shared secret
|
||||||
|
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
||||||
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
|
// Bob receives Alices's confirmation record
|
||||||
|
oneOf(transport).receiveConfirm();
|
||||||
|
will(returnValue(ALICE_CONFIRM));
|
||||||
|
|
||||||
|
// Bob verifies Alice's confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||||
|
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
||||||
|
will(returnValue(ALICE_CONFIRM));
|
||||||
|
|
||||||
|
// Bob sends his confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||||
|
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
||||||
|
will(returnValue(BOB_CONFIRM));
|
||||||
|
oneOf(transport).sendConfirm(BOB_CONFIRM);
|
||||||
|
|
||||||
|
// Bob computes master secret
|
||||||
|
oneOf(crypto).deriveMasterSecret(sharedSecret);
|
||||||
|
will(returnValue(masterSecret));
|
||||||
|
}});
|
||||||
|
|
||||||
|
// execute
|
||||||
|
assertThat(masterSecret, is(equalTo(protocol.perform())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AbortException.class)
|
||||||
|
public void testAliceProtocolAbortOnBadKey() throws Exception {
|
||||||
|
// set up
|
||||||
|
final Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||||
|
final Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||||
|
final KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
|
||||||
|
KeyAgreementProtocol protocol =
|
||||||
|
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||||
|
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
|
// expectations
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Helpers
|
||||||
|
allowing(ourPubKey).getEncoded();
|
||||||
|
will(returnValue(ALICE_PUBKEY));
|
||||||
|
|
||||||
|
// Alice sends her public key
|
||||||
|
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||||
|
|
||||||
|
// Alice receives a bad public key
|
||||||
|
oneOf(callbacks).connectionWaiting();
|
||||||
|
oneOf(transport).receiveKey();
|
||||||
|
will(returnValue(BAD_PUBKEY));
|
||||||
|
oneOf(callbacks).initialPacketReceived();
|
||||||
|
|
||||||
|
// Alice verifies Bob's public key
|
||||||
|
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
||||||
|
will(returnValue(BAD_COMMIT));
|
||||||
|
|
||||||
|
// Alice aborts
|
||||||
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
|
// Alice never computes shared secret
|
||||||
|
never(crypto).deriveSharedSecret(BAD_PUBKEY, ourKeyPair, true);
|
||||||
|
}});
|
||||||
|
|
||||||
|
// execute
|
||||||
|
protocol.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AbortException.class)
|
||||||
|
public void testBobProtocolAbortOnBadKey() throws Exception {
|
||||||
|
// set up
|
||||||
|
final Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||||
|
final Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||||
|
final KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
|
||||||
|
KeyAgreementProtocol protocol =
|
||||||
|
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||||
|
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
|
// expectations
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Helpers
|
||||||
|
allowing(ourPubKey).getEncoded();
|
||||||
|
will(returnValue(BOB_PUBKEY));
|
||||||
|
|
||||||
|
// Bob receives a bad public key
|
||||||
|
oneOf(transport).receiveKey();
|
||||||
|
will(returnValue(BAD_PUBKEY));
|
||||||
|
oneOf(callbacks).initialPacketReceived();
|
||||||
|
|
||||||
|
// Bob verifies Alice's public key
|
||||||
|
oneOf(crypto).deriveKeyCommitment(BAD_PUBKEY);
|
||||||
|
will(returnValue(BAD_COMMIT));
|
||||||
|
|
||||||
|
// Bob aborts
|
||||||
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
|
// Bob never sends his public key
|
||||||
|
never(transport).sendKey(BOB_PUBKEY);
|
||||||
|
}});
|
||||||
|
|
||||||
|
// execute
|
||||||
|
protocol.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AbortException.class)
|
||||||
|
public void testAliceProtocolAbortOnBadConfirm() throws Exception {
|
||||||
|
// set up
|
||||||
|
final Payload theirPayload = new Payload(BOB_COMMIT, null);
|
||||||
|
final Payload ourPayload = new Payload(ALICE_COMMIT, null);
|
||||||
|
final KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
final SecretKey sharedSecret = TestUtils.createSecretKey();
|
||||||
|
|
||||||
|
KeyAgreementProtocol protocol =
|
||||||
|
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||||
|
transport, theirPayload, ourPayload, ourKeyPair, true);
|
||||||
|
|
||||||
|
// expectations
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Helpers
|
||||||
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
|
will(returnValue(ALICE_PAYLOAD));
|
||||||
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
|
will(returnValue(BOB_PAYLOAD));
|
||||||
|
allowing(ourPubKey).getEncoded();
|
||||||
|
will(returnValue(ALICE_PUBKEY));
|
||||||
|
|
||||||
|
// Alice sends her public key
|
||||||
|
oneOf(transport).sendKey(ALICE_PUBKEY);
|
||||||
|
|
||||||
|
// Alice receives Bob's public key
|
||||||
|
oneOf(callbacks).connectionWaiting();
|
||||||
|
oneOf(transport).receiveKey();
|
||||||
|
will(returnValue(BOB_PUBKEY));
|
||||||
|
oneOf(callbacks).initialPacketReceived();
|
||||||
|
|
||||||
|
// Alice verifies Bob's public key
|
||||||
|
oneOf(crypto).deriveKeyCommitment(BOB_PUBKEY);
|
||||||
|
will(returnValue(BOB_COMMIT));
|
||||||
|
|
||||||
|
// Alice computes shared secret
|
||||||
|
oneOf(crypto).deriveSharedSecret(BOB_PUBKEY, ourKeyPair, true);
|
||||||
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
|
// Alice sends her confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||||
|
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, true);
|
||||||
|
will(returnValue(ALICE_CONFIRM));
|
||||||
|
oneOf(transport).sendConfirm(ALICE_CONFIRM);
|
||||||
|
|
||||||
|
// Alice receives a bad confirmation record
|
||||||
|
oneOf(transport).receiveConfirm();
|
||||||
|
will(returnValue(BAD_CONFIRM));
|
||||||
|
|
||||||
|
// Alice verifies Bob's confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, BOB_PAYLOAD,
|
||||||
|
ALICE_PAYLOAD, BOB_PUBKEY, ourKeyPair, true, false);
|
||||||
|
will(returnValue(BOB_CONFIRM));
|
||||||
|
|
||||||
|
// Alice aborts
|
||||||
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
|
// Alice never computes master secret
|
||||||
|
never(crypto).deriveMasterSecret(sharedSecret);
|
||||||
|
}});
|
||||||
|
|
||||||
|
// execute
|
||||||
|
protocol.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AbortException.class)
|
||||||
|
public void testBobProtocolAbortOnBadConfirm() throws Exception {
|
||||||
|
// set up
|
||||||
|
final Payload theirPayload = new Payload(ALICE_COMMIT, null);
|
||||||
|
final Payload ourPayload = new Payload(BOB_COMMIT, null);
|
||||||
|
final KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
|
final SecretKey sharedSecret = TestUtils.createSecretKey();
|
||||||
|
|
||||||
|
KeyAgreementProtocol protocol =
|
||||||
|
new KeyAgreementProtocol(callbacks, crypto, payloadEncoder,
|
||||||
|
transport, theirPayload, ourPayload, ourKeyPair, false);
|
||||||
|
|
||||||
|
// expectations
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Helpers
|
||||||
|
allowing(payloadEncoder).encode(ourPayload);
|
||||||
|
will(returnValue(BOB_PAYLOAD));
|
||||||
|
allowing(payloadEncoder).encode(theirPayload);
|
||||||
|
will(returnValue(ALICE_PAYLOAD));
|
||||||
|
allowing(ourPubKey).getEncoded();
|
||||||
|
will(returnValue(BOB_PUBKEY));
|
||||||
|
|
||||||
|
// Bob receives Alice's public key
|
||||||
|
oneOf(transport).receiveKey();
|
||||||
|
will(returnValue(ALICE_PUBKEY));
|
||||||
|
oneOf(callbacks).initialPacketReceived();
|
||||||
|
|
||||||
|
// Bob verifies Alice's public key
|
||||||
|
oneOf(crypto).deriveKeyCommitment(ALICE_PUBKEY);
|
||||||
|
will(returnValue(ALICE_COMMIT));
|
||||||
|
|
||||||
|
// Bob sends his public key
|
||||||
|
oneOf(transport).sendKey(BOB_PUBKEY);
|
||||||
|
|
||||||
|
// Bob computes shared secret
|
||||||
|
oneOf(crypto).deriveSharedSecret(ALICE_PUBKEY, ourKeyPair, false);
|
||||||
|
will(returnValue(sharedSecret));
|
||||||
|
|
||||||
|
// Bob receives a bad confirmation record
|
||||||
|
oneOf(transport).receiveConfirm();
|
||||||
|
will(returnValue(BAD_CONFIRM));
|
||||||
|
|
||||||
|
// Bob verifies Alice's confirmation record
|
||||||
|
oneOf(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||||
|
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, true);
|
||||||
|
will(returnValue(ALICE_CONFIRM));
|
||||||
|
|
||||||
|
// Bob aborts
|
||||||
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
|
// Bob never sends his confirmation record
|
||||||
|
never(crypto).deriveConfirmationRecord(sharedSecret, ALICE_PAYLOAD,
|
||||||
|
BOB_PAYLOAD, ALICE_PUBKEY, ourKeyPair, false, false);
|
||||||
|
}});
|
||||||
|
|
||||||
|
// execute
|
||||||
|
protocol.perform();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user