mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Rewrote the bundle reading and writing code to eliminate copying. Signatures and digests are now calculated on the fly as the data is read or written. This is a little bit tricky in the case of reading because ReaderImpl uses a lookahead byte, so the signature and message digest need to lag one byte behind.
This commit is contained in:
@@ -9,13 +9,9 @@ import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.Status;
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
* Abstract superclass containing code shared by ReadWriteLockDatabaseComponent
|
||||
* and SynchronizedDatabaseComponent.
|
||||
@@ -28,8 +24,6 @@ DatabaseCleaner.Callback {
|
||||
|
||||
protected final Database<Txn> db;
|
||||
protected final DatabaseCleaner cleaner;
|
||||
protected final Provider<HeaderBuilder> headerBuilderProvider;
|
||||
protected final Provider<BatchBuilder> batchBuilderProvider;
|
||||
|
||||
private final Object spaceLock = new Object();
|
||||
private final Object writeLock = new Object();
|
||||
@@ -37,13 +31,9 @@ DatabaseCleaner.Callback {
|
||||
private long timeOfLastCheck = 0L; // Locking: spaceLock
|
||||
private volatile boolean writesAllowed = true;
|
||||
|
||||
DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
DatabaseComponentImpl(Database<Txn> db, DatabaseCleaner cleaner) {
|
||||
this.db = db;
|
||||
this.cleaner = cleaner;
|
||||
this.headerBuilderProvider = headerBuilderProvider;
|
||||
this.batchBuilderProvider = batchBuilderProvider;
|
||||
}
|
||||
|
||||
public void open(boolean resume) throws DbException {
|
||||
|
||||
@@ -52,7 +52,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " authorId XXXX NOT NULL,"
|
||||
+ " timestamp BIGINT NOT NULL,"
|
||||
+ " size INT NOT NULL,"
|
||||
+ " body BLOB NOT NULL,"
|
||||
+ " raw BLOB NOT NULL,"
|
||||
+ " sendability INT NOT NULL,"
|
||||
+ " PRIMARY KEY (messageId),"
|
||||
+ " FOREIGN KEY (groupId) REFERENCES localSubscriptions (groupId)"
|
||||
@@ -458,7 +458,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
String sql = "INSERT INTO messages"
|
||||
+ " (messageId, parentId, groupId, authorId, timestamp, size,"
|
||||
+ " body, sendability)"
|
||||
+ " raw, sendability)"
|
||||
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getId().getBytes());
|
||||
@@ -834,7 +834,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql =
|
||||
"SELECT parentId, groupId, authorId, timestamp, size, body"
|
||||
"SELECT parentId, groupId, authorId, timestamp, size, raw"
|
||||
+ " FROM messages WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
@@ -847,14 +847,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
long timestamp = rs.getLong(4);
|
||||
int size = rs.getInt(5);
|
||||
Blob b = rs.getBlob(6);
|
||||
byte[] body = b.getBytes(1, size);
|
||||
assert body.length == size;
|
||||
byte[] raw = b.getBytes(1, size);
|
||||
assert raw.length == size;
|
||||
boolean more = rs.next();
|
||||
assert !more;
|
||||
rs.close();
|
||||
ps.close();
|
||||
return messageFactory.createMessage(m, parent, group, author,
|
||||
timestamp, body);
|
||||
timestamp, raw);
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
|
||||
@@ -2,9 +2,10 @@ package net.sf.briar.db;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
@@ -17,19 +18,16 @@ import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.BundleReader;
|
||||
import net.sf.briar.api.protocol.BundleWriter;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
* An implementation of DatabaseComponent using reentrant read-write locks.
|
||||
@@ -59,10 +57,8 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
new ReentrantReadWriteLock(true);
|
||||
|
||||
@Inject
|
||||
ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
super(db, cleaner, headerBuilderProvider, batchBuilderProvider);
|
||||
ReadWriteLockDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) {
|
||||
super(db, cleaner);
|
||||
}
|
||||
|
||||
public void close() throws DbException {
|
||||
@@ -195,18 +191,18 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
||||
IOException, GeneralSecurityException {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||
HeaderBuilder h;
|
||||
Set<BatchId> acks;
|
||||
Set<GroupId> subs;
|
||||
Map<String, String> transports;
|
||||
// Add acks
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
h = headerBuilderProvider.get();
|
||||
messageStatusLock.writeLock().lock();
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
||||
h.addAcks(acks);
|
||||
acks = db.removeBatchesToAck(txn, c);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + acks.size() + " acks");
|
||||
db.commitTransaction(txn);
|
||||
@@ -228,8 +224,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
Set<GroupId> subs = db.getSubscriptions(txn);
|
||||
h.addSubscriptions(subs);
|
||||
subs = db.getSubscriptions(txn);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||
db.commitTransaction(txn);
|
||||
@@ -251,8 +246,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
Map<String, String> transports = db.getTransports(txn);
|
||||
h.addTransports(transports);
|
||||
transports = db.getTransports(txn);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + transports.size() + " transports");
|
||||
db.commitTransaction(txn);
|
||||
@@ -266,28 +260,16 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
} finally {
|
||||
contactLock.readLock().unlock();
|
||||
}
|
||||
// Sign the header and add it to the bundle
|
||||
Header header = h.build();
|
||||
long capacity = b.getCapacity();
|
||||
capacity -= header.getSize();
|
||||
b.addHeader(header);
|
||||
// Add the header to the bundle
|
||||
b.addHeader(acks, subs, transports);
|
||||
// Add as many messages as possible to the bundle
|
||||
while(true) {
|
||||
Batch batch = fillBatch(c, capacity);
|
||||
if(batch == null) break; // No more messages to send
|
||||
b.addBatch(batch);
|
||||
long size = batch.getSize();
|
||||
capacity -= size;
|
||||
// If the batch is less than half full, stop trying - there may be
|
||||
// more messages trickling in but we can't wait forever
|
||||
if(size * 2 < Batch.MAX_SIZE) break;
|
||||
}
|
||||
b.close();
|
||||
while(fillBatch(c, b));
|
||||
b.finish();
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
||||
System.gc();
|
||||
}
|
||||
|
||||
private Batch fillBatch(ContactId c, long capacity) throws DbException,
|
||||
private boolean fillBatch(ContactId c, BundleWriter b) throws DbException,
|
||||
IOException, GeneralSecurityException {
|
||||
contactLock.readLock().lock();
|
||||
try {
|
||||
@@ -295,31 +277,38 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
messageLock.readLock().lock();
|
||||
try {
|
||||
Set<MessageId> sent;
|
||||
Batch batch;
|
||||
int bytesSent = 0;
|
||||
BatchId batchId;
|
||||
messageStatusLock.readLock().lock();
|
||||
try {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
capacity = Math.min(capacity, Batch.MAX_SIZE);
|
||||
long capacity = Math.min(b.getRemainingCapacity(),
|
||||
Batch.MAX_SIZE);
|
||||
Iterator<MessageId> it =
|
||||
db.getSendableMessages(txn, c, capacity).iterator();
|
||||
if(!it.hasNext()) {
|
||||
db.commitTransaction(txn);
|
||||
return null; // No more messages to send
|
||||
return false; // No more messages to send
|
||||
}
|
||||
sent = new HashSet<MessageId>();
|
||||
BatchBuilder b = batchBuilderProvider.get();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
while(it.hasNext()) {
|
||||
MessageId m = it.next();
|
||||
b.addMessage(db.getMessage(txn, m));
|
||||
Message message = db.getMessage(txn, m);
|
||||
bytesSent += message.getSize();
|
||||
messages.add(message);
|
||||
sent.add(m);
|
||||
}
|
||||
batch = b.build();
|
||||
batchId = b.addBatch(messages);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
} catch(SignatureException e) {
|
||||
} catch(IOException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
} catch(GeneralSecurityException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
@@ -332,9 +321,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
assert !sent.isEmpty();
|
||||
db.addOutstandingBatch(txn, c, batch.getId(), sent);
|
||||
db.addOutstandingBatch(txn, c, batchId, sent);
|
||||
db.commitTransaction(txn);
|
||||
return batch;
|
||||
// Don't create another batch if this one was half-empty
|
||||
return bytesSent > Batch.MAX_SIZE / 2;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
@@ -443,9 +433,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
|
||||
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||
IOException, GeneralSecurityException {
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received bundle from " + c + ", "
|
||||
+ b.getSize() + " bytes");
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
||||
Header h;
|
||||
// Mark all messages in acked batches as seen
|
||||
contactLock.readLock().lock();
|
||||
@@ -536,7 +524,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + batches + " batches");
|
||||
b.close();
|
||||
b.finish();
|
||||
retransmitLostBatches(c, h.getId());
|
||||
System.gc();
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package net.sf.briar.db;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
@@ -16,19 +18,16 @@ import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.BundleReader;
|
||||
import net.sf.briar.api.protocol.BundleWriter;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
/**
|
||||
* An implementation of DatabaseComponent using Java synchronization. This
|
||||
@@ -52,10 +51,8 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
private final Object transportLock = new Object();
|
||||
|
||||
@Inject
|
||||
SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
super(db, cleaner, headerBuilderProvider, batchBuilderProvider);
|
||||
SynchronizedDatabaseComponent(Database<Txn> db, DatabaseCleaner cleaner) {
|
||||
super(db, cleaner);
|
||||
}
|
||||
|
||||
public void close() throws DbException {
|
||||
@@ -148,16 +145,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
||||
IOException, GeneralSecurityException {
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||
HeaderBuilder h;
|
||||
Set<BatchId> acks;
|
||||
Set<GroupId> subs;
|
||||
Map<String, String> transports;
|
||||
// Add acks
|
||||
synchronized(contactLock) {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
h = headerBuilderProvider.get();
|
||||
synchronized(messageStatusLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
||||
h.addAcks(acks);
|
||||
acks = db.removeBatchesToAck(txn, c);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + acks.size() + " acks");
|
||||
db.commitTransaction(txn);
|
||||
@@ -173,8 +170,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(subscriptionLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
Set<GroupId> subs = db.getSubscriptions(txn);
|
||||
h.addSubscriptions(subs);
|
||||
subs = db.getSubscriptions(txn);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||
db.commitTransaction(txn);
|
||||
@@ -190,8 +186,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(transportLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
Map<String, String> transports = db.getTransports(txn);
|
||||
h.addTransports(transports);
|
||||
transports = db.getTransports(txn);
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Added " + transports.size() + " transports");
|
||||
db.commitTransaction(txn);
|
||||
@@ -201,28 +196,16 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sign the header and add it to the bundle
|
||||
Header header = h.build();
|
||||
long capacity = b.getCapacity();
|
||||
capacity -= header.getSize();
|
||||
b.addHeader(header);
|
||||
// Add the header to the bundle
|
||||
b.addHeader(acks, subs, transports);
|
||||
// Add as many messages as possible to the bundle
|
||||
while(true) {
|
||||
Batch batch = fillBatch(c, capacity);
|
||||
if(batch == null) break; // No more messages to send
|
||||
b.addBatch(batch);
|
||||
long size = batch.getSize();
|
||||
capacity -= size;
|
||||
// If the batch is less than half full, stop trying - there may be
|
||||
// more messages trickling in but we can't wait forever
|
||||
if(size * 2 < Batch.MAX_SIZE) break;
|
||||
}
|
||||
b.close();
|
||||
while(fillBatch(c, b));
|
||||
b.finish();
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
||||
System.gc();
|
||||
}
|
||||
|
||||
private Batch fillBatch(ContactId c, long capacity) throws DbException,
|
||||
private boolean fillBatch(ContactId c, BundleWriter b) throws DbException,
|
||||
IOException, GeneralSecurityException {
|
||||
synchronized(contactLock) {
|
||||
if(!containsContact(c)) throw new NoSuchContactException();
|
||||
@@ -230,26 +213,31 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
synchronized(messageStatusLock) {
|
||||
Txn txn = db.startTransaction();
|
||||
try {
|
||||
capacity = Math.min(capacity, Batch.MAX_SIZE);
|
||||
long capacity = Math.min(b.getRemainingCapacity(),
|
||||
Batch.MAX_SIZE);
|
||||
Iterator<MessageId> it =
|
||||
db.getSendableMessages(txn, c, capacity).iterator();
|
||||
if(!it.hasNext()) {
|
||||
db.commitTransaction(txn);
|
||||
return null; // No more messages to send
|
||||
return false; // No more messages to send
|
||||
}
|
||||
BatchBuilder b = batchBuilderProvider.get();
|
||||
Set<MessageId> sent = new HashSet<MessageId>();
|
||||
List<Message> messages = new ArrayList<Message>();
|
||||
int bytesSent = 0;
|
||||
while(it.hasNext()) {
|
||||
MessageId m = it.next();
|
||||
b.addMessage(db.getMessage(txn, m));
|
||||
Message message = db.getMessage(txn, m);
|
||||
bytesSent += message.getSize();
|
||||
messages.add(message);
|
||||
sent.add(m);
|
||||
}
|
||||
Batch batch = b.build();
|
||||
BatchId batchId = b.addBatch(messages);
|
||||
// Record the contents of the batch
|
||||
assert !sent.isEmpty();
|
||||
db.addOutstandingBatch(txn, c, batch.getId(), sent);
|
||||
db.addOutstandingBatch(txn, c, batchId, sent);
|
||||
db.commitTransaction(txn);
|
||||
return batch;
|
||||
// Don't create another batch if this one was half-empty
|
||||
return bytesSent > Batch.MAX_SIZE / 2;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
@@ -337,9 +325,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
|
||||
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||
IOException, GeneralSecurityException {
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received bundle from " + c + ", "
|
||||
+ b.getSize() + " bytes");
|
||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
||||
Header h;
|
||||
// Mark all messages in acked batches as seen
|
||||
synchronized(contactLock) {
|
||||
@@ -409,7 +395,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
||||
}
|
||||
if(LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Received " + batches + " batches");
|
||||
b.close();
|
||||
b.finish();
|
||||
retransmitLostBatches(c, h.getId());
|
||||
System.gc();
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
abstract class BatchBuilderImpl implements BatchBuilder {
|
||||
|
||||
protected final List<Message> messages = new ArrayList<Message>();
|
||||
protected final KeyPair keyPair;
|
||||
protected final Signature signature;
|
||||
protected final MessageDigest messageDigest;
|
||||
protected final WriterFactory writerFactory;
|
||||
|
||||
protected BatchBuilderImpl(KeyPair keyPair, Signature signature,
|
||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
||||
this.keyPair = keyPair;
|
||||
this.signature = signature;
|
||||
this.messageDigest = messageDigest;
|
||||
this.writerFactory = writerFactory;
|
||||
}
|
||||
|
||||
public void addMessage(Message m) {
|
||||
messages.add(m);
|
||||
}
|
||||
}
|
||||
12
components/net/sf/briar/protocol/BatchFactory.java
Normal file
12
components/net/sf/briar/protocol/BatchFactory.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
|
||||
interface BatchFactory {
|
||||
|
||||
Batch createBatch(BatchId id, List<Message> messages);
|
||||
}
|
||||
14
components/net/sf/briar/protocol/BatchFactoryImpl.java
Normal file
14
components/net/sf/briar/protocol/BatchFactoryImpl.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
|
||||
public class BatchFactoryImpl implements BatchFactory {
|
||||
|
||||
public Batch createBatch(BatchId id, List<Message> messages) {
|
||||
return new BatchImpl(id, messages);
|
||||
}
|
||||
}
|
||||
@@ -10,30 +10,18 @@ import net.sf.briar.api.protocol.Message;
|
||||
class BatchImpl implements Batch {
|
||||
|
||||
private final BatchId id;
|
||||
private final long size;
|
||||
private final List<Message> messages;
|
||||
private final byte[] signature;
|
||||
|
||||
BatchImpl(BatchId id, long size, List<Message> messages, byte[] signature) {
|
||||
BatchImpl(BatchId id, List<Message> messages) {
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.messages = messages;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public BatchId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Iterable<Message> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,70 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.BundleReader;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageParser;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.serial.FormatException;
|
||||
import net.sf.briar.api.serial.Raw;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
|
||||
/** A bundle that deserialises its contents on demand using a reader. */
|
||||
class BundleReaderImpl implements BundleReader {
|
||||
|
||||
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
||||
|
||||
private final SigningDigestingInputStream in;
|
||||
private final Reader r;
|
||||
private final long size;
|
||||
private final PublicKey publicKey;
|
||||
private final Signature signature;
|
||||
private final MessageDigest messageDigest;
|
||||
private final MessageParser messageParser;
|
||||
private final Provider<HeaderBuilder> headerBuilderProvider;
|
||||
private final Provider<BatchBuilder> batchBuilderProvider;
|
||||
private final HeaderFactory headerFactory;
|
||||
private final BatchFactory batchFactory;
|
||||
private State state = State.START;
|
||||
|
||||
BundleReaderImpl(Reader r, long size, MessageParser messageParser,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
this.r = r;
|
||||
this.size = size;
|
||||
BundleReaderImpl(InputStream in, ReaderFactory readerFactory,
|
||||
PublicKey publicKey, Signature signature,
|
||||
MessageDigest messageDigest, MessageParser messageParser,
|
||||
HeaderFactory headerFactory, BatchFactory batchFactory) {
|
||||
this.in = new SigningDigestingInputStream(in, signature, messageDigest);
|
||||
r = readerFactory.createReader(this.in);
|
||||
this.publicKey = publicKey;
|
||||
this.signature = signature;
|
||||
this.messageDigest = messageDigest;
|
||||
this.messageParser = messageParser;
|
||||
this.headerBuilderProvider = headerBuilderProvider;
|
||||
this.batchBuilderProvider = batchBuilderProvider;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
this.headerFactory = headerFactory;
|
||||
this.batchFactory = batchFactory;
|
||||
}
|
||||
|
||||
public Header getHeader() throws IOException, GeneralSecurityException {
|
||||
if(state != State.START) throw new IllegalStateException();
|
||||
state = State.FIRST_BATCH;
|
||||
// Initialise the input stream
|
||||
signature.initVerify(publicKey);
|
||||
messageDigest.reset();
|
||||
// Read the signed data
|
||||
in.setDigesting(true);
|
||||
in.setSigning(true);
|
||||
r.setReadLimit(Header.MAX_SIZE);
|
||||
Set<BatchId> acks = new HashSet<BatchId>();
|
||||
for(Raw raw : r.readList(Raw.class)) {
|
||||
@@ -64,15 +78,16 @@ class BundleReaderImpl implements BundleReader {
|
||||
if(b.length != UniqueId.LENGTH) throw new FormatException();
|
||||
subs.add(new GroupId(b));
|
||||
}
|
||||
Map<String, String> transports = r.readMap(String.class, String.class);
|
||||
Map<String, String> transports =
|
||||
r.readMap(String.class, String.class);
|
||||
in.setSigning(false);
|
||||
// Read and verify the signature
|
||||
byte[] sig = r.readRaw();
|
||||
state = State.FIRST_BATCH;
|
||||
HeaderBuilder h = headerBuilderProvider.get();
|
||||
h.addAcks(acks);
|
||||
h.addSubscriptions(subs);
|
||||
h.addTransports(transports);
|
||||
h.setSignature(sig);
|
||||
return h.build();
|
||||
in.setDigesting(false);
|
||||
if(!signature.verify(sig)) throw new SignatureException();
|
||||
// Build and return the header
|
||||
BundleId id = new BundleId(messageDigest.digest());
|
||||
return headerFactory.createHeader(id, acks, subs, transports);
|
||||
}
|
||||
|
||||
public Batch getNextBatch() throws IOException, GeneralSecurityException {
|
||||
@@ -86,19 +101,31 @@ class BundleReaderImpl implements BundleReader {
|
||||
state = State.END;
|
||||
return null;
|
||||
}
|
||||
// Initialise the input stream
|
||||
signature.initVerify(publicKey);
|
||||
messageDigest.reset();
|
||||
// Read the signed data
|
||||
in.setDigesting(true);
|
||||
in.setSigning(true);
|
||||
r.setReadLimit(Batch.MAX_SIZE);
|
||||
List<Raw> messages = r.readList(Raw.class);
|
||||
BatchBuilder b = batchBuilderProvider.get();
|
||||
for(Raw r : messages) {
|
||||
Message m = messageParser.parseMessage(r.getBytes());
|
||||
b.addMessage(m);
|
||||
}
|
||||
List<Raw> rawMessages = r.readList(Raw.class);
|
||||
in.setSigning(false);
|
||||
// Read and verify the signature
|
||||
byte[] sig = r.readRaw();
|
||||
b.setSignature(sig);
|
||||
return b.build();
|
||||
in.setDigesting(false);
|
||||
if(!signature.verify(sig)) throw new SignatureException();
|
||||
// Parse the messages
|
||||
List<Message> messages = new ArrayList<Message>(rawMessages.size());
|
||||
for(Raw r : rawMessages) {
|
||||
Message m = messageParser.parseMessage(r.getBytes());
|
||||
messages.add(m);
|
||||
}
|
||||
// Build and return the batch
|
||||
BatchId id = new BatchId(messageDigest.digest());
|
||||
return batchFactory.createBatch(id, messages);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
public void finish() throws IOException {
|
||||
r.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,100 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.BundleWriter;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
/** A bundle builder that serialises its contents using a writer. */
|
||||
class BundleWriterImpl implements BundleWriter {
|
||||
|
||||
private static enum State { START, FIRST_BATCH, MORE_BATCHES, END };
|
||||
|
||||
private final SigningOutputStream out;
|
||||
private final Writer w;
|
||||
private final PrivateKey privateKey;
|
||||
private final Signature signature;
|
||||
private final MessageDigest messageDigest;
|
||||
private final long capacity;
|
||||
private State state = State.START;
|
||||
|
||||
BundleWriterImpl(Writer w, long capacity) {
|
||||
this.w = w;
|
||||
BundleWriterImpl(OutputStream out, WriterFactory writerFactory,
|
||||
PrivateKey privateKey, Signature signature,
|
||||
MessageDigest messageDigest, long capacity) {
|
||||
OutputStream out1 = new DigestOutputStream(out, messageDigest);
|
||||
this.out = new SigningOutputStream(out1, signature);
|
||||
w = writerFactory.createWriter(this.out);
|
||||
this.privateKey = privateKey;
|
||||
this.signature = signature;
|
||||
this.messageDigest = messageDigest;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public long getCapacity() {
|
||||
return capacity;
|
||||
public long getRemainingCapacity() {
|
||||
return capacity - w.getRawBytesWritten();
|
||||
}
|
||||
|
||||
public void addHeader(Header h) throws IOException {
|
||||
public BundleId addHeader(Iterable<BatchId> acks, Iterable<GroupId> subs,
|
||||
Map<String, String> transports) throws IOException,
|
||||
GeneralSecurityException {
|
||||
if(state != State.START) throw new IllegalStateException();
|
||||
// Initialise the output stream
|
||||
signature.initSign(privateKey);
|
||||
messageDigest.reset();
|
||||
// Write the data to be signed
|
||||
out.setSigning(true);
|
||||
w.writeListStart();
|
||||
for(BatchId ack : h.getAcks()) w.writeRaw(ack);
|
||||
for(BatchId ack : acks) w.writeRaw(ack);
|
||||
w.writeListEnd();
|
||||
w.writeListStart();
|
||||
for(GroupId sub : h.getSubscriptions()) w.writeRaw(sub);
|
||||
for(GroupId sub : subs) w.writeRaw(sub);
|
||||
w.writeListEnd();
|
||||
w.writeMap(h.getTransports());
|
||||
w.writeRaw(h.getSignature());
|
||||
w.writeMap(transports);
|
||||
out.setSigning(false);
|
||||
// Create and write the signature
|
||||
byte[] sig = signature.sign();
|
||||
w.writeRaw(sig);
|
||||
// Calculate and return the ID
|
||||
state = State.FIRST_BATCH;
|
||||
return new BundleId(messageDigest.digest());
|
||||
}
|
||||
|
||||
public void addBatch(Batch b) throws IOException {
|
||||
public BatchId addBatch(Iterable<Message> messages) throws IOException,
|
||||
GeneralSecurityException {
|
||||
if(state == State.FIRST_BATCH) {
|
||||
w.writeListStart();
|
||||
state = State.MORE_BATCHES;
|
||||
}
|
||||
if(state != State.MORE_BATCHES) throw new IllegalStateException();
|
||||
// Initialise the output stream
|
||||
signature.initSign(privateKey);
|
||||
messageDigest.reset();
|
||||
// Write the data to be signed
|
||||
out.setSigning(true);
|
||||
w.writeListStart();
|
||||
for(Message m : b.getMessages()) w.writeRaw(m.getBytes());
|
||||
for(Message m : messages) w.writeRaw(m);
|
||||
w.writeListEnd();
|
||||
w.writeRaw(b.getSignature());
|
||||
out.setSigning(false);
|
||||
// Create and write the signature
|
||||
byte[] sig = signature.sign();
|
||||
w.writeRaw(sig);
|
||||
// Calculate and return the ID
|
||||
return new BatchId(messageDigest.digest());
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
public void finish() throws IOException {
|
||||
if(state == State.FIRST_BATCH) {
|
||||
w.writeListStart();
|
||||
state = State.MORE_BATCHES;
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
abstract class HeaderBuilderImpl implements HeaderBuilder {
|
||||
|
||||
protected final List<BatchId> acks = new ArrayList<BatchId>();
|
||||
protected final List<GroupId> subs = new ArrayList<GroupId>();
|
||||
protected final Map<String, String> transports =
|
||||
new LinkedHashMap<String, String>();
|
||||
|
||||
protected final KeyPair keyPair;
|
||||
protected final Signature signature;
|
||||
protected final MessageDigest messageDigest;
|
||||
protected final WriterFactory writerFactory;
|
||||
|
||||
protected HeaderBuilderImpl(KeyPair keyPair, Signature signature,
|
||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
||||
this.keyPair = keyPair;
|
||||
this.signature = signature;
|
||||
this.messageDigest = messageDigest;
|
||||
this.writerFactory = writerFactory;
|
||||
}
|
||||
|
||||
public void addAcks(Iterable<BatchId> acks) {
|
||||
for(BatchId ack : acks) this.acks.add(ack);
|
||||
}
|
||||
|
||||
public void addSubscriptions(Iterable<GroupId> subs) {
|
||||
for(GroupId sub : subs) this.subs.add(sub);
|
||||
}
|
||||
|
||||
public void addTransports(Map<String, String> transports) {
|
||||
for(String key : transports.keySet()) {
|
||||
this.transports.put(key, transports.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
15
components/net/sf/briar/protocol/HeaderFactory.java
Normal file
15
components/net/sf/briar/protocol/HeaderFactory.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
|
||||
interface HeaderFactory {
|
||||
|
||||
Header createHeader(BundleId id, Set<BatchId> acks, Set<GroupId> subs,
|
||||
Map<String, String> transports);
|
||||
}
|
||||
17
components/net/sf/briar/protocol/HeaderFactoryImpl.java
Normal file
17
components/net/sf/briar/protocol/HeaderFactoryImpl.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
|
||||
class HeaderFactoryImpl implements HeaderFactory {
|
||||
|
||||
public Header createHeader(BundleId id, Set<BatchId> acks,
|
||||
Set<GroupId> subs, Map<String, String> transports) {
|
||||
return new HeaderImpl(id, acks, subs, transports);
|
||||
}
|
||||
}
|
||||
@@ -12,44 +12,31 @@ import net.sf.briar.api.protocol.Header;
|
||||
class HeaderImpl implements Header {
|
||||
|
||||
private final BundleId id;
|
||||
private final long size;
|
||||
private final Set<BatchId> acks;
|
||||
private final Set<GroupId> subscriptions;
|
||||
private final Set<GroupId> subs;
|
||||
private final Map<String, String> transports;
|
||||
private final byte[] signature;
|
||||
|
||||
HeaderImpl(BundleId id, long size, Set<BatchId> acks,
|
||||
Set<GroupId> subscriptions, Map<String, String> transports,
|
||||
byte[] signature) {
|
||||
HeaderImpl(BundleId id, Set<BatchId> acks, Set<GroupId> subs,
|
||||
Map<String, String> transports) {
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.acks = acks;
|
||||
this.subscriptions = subscriptions;
|
||||
this.subs = subs;
|
||||
this.transports = transports;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public BundleId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Set<BatchId> getAcks() {
|
||||
return acks;
|
||||
}
|
||||
|
||||
public Set<GroupId> getSubscriptions() {
|
||||
return subscriptions;
|
||||
return subs;
|
||||
}
|
||||
|
||||
public Map<String, String> getTransports() {
|
||||
return transports;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
public class IncomingBatchBuilder extends BatchBuilderImpl {
|
||||
|
||||
IncomingBatchBuilder(KeyPair keyPair, Signature signature,
|
||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
||||
super(keyPair, signature, messageDigest, writerFactory);
|
||||
}
|
||||
|
||||
private byte[] sig = null;
|
||||
|
||||
public void setSignature(byte[] sig) {
|
||||
this.sig = sig;
|
||||
}
|
||||
|
||||
public Batch build() throws IOException, GeneralSecurityException {
|
||||
if(sig == null) throw new IllegalStateException();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeList(messages);
|
||||
byte[] signable = out.toByteArray();
|
||||
signature.initVerify(keyPair.getPublic());
|
||||
signature.update(signable);
|
||||
if(!signature.verify(sig)) throw new SignatureException();
|
||||
w.writeRaw(sig);
|
||||
w.close();
|
||||
byte[] raw = out.toByteArray();
|
||||
messageDigest.reset();
|
||||
messageDigest.update(raw);
|
||||
BatchId id = new BatchId(messageDigest.digest());
|
||||
return new BatchImpl(id, raw.length, messages, sig);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
class IncomingHeaderBuilder extends HeaderBuilderImpl {
|
||||
|
||||
private byte[] sig = null;
|
||||
|
||||
IncomingHeaderBuilder(KeyPair keyPair, Signature signature,
|
||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
||||
super(keyPair, signature, messageDigest, writerFactory);
|
||||
}
|
||||
|
||||
public void setSignature(byte[] sig) {
|
||||
this.sig = sig;
|
||||
}
|
||||
|
||||
public Header build() throws IOException, GeneralSecurityException {
|
||||
if(sig == null) throw new IllegalStateException();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeList(acks);
|
||||
w.writeList(subs);
|
||||
w.writeMap(transports);
|
||||
byte[] signable = out.toByteArray();
|
||||
signature.initVerify(keyPair.getPublic());
|
||||
signature.update(signable);
|
||||
if(!signature.verify(sig)) throw new SignatureException();
|
||||
w.writeRaw(sig);
|
||||
w.close();
|
||||
byte[] raw = out.toByteArray();
|
||||
messageDigest.reset();
|
||||
messageDigest.update(raw);
|
||||
BundleId id = new BundleId(messageDigest.digest());
|
||||
Set<BatchId> ackSet = new HashSet<BatchId>(acks);
|
||||
Set<GroupId> subSet = new HashSet<GroupId>(subs);
|
||||
return new HeaderImpl(id, raw.length, ackSet, subSet, transports, sig);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
public class OutgoingBatchBuilder extends BatchBuilderImpl {
|
||||
|
||||
OutgoingBatchBuilder(KeyPair keyPair, Signature signature,
|
||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
||||
super(keyPair, signature, messageDigest, writerFactory);
|
||||
}
|
||||
|
||||
public void setSignature(byte[] sig) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Batch build() throws IOException, GeneralSecurityException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeList(messages);
|
||||
byte[] signable = out.toByteArray();
|
||||
signature.initSign(keyPair.getPrivate());
|
||||
signature.update(signable);
|
||||
byte[] sig = signature.sign();
|
||||
w.writeRaw(sig);
|
||||
w.close();
|
||||
byte[] raw = out.toByteArray();
|
||||
messageDigest.reset();
|
||||
messageDigest.update(raw);
|
||||
BatchId id = new BatchId(messageDigest.digest());
|
||||
return new BatchImpl(id, raw.length, messages, sig);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
public class OutgoingHeaderBuilder extends HeaderBuilderImpl {
|
||||
|
||||
OutgoingHeaderBuilder(KeyPair keyPair, Signature signature,
|
||||
MessageDigest messageDigest, WriterFactory writerFactory) {
|
||||
super(keyPair, signature, messageDigest, writerFactory);
|
||||
}
|
||||
|
||||
public void setSignature(byte[] sig) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Header build() throws IOException, GeneralSecurityException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeList(acks);
|
||||
w.writeList(subs);
|
||||
w.writeMap(transports);
|
||||
byte[] signable = out.toByteArray();
|
||||
signature.initSign(keyPair.getPrivate());
|
||||
signature.update(signable);
|
||||
byte[] sig = signature.sign();
|
||||
w.writeRaw(sig);
|
||||
w.close();
|
||||
byte[] raw = out.toByteArray();
|
||||
messageDigest.reset();
|
||||
messageDigest.update(raw);
|
||||
BundleId id = new BundleId(messageDigest.digest());
|
||||
Set<BatchId> ackSet = new HashSet<BatchId>(acks);
|
||||
Set<GroupId> subSet = new HashSet<GroupId>(subs);
|
||||
return new HeaderImpl(id, raw.length, ackSet, subSet, transports, sig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
|
||||
/**
|
||||
* An input stream that passes its input through a signature and a message
|
||||
* digest. The signature and message digest lag behind the input by one byte
|
||||
* until the end of the input is reached, to allow users of this class to
|
||||
* maintain one byte of lookahead without affecting the signature or digest.
|
||||
*/
|
||||
class SigningDigestingInputStream extends FilterInputStream {
|
||||
|
||||
private final Signature signature;
|
||||
private final MessageDigest messageDigest;
|
||||
private byte nextByte = 0;
|
||||
private boolean started = false, eof = false;
|
||||
private boolean signing = false, digesting = false;
|
||||
|
||||
protected SigningDigestingInputStream(InputStream in, Signature signature,
|
||||
MessageDigest messageDigest) {
|
||||
super(in);
|
||||
this.signature = signature;
|
||||
this.messageDigest = messageDigest;
|
||||
}
|
||||
|
||||
public void setSigning(boolean signing) {
|
||||
this.signing = signing;
|
||||
}
|
||||
|
||||
public void setDigesting(boolean digesting) {
|
||||
this.digesting = digesting;
|
||||
}
|
||||
|
||||
private void write(byte b) throws IOException {
|
||||
if(signing) {
|
||||
try {
|
||||
signature.update(b);
|
||||
} catch(SignatureException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
if(digesting) messageDigest.update(b);
|
||||
}
|
||||
|
||||
private void write(byte[] b, int off, int len) throws IOException {
|
||||
if(signing) {
|
||||
try {
|
||||
signature.update(b, off, len);
|
||||
} catch(SignatureException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
if(digesting) messageDigest.update(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readLimit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if(eof) return -1;
|
||||
if(started) write(nextByte);
|
||||
started = true;
|
||||
int i = in.read();
|
||||
if(i == -1) {
|
||||
eof = true;
|
||||
return -1;
|
||||
}
|
||||
nextByte = (byte) (i > 127 ? i - 256 : i);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if(eof) return -1;
|
||||
if(started) write(nextByte);
|
||||
started = true;
|
||||
int read = in.read(b, off, len);
|
||||
if(read == -1) {
|
||||
eof = true;
|
||||
return -1;
|
||||
}
|
||||
if(read > 0) {
|
||||
write(b, off, read - 1);
|
||||
nextByte = b[off + read - 1];
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
52
components/net/sf/briar/protocol/SigningOutputStream.java
Normal file
52
components/net/sf/briar/protocol/SigningOutputStream.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
|
||||
/** An output stream that passes its output through a signature. */
|
||||
class SigningOutputStream extends FilterOutputStream {
|
||||
|
||||
private final Signature signature;
|
||||
private boolean signing = false;
|
||||
|
||||
public SigningOutputStream(OutputStream out, Signature signature) {
|
||||
super(out);
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
void setSigning(boolean signing) {
|
||||
this.signing = signing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
out.write(b, off, len);
|
||||
if(signing) {
|
||||
try {
|
||||
signature.update(b, off, len);
|
||||
} catch(SignatureException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
out.write(b);
|
||||
if(signing) {
|
||||
try {
|
||||
signature.update((byte) b);
|
||||
} catch(SignatureException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user