|
|
|
|
@@ -3,17 +3,16 @@ package org.briarproject.briar.socialbackup;
|
|
|
|
|
import org.briarproject.bramble.api.FormatException;
|
|
|
|
|
import org.briarproject.bramble.api.Predicate;
|
|
|
|
|
import org.briarproject.bramble.api.client.ClientHelper;
|
|
|
|
|
import org.briarproject.bramble.api.contact.Contact;
|
|
|
|
|
import org.briarproject.bramble.api.contact.ContactExchangeConstants;
|
|
|
|
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|
|
|
|
import org.briarproject.bramble.api.contact.ContactExchangeRecordTypes;
|
|
|
|
|
import org.briarproject.bramble.api.contact.ContactManager;
|
|
|
|
|
import org.briarproject.bramble.api.contact.PendingContactId;
|
|
|
|
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
|
|
|
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
|
|
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
|
|
|
|
import org.briarproject.bramble.api.data.BdfList;
|
|
|
|
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
|
|
|
|
import org.briarproject.bramble.api.db.DbException;
|
|
|
|
|
import org.briarproject.bramble.api.identity.Author;
|
|
|
|
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
|
|
|
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
|
|
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
|
|
|
@@ -30,7 +29,11 @@ import org.briarproject.bramble.api.system.Clock;
|
|
|
|
|
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
|
|
|
|
import org.briarproject.bramble.api.transport.StreamWriter;
|
|
|
|
|
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|
|
|
|
import org.briarproject.bramble.contact.ContactExchangeCrypto;
|
|
|
|
|
import org.briarproject.bramble.api.contact.ContactExchangeCrypto;
|
|
|
|
|
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
|
|
|
|
|
import org.briarproject.briar.api.socialbackup.Shard;
|
|
|
|
|
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
|
|
|
|
import org.briarproject.briar.api.socialbackup.SocialBackupExchangeManager;
|
|
|
|
|
|
|
|
|
|
import java.io.EOFException;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
@@ -43,28 +46,30 @@ import javax.annotation.Nullable;
|
|
|
|
|
import javax.annotation.concurrent.Immutable;
|
|
|
|
|
import javax.inject.Inject;
|
|
|
|
|
|
|
|
|
|
import sun.java2d.xr.XRBackendNative;
|
|
|
|
|
|
|
|
|
|
import static java.util.logging.Logger.getLogger;
|
|
|
|
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
|
|
|
|
import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
|
|
|
|
|
import static org.briarproject.bramble.contact.ContactExchangeRecordTypes.CONTACT_INFO;
|
|
|
|
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
|
|
|
|
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
|
|
|
|
|
|
|
|
|
@Immutable
|
|
|
|
|
@NotNullByDefault
|
|
|
|
|
class SocialBackupExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
class SocialBackupExchangeManagerImpl implements SocialBackupExchangeManager {
|
|
|
|
|
|
|
|
|
|
private static final Logger LOG =
|
|
|
|
|
getLogger(SocialBackupExchangeManagerImpl.class.getName());
|
|
|
|
|
|
|
|
|
|
// Accept records with current protocol version, known record type
|
|
|
|
|
private static final Predicate<Record> ACCEPT = r ->
|
|
|
|
|
r.getProtocolVersion() == ContactExchangeConstants.PROTOCOL_VERSION &&
|
|
|
|
|
r.getProtocolVersion() ==
|
|
|
|
|
ContactExchangeConstants.PROTOCOL_VERSION &&
|
|
|
|
|
isKnownRecordType(r.getRecordType());
|
|
|
|
|
|
|
|
|
|
// Ignore records with current protocol version, unknown record type
|
|
|
|
|
private static final Predicate<Record> IGNORE = r ->
|
|
|
|
|
r.getProtocolVersion() == ContactExchangeConstants.PROTOCOL_VERSION &&
|
|
|
|
|
r.getProtocolVersion() ==
|
|
|
|
|
ContactExchangeConstants.PROTOCOL_VERSION &&
|
|
|
|
|
!isKnownRecordType(r.getRecordType());
|
|
|
|
|
|
|
|
|
|
private static boolean isKnownRecordType(byte type) {
|
|
|
|
|
@@ -77,17 +82,19 @@ class SocialBackupExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
private final RecordWriterFactory recordWriterFactory;
|
|
|
|
|
private final Clock clock;
|
|
|
|
|
private final ContactManager contactManager;
|
|
|
|
|
private final IdentityManager identityManager;
|
|
|
|
|
// private final IdentityManager identityManager;
|
|
|
|
|
private final TransportPropertyManager transportPropertyManager;
|
|
|
|
|
private final ContactExchangeCrypto contactExchangeCrypto;
|
|
|
|
|
private final StreamReaderFactory streamReaderFactory;
|
|
|
|
|
private final StreamWriterFactory streamWriterFactory;
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
|
SocialBackupExchangeManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
|
|
|
|
SocialBackupExchangeManagerImpl(DatabaseComponent db,
|
|
|
|
|
ClientHelper clientHelper,
|
|
|
|
|
RecordReaderFactory recordReaderFactory,
|
|
|
|
|
RecordWriterFactory recordWriterFactory, Clock clock,
|
|
|
|
|
ContactManager contactManager, IdentityManager identityManager,
|
|
|
|
|
ContactManager contactManager,
|
|
|
|
|
// IdentityManager identityManager,
|
|
|
|
|
TransportPropertyManager transportPropertyManager,
|
|
|
|
|
ContactExchangeCrypto contactExchangeCrypto,
|
|
|
|
|
StreamReaderFactory streamReaderFactory,
|
|
|
|
|
@@ -98,7 +105,7 @@ class SocialBackupExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
this.recordWriterFactory = recordWriterFactory;
|
|
|
|
|
this.clock = clock;
|
|
|
|
|
this.contactManager = contactManager;
|
|
|
|
|
this.identityManager = identityManager;
|
|
|
|
|
// this.identityManager = identityManager;
|
|
|
|
|
this.transportPropertyManager = transportPropertyManager;
|
|
|
|
|
this.contactExchangeCrypto = contactExchangeCrypto;
|
|
|
|
|
this.streamReaderFactory = streamReaderFactory;
|
|
|
|
|
@@ -106,70 +113,16 @@ class SocialBackupExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void sendSocialBackup(DuplexTransportConnection conn,
|
|
|
|
|
SecretKey masterKey, boolean alice,
|
|
|
|
|
boolean verified) throws IOException, DbException {
|
|
|
|
|
return exchange(null, conn, masterKey, alice, verified);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ReturnShardPayload receiveSocialBackup(DuplexTransportConnection conn,
|
|
|
|
|
SecretKey masterKey, boolean verified) throws IOException, DbException {
|
|
|
|
|
boolean alice = false;
|
|
|
|
|
InputStream in = conn.getReader().getInputStream();
|
|
|
|
|
|
|
|
|
|
Map<TransportId, TransportProperties> localProperties =
|
|
|
|
|
transportPropertyManager.getLocalProperties();
|
|
|
|
|
|
|
|
|
|
// Derive the header keys for the transport streams
|
|
|
|
|
SecretKey remoteHeaderKey =
|
|
|
|
|
contactExchangeCrypto.deriveHeaderKey(masterKey, !alice);
|
|
|
|
|
|
|
|
|
|
// Create the readers
|
|
|
|
|
InputStream streamReader = streamReaderFactory
|
|
|
|
|
.createContactExchangeStreamReader(in, remoteHeaderKey);
|
|
|
|
|
RecordReader recordReader =
|
|
|
|
|
recordReaderFactory.createRecordReader(streamReader);
|
|
|
|
|
|
|
|
|
|
long localTimestamp = clock.currentTimeMillis();
|
|
|
|
|
ContactInfo remoteInfo;
|
|
|
|
|
remoteInfo = receiveContactInfo(recordReader);
|
|
|
|
|
|
|
|
|
|
// Skip any remaining records from the incoming stream
|
|
|
|
|
recordReader.readRecord(r -> false, IGNORE);
|
|
|
|
|
|
|
|
|
|
// Verify the contact's signature
|
|
|
|
|
PublicKey remotePublicKey = remoteInfo.author.getPublicKey();
|
|
|
|
|
if (!contactExchangeCrypto.verify(remotePublicKey,
|
|
|
|
|
masterKey, !alice, remoteInfo.signature)) {
|
|
|
|
|
LOG.warning("Invalid signature");
|
|
|
|
|
throw new FormatException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The agreed timestamp is the minimum of the peers' timestamps
|
|
|
|
|
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
|
|
|
|
|
|
|
|
|
// Contact exchange succeeded
|
|
|
|
|
LOG.info("Received social backup");
|
|
|
|
|
return contact;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @Override
|
|
|
|
|
// public Contact exchangeContacts(PendingContactId p,
|
|
|
|
|
// DuplexTransportConnection conn, SecretKey masterKey, boolean alice,
|
|
|
|
|
// boolean verified) throws IOException, DbException {
|
|
|
|
|
// return exchange(p, conn, masterKey, alice, verified);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
private Contact exchange(@Nullable PendingContactId p,
|
|
|
|
|
DuplexTransportConnection conn, SecretKey masterKey, boolean alice,
|
|
|
|
|
public void sendReturnShard(DuplexTransportConnection conn,
|
|
|
|
|
SecretKey masterKey,
|
|
|
|
|
boolean verified) throws IOException, DbException {
|
|
|
|
|
boolean alice = true;
|
|
|
|
|
// Get the transport connection's input and output streams
|
|
|
|
|
InputStream in = conn.getReader().getInputStream();
|
|
|
|
|
OutputStream out = conn.getWriter().getOutputStream();
|
|
|
|
|
|
|
|
|
|
// Get the local author and transport properties
|
|
|
|
|
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
|
|
|
|
// LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
|
|
|
|
Map<TransportId, TransportProperties> localProperties =
|
|
|
|
|
transportPropertyManager.getLocalProperties();
|
|
|
|
|
|
|
|
|
|
@@ -192,21 +145,14 @@ class SocialBackupExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
.createRecordWriter(streamWriter.getOutputStream());
|
|
|
|
|
|
|
|
|
|
// Create our signature
|
|
|
|
|
byte[] localSignature = contactExchangeCrypto
|
|
|
|
|
.sign(localAuthor.getPrivateKey(), masterKey, alice);
|
|
|
|
|
// byte[] localSignature = contactExchangeCrypto
|
|
|
|
|
// .sign(localAuthor.getPrivateKey(), masterKey, alice);
|
|
|
|
|
|
|
|
|
|
// Exchange contact info
|
|
|
|
|
long localTimestamp = clock.currentTimeMillis();
|
|
|
|
|
ContactInfo remoteInfo;
|
|
|
|
|
if (alice) {
|
|
|
|
|
sendShardAndBackup(recordWriter, localAuthor, localProperties,
|
|
|
|
|
localSignature, localTimestamp);
|
|
|
|
|
remoteAcknowledgement = receiveRemoteAcknowledgement(recordReader);
|
|
|
|
|
} else {
|
|
|
|
|
remoteInfo = receiveContactInfo(recordReader);
|
|
|
|
|
sendContactInfo(recordWriter, localAuthor, localProperties,
|
|
|
|
|
localSignature, localTimestamp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendShardPayload(recordWriter, localProperties, returnShardPayload, localTimestamp);
|
|
|
|
|
receiveAcknowledgement(recordReader);
|
|
|
|
|
|
|
|
|
|
// Send EOF on the outgoing stream
|
|
|
|
|
streamWriter.sendEndOfStream();
|
|
|
|
|
@@ -215,71 +161,140 @@ class SocialBackupExchangeManagerImpl implements ContactExchangeManager {
|
|
|
|
|
recordReader.readRecord(r -> false, IGNORE);
|
|
|
|
|
|
|
|
|
|
// Verify the contact's signature
|
|
|
|
|
PublicKey remotePublicKey = remoteInfo.author.getPublicKey();
|
|
|
|
|
if (!contactExchangeCrypto.verify(remotePublicKey,
|
|
|
|
|
masterKey, !alice, remoteInfo.signature)) {
|
|
|
|
|
LOG.warning("Invalid signature");
|
|
|
|
|
throw new FormatException();
|
|
|
|
|
}
|
|
|
|
|
// PublicKey remotePublicKey = remoteInfo.author.getPublicKey();
|
|
|
|
|
// if (!contactExchangeCrypto.verify(remotePublicKey,
|
|
|
|
|
// masterKey, !alice, remoteInfo.signature)) {
|
|
|
|
|
// LOG.warning("Invalid signature");
|
|
|
|
|
// throw new FormatException();
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// The agreed timestamp is the minimum of the peers' timestamps
|
|
|
|
|
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
|
|
|
|
// long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
|
|
|
|
|
|
|
|
|
// Add the contact
|
|
|
|
|
Contact contact = addContact(p, remoteInfo.author, localAuthor,
|
|
|
|
|
masterKey, timestamp, alice, verified, remoteInfo.properties);
|
|
|
|
|
LOG.info("Social backup sent");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ReturnShardPayload receiveReturnShard(DuplexTransportConnection conn,
|
|
|
|
|
SecretKey masterKey, boolean verified)
|
|
|
|
|
throws IOException, DbException {
|
|
|
|
|
boolean alice = false;
|
|
|
|
|
// Get the transport connection's input and output streams
|
|
|
|
|
InputStream in = conn.getReader().getInputStream();
|
|
|
|
|
OutputStream out = conn.getWriter().getOutputStream();
|
|
|
|
|
|
|
|
|
|
// Get the local author and transport properties
|
|
|
|
|
// LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
|
|
|
|
Map<TransportId, TransportProperties> localProperties =
|
|
|
|
|
transportPropertyManager.getLocalProperties();
|
|
|
|
|
|
|
|
|
|
// Derive the header keys for the transport streams
|
|
|
|
|
SecretKey localHeaderKey =
|
|
|
|
|
contactExchangeCrypto.deriveHeaderKey(masterKey, alice);
|
|
|
|
|
SecretKey remoteHeaderKey =
|
|
|
|
|
contactExchangeCrypto.deriveHeaderKey(masterKey, !alice);
|
|
|
|
|
|
|
|
|
|
// Create the readers
|
|
|
|
|
InputStream streamReader = streamReaderFactory
|
|
|
|
|
.createContactExchangeStreamReader(in, remoteHeaderKey);
|
|
|
|
|
RecordReader recordReader =
|
|
|
|
|
recordReaderFactory.createRecordReader(streamReader);
|
|
|
|
|
|
|
|
|
|
// Create the writers
|
|
|
|
|
StreamWriter streamWriter = streamWriterFactory
|
|
|
|
|
.createContactExchangeStreamWriter(out, localHeaderKey);
|
|
|
|
|
RecordWriter recordWriter = recordWriterFactory
|
|
|
|
|
.createRecordWriter(streamWriter.getOutputStream());
|
|
|
|
|
|
|
|
|
|
// Create our signature
|
|
|
|
|
// byte[] localSignature = contactExchangeCrypto
|
|
|
|
|
// .sign(localAuthor.getPrivateKey(), masterKey, alice);
|
|
|
|
|
|
|
|
|
|
// Exchange contact info
|
|
|
|
|
long localTimestamp = clock.currentTimeMillis();
|
|
|
|
|
ReturnShardPayload returnShardPayload =
|
|
|
|
|
receiveShardPayload(recordReader);
|
|
|
|
|
sendAcknowledgement(recordWriter, localProperties, localTimestamp);
|
|
|
|
|
|
|
|
|
|
// Send EOF on the outgoing stream
|
|
|
|
|
streamWriter.sendEndOfStream();
|
|
|
|
|
|
|
|
|
|
// Skip any remaining records from the incoming stream
|
|
|
|
|
recordReader.readRecord(r -> false, IGNORE);
|
|
|
|
|
|
|
|
|
|
// Verify the contact's signature
|
|
|
|
|
// PublicKey remotePublicKey = remoteInfo.author.getPublicKey();
|
|
|
|
|
// if (!contactExchangeCrypto.verify(remotePublicKey,
|
|
|
|
|
// masterKey, !alice, remoteInfo.signature)) {
|
|
|
|
|
// LOG.warning("Invalid signature");
|
|
|
|
|
// throw new FormatException();
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// The agreed timestamp is the minimum of the peers' timestamps
|
|
|
|
|
// long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
|
|
|
|
|
|
|
|
|
|
// Contact exchange succeeded
|
|
|
|
|
LOG.info("Contact exchange succeeded");
|
|
|
|
|
return contact;
|
|
|
|
|
LOG.info("Received shard payload");
|
|
|
|
|
return returnShardPayload;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void sendContactInfo(RecordWriter recordWriter, Author author,
|
|
|
|
|
Map<TransportId, TransportProperties> properties, byte[] signature,
|
|
|
|
|
private void sendShardPayload(RecordWriter recordWriter,
|
|
|
|
|
Map<TransportId, TransportProperties> properties,
|
|
|
|
|
ReturnShardPayload returnShardPayload,
|
|
|
|
|
long timestamp) throws IOException {
|
|
|
|
|
BdfList authorList = clientHelper.toList(author);
|
|
|
|
|
// BdfList authorList = clientHelper.toList(author);
|
|
|
|
|
BdfDictionary props = clientHelper.toDictionary(properties);
|
|
|
|
|
BdfList payload = BdfList.of(authorList, props, signature, timestamp);
|
|
|
|
|
Shard shard = returnShardPayload.getShard();
|
|
|
|
|
BdfList shardList = BdfList.of(shard.getSecretId(), shard.getShard());
|
|
|
|
|
BdfList payload = BdfList.of(shardList,
|
|
|
|
|
returnShardPayload.getBackupPayload().getBytes(), timestamp);
|
|
|
|
|
recordWriter.writeRecord(new Record(
|
|
|
|
|
ContactExchangeConstants.PROTOCOL_VERSION, ContactExchangeRecordTypes.CONTACT_INFO,
|
|
|
|
|
ContactExchangeConstants.PROTOCOL_VERSION,
|
|
|
|
|
SocialBackupExchangeRecordTypes.RETURN_SHARD,
|
|
|
|
|
clientHelper.toByteArray(payload)));
|
|
|
|
|
recordWriter.flush();
|
|
|
|
|
LOG.info("Sent contact info");
|
|
|
|
|
LOG.info("Sent shard and encrypted backup");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ContactInfo receiveContactInfo(RecordReader recordReader)
|
|
|
|
|
private ReturnShardPayload receiveShardPayload(RecordReader recordReader)
|
|
|
|
|
throws IOException {
|
|
|
|
|
Record record = recordReader.readRecord(ACCEPT, IGNORE);
|
|
|
|
|
if (record == null) throw new EOFException();
|
|
|
|
|
LOG.info("Received contact info");
|
|
|
|
|
LOG.info("Received shard and encrypted backup");
|
|
|
|
|
BdfList payload = clientHelper.toList(record.getPayload());
|
|
|
|
|
checkSize(payload, 4);
|
|
|
|
|
Author author = clientHelper.parseAndValidateAuthor(payload.getList(0));
|
|
|
|
|
BdfDictionary props = payload.getDictionary(1);
|
|
|
|
|
Map<TransportId, TransportProperties> properties =
|
|
|
|
|
clientHelper.parseAndValidateTransportPropertiesMap(props);
|
|
|
|
|
byte[] signature = payload.getRaw(2);
|
|
|
|
|
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
|
|
|
|
long timestamp = payload.getLong(3);
|
|
|
|
|
checkSize(payload, 3);
|
|
|
|
|
BdfList shardList = payload.getList(0);
|
|
|
|
|
Shard shard = new Shard(shardList.getRaw(0), shardList.getRaw(1));
|
|
|
|
|
BackupPayload backupPayload = new BackupPayload(payload.getRaw(1));
|
|
|
|
|
// Map<TransportId, TransportProperties> properties =
|
|
|
|
|
// clientHelper.parseAndValidateTransportPropertiesMap(props);
|
|
|
|
|
// byte[] signature = payload.getRaw(2);
|
|
|
|
|
// checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
|
|
|
|
long timestamp = payload.getLong(2);
|
|
|
|
|
if (timestamp < 0) throw new FormatException();
|
|
|
|
|
return new ContactInfo(author, properties, signature, timestamp);
|
|
|
|
|
return new ReturnShardPayload(shard, backupPayload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void sendAcknowledgement(RecordWriter recordWriter,
|
|
|
|
|
Map<TransportId, TransportProperties> properties,
|
|
|
|
|
long timestamp) throws IOException {
|
|
|
|
|
|
|
|
|
|
private static class ContactInfo {
|
|
|
|
|
BdfList payload = BdfList.of(timestamp);
|
|
|
|
|
|
|
|
|
|
private final Author author;
|
|
|
|
|
private final Map<TransportId, TransportProperties> properties;
|
|
|
|
|
private final byte[] signature;
|
|
|
|
|
private final long timestamp;
|
|
|
|
|
recordWriter.writeRecord(new Record(
|
|
|
|
|
ContactExchangeConstants.PROTOCOL_VERSION,
|
|
|
|
|
SocialBackupExchangeRecordTypes.ACKNOWLEDGEMENT,
|
|
|
|
|
clientHelper.toByteArray(payload)));
|
|
|
|
|
recordWriter.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ContactInfo(Author author,
|
|
|
|
|
Map<TransportId, TransportProperties> properties,
|
|
|
|
|
byte[] signature, long timestamp) {
|
|
|
|
|
this.author = author;
|
|
|
|
|
this.properties = properties;
|
|
|
|
|
this.signature = signature;
|
|
|
|
|
this.timestamp = timestamp;
|
|
|
|
|
}
|
|
|
|
|
private void receiveAcknowledgement(RecordReader recordReader)
|
|
|
|
|
throws IOException {
|
|
|
|
|
Record record = recordReader.readRecord(ACCEPT, IGNORE);
|
|
|
|
|
if (record == null) throw new EOFException();
|
|
|
|
|
BdfList payload = clientHelper.toList(record.getPayload());
|
|
|
|
|
checkSize(payload, 1);
|
|
|
|
|
long timestamp = payload.getLong(0);
|
|
|
|
|
if (timestamp < 0) throw new FormatException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|