mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Add initial stream hashing implementation.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
package org.briarproject.bramble.api.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeHash;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface MessageFactory {
|
public interface MessageFactory {
|
||||||
@@ -10,4 +11,6 @@ public interface MessageFactory {
|
|||||||
Message createMessage(byte[] raw);
|
Message createMessage(byte[] raw);
|
||||||
|
|
||||||
byte[] getRawMessage(Message m);
|
byte[] getRawMessage(Message m);
|
||||||
|
|
||||||
|
MessageId getMessageId(GroupId g, long timestamp, TreeHash rootHash);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.bramble.api.sync.tree;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSink;
|
||||||
|
import org.briarproject.bramble.api.io.HashingId;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface StreamHasher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the given input stream, divides the data into blocks, stores
|
||||||
|
* the blocks and the resulting hash tree using the given block sink and
|
||||||
|
* temporary ID, and returns the message ID.
|
||||||
|
*/
|
||||||
|
MessageId hash(InputStream in, BlockSink sink, HashingId h, GroupId g,
|
||||||
|
long timestamp) throws IOException, DbException;
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.briarproject.bramble.api.sync.tree;
|
package org.briarproject.bramble.api.sync.tree;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
public interface TreeHasher {
|
public interface TreeHasher {
|
||||||
|
|
||||||
LeafNode hashBlock(int blockNumber, byte[] data);
|
LeafNode hashBlock(int blockNumber, byte[] data);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeHash;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
@@ -39,13 +40,19 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
if (body.length == 0) throw new IllegalArgumentException();
|
if (body.length == 0) throw new IllegalArgumentException();
|
||||||
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
MessageId id = getMessageId(g, timestamp, body);
|
MessageId id = getMessageIdFromBody(g, timestamp, body);
|
||||||
return new Message(id, g, timestamp, body);
|
return new Message(id, g, timestamp, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageId getMessageId(GroupId g, long timestamp, byte[] body) {
|
private MessageId getMessageIdFromBody(GroupId g, long timestamp,
|
||||||
|
byte[] body) {
|
||||||
// There's only one block, so the root hash is the hash of the block
|
// There's only one block, so the root hash is the hash of the block
|
||||||
byte[] rootHash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, body);
|
byte[] rootHash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, body);
|
||||||
|
return getMessageIdFromRootHash(g, timestamp, rootHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageId getMessageIdFromRootHash(GroupId g, long timestamp,
|
||||||
|
byte[] rootHash) {
|
||||||
byte[] timeBytes = new byte[INT_64_BYTES];
|
byte[] timeBytes = new byte[INT_64_BYTES];
|
||||||
ByteUtils.writeUint64(timestamp, timeBytes, 0);
|
ByteUtils.writeUint64(timestamp, timeBytes, 0);
|
||||||
byte[] idHash = crypto.hash(ID_LABEL, FORMAT_VERSION_BYTES,
|
byte[] idHash = crypto.hash(ID_LABEL, FORMAT_VERSION_BYTES,
|
||||||
@@ -65,7 +72,7 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH);
|
long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH);
|
||||||
byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
|
byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
|
||||||
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
||||||
MessageId id = getMessageId(g, timestamp, body);
|
MessageId id = getMessageIdFromBody(g, timestamp, body);
|
||||||
return new Message(id, g, timestamp, body);
|
return new Message(id, g, timestamp, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,4 +85,10 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
|
System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageId getMessageId(GroupId g, long timestamp,
|
||||||
|
TreeHash rootHash) {
|
||||||
|
return getMessageIdFromRootHash(g, timestamp, rootHash.getBytes());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.bramble.sync.tree;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.LeafNode;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeNode;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
interface HashTree {
|
||||||
|
|
||||||
|
void addLeaf(LeafNode leaf);
|
||||||
|
|
||||||
|
TreeNode getRoot();
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.briarproject.bramble.sync.tree;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.LeafNode;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeHasher;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeNode;
|
||||||
|
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class HashTreeImpl implements HashTree {
|
||||||
|
|
||||||
|
private final TreeHasher treeHasher;
|
||||||
|
private final Deque<TreeNode> nodes = new LinkedList<>();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
HashTreeImpl(TreeHasher treeHasher) {
|
||||||
|
this.treeHasher = treeHasher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLeaf(LeafNode leaf) {
|
||||||
|
TreeNode add = leaf;
|
||||||
|
int height = leaf.getHeight();
|
||||||
|
TreeNode last = nodes.peekLast();
|
||||||
|
while (last != null && last.getHeight() == height) {
|
||||||
|
add = treeHasher.mergeTrees(last, add);
|
||||||
|
height = add.getHeight();
|
||||||
|
nodes.removeLast();
|
||||||
|
last = nodes.peekLast();
|
||||||
|
}
|
||||||
|
nodes.addLast(add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TreeNode getRoot() {
|
||||||
|
TreeNode root = nodes.removeLast();
|
||||||
|
while (!nodes.isEmpty()) {
|
||||||
|
root = treeHasher.mergeTrees(nodes.removeLast(), root);
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package org.briarproject.bramble.sync.tree;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.io.BlockSink;
|
||||||
|
import org.briarproject.bramble.api.io.HashingId;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.LeafNode;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.StreamHasher;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeHash;
|
||||||
|
import org.briarproject.bramble.api.sync.tree.TreeHasher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import static java.util.Arrays.copyOfRange;
|
||||||
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class StreamHasherImpl implements StreamHasher {
|
||||||
|
|
||||||
|
private final TreeHasher treeHasher;
|
||||||
|
private final MessageFactory messageFactory;
|
||||||
|
private final Provider<HashTree> hashTreeProvider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
StreamHasherImpl(TreeHasher treeHasher, MessageFactory messageFactory,
|
||||||
|
Provider<HashTree> hashTreeProvider) {
|
||||||
|
this.treeHasher = treeHasher;
|
||||||
|
this.messageFactory = messageFactory;
|
||||||
|
this.hashTreeProvider = hashTreeProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageId hash(InputStream in, BlockSink sink, HashingId h,
|
||||||
|
GroupId g, long timestamp) throws IOException, DbException {
|
||||||
|
HashTree hashTree = hashTreeProvider.get();
|
||||||
|
byte[] block = new byte[MAX_BLOCK_LENGTH];
|
||||||
|
int read;
|
||||||
|
for (int blockNumber = 0; (read = read(in, block)) > 0; blockNumber++) {
|
||||||
|
byte[] data;
|
||||||
|
if (read == block.length) data = block;
|
||||||
|
else data = copyOfRange(block, 0, read);
|
||||||
|
sink.putBlock(h, blockNumber, data);
|
||||||
|
LeafNode leaf = treeHasher.hashBlock(blockNumber, data);
|
||||||
|
hashTree.addLeaf(leaf);
|
||||||
|
}
|
||||||
|
// TODO: Set paths on block sink
|
||||||
|
TreeHash rootHash = hashTree.getRoot().getHash();
|
||||||
|
MessageId m = messageFactory.getMessageId(g, timestamp, rootHash);
|
||||||
|
sink.setMessageId(h, m);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a block from the given input stream and returns the number of
|
||||||
|
* bytes read, or 0 if no bytes were read before reaching the end of the
|
||||||
|
* stream.
|
||||||
|
*/
|
||||||
|
private int read(InputStream in, byte[] block) throws IOException {
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < block.length) {
|
||||||
|
int read = in.read(block, offset, block.length - offset);
|
||||||
|
if (read == -1) return offset;
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,22 @@
|
|||||||
package org.briarproject.bramble.sync.tree;
|
package org.briarproject.bramble.sync.tree;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.tree.LeafNode;
|
import org.briarproject.bramble.api.sync.tree.LeafNode;
|
||||||
import org.briarproject.bramble.api.sync.tree.ParentNode;
|
import org.briarproject.bramble.api.sync.tree.ParentNode;
|
||||||
import org.briarproject.bramble.api.sync.tree.TreeHash;
|
import org.briarproject.bramble.api.sync.tree.TreeHash;
|
||||||
import org.briarproject.bramble.api.sync.tree.TreeHasher;
|
import org.briarproject.bramble.api.sync.tree.TreeHasher;
|
||||||
import org.briarproject.bramble.api.sync.tree.TreeNode;
|
import org.briarproject.bramble.api.sync.tree.TreeNode;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.sync.Message.FORMAT_VERSION;
|
import static org.briarproject.bramble.api.sync.Message.FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.sync.MessageId.BLOCK_LABEL;
|
import static org.briarproject.bramble.api.sync.MessageId.BLOCK_LABEL;
|
||||||
import static org.briarproject.bramble.api.sync.MessageId.TREE_LABEL;
|
import static org.briarproject.bramble.api.sync.MessageId.TREE_LABEL;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
class TreeHasherImpl implements TreeHasher {
|
class TreeHasherImpl implements TreeHasher {
|
||||||
|
|
||||||
private static final byte[] FORMAT_VERSION_BYTES =
|
private static final byte[] FORMAT_VERSION_BYTES =
|
||||||
|
|||||||
Reference in New Issue
Block a user