From e1915e6b4b39203bc6164ca9b4905c43881be0f5 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Sat, 20 Aug 2016 19:30:32 -0400 Subject: [PATCH 1/5] Add merkletree package - Part of #32 and #26 --- README.md | 1 + merkletree/pom.xml | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 merkletree/pom.xml diff --git a/README.md b/README.md index 5fea1ca..435a0af 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ The pckages in this library implement the various components of the CONIKS syste - `coniks_server`: Prototype key server - `coniks_test_client`: Prototype client CLI - `crypto`: Cryptographic algorithms and operations +- `merkletree`: Merkle prefix tree and Persistent Authenticated Dictionary - `util`: Utility functions The `protos` directory contains the Protocol Buffer message definitions diff --git a/merkletree/pom.xml b/merkletree/pom.xml new file mode 100644 index 0000000..ebebb5e --- /dev/null +++ b/merkletree/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + + + org.coniks + coniks-java + 1.3-SNAPSHOT + + + org.coniks.merkletree + coniks-merkletree + 1.3-SNAPSHOT + jar + + CONIKS [Merkle Tree] + http://coniks.org + + Merkle prefix tree and Persistent Authenticated Dictionary library for CONIKS. + + + From 3de0449f7478f6568dfb23abe9908be135b1c945 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Mon, 22 Aug 2016 12:09:44 -0400 Subject: [PATCH 2/5] Start refactoring tree node classes --- .../org/coniks/coniks_server/ServerUtils.java | 2 + .../org/coniks/merkletree/InteriorNode.java | 216 ++++++++++++++++++ .../java/org/coniks/merkletree/LeafNode.java | 72 ++++++ .../java/org/coniks/merkletree/TreeNode.java | 142 ++++++++++++ .../org/coniks/merkletree/UserLeafNode.java | 138 +++++++++++ 5 files changed, 570 insertions(+) create mode 100644 merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java create mode 100644 merkletree/src/main/java/org/coniks/merkletree/LeafNode.java create mode 100644 merkletree/src/main/java/org/coniks/merkletree/TreeNode.java create mode 100644 merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java diff --git a/coniks_server/src/main/java/org/coniks/coniks_server/ServerUtils.java b/coniks_server/src/main/java/org/coniks/coniks_server/ServerUtils.java index 864504b..5b31bfb 100644 --- a/coniks_server/src/main/java/org/coniks/coniks_server/ServerUtils.java +++ b/coniks_server/src/main/java/org/coniks/coniks_server/ServerUtils.java @@ -357,7 +357,9 @@ public static byte[] getUserLeafNodeBytes(UserLeafNode uln){ /** Converts a {@link InteriorNode} {@code in} to a hashable array of bytes. * *@return The {@code byte[]} containing the serialized InteriorNode. + *@deprecated Use more general {@link org.coniks.merkletree.InteriorNode#serialize()}. */ + @Deprecated public static byte[] getInteriorNodeBytes(InteriorNode in){ byte[] left = in.getLeftHash(); byte[] right = in.getRightHash(); diff --git a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java new file mode 100644 index 0000000..fa8c70d --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java @@ -0,0 +1,216 @@ +/* + Copyright (c) 2016, Princeton University. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Princeton University nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +package org.coniks.merkletree; + +import java.nio.ByteBuffer; + +// coniks-java imports +import org.coniks.crypto.Digest; + +/** Represents an interior node in the CONIKS binary Merkle + * prefix tree. + * + *@author Marcela S. Melara (melara@cs.princeton.edu) + *@author Aaron Blankstein + *@author Michael Rochlin + */ +public class InteriorNode extends TreeNode { + + protected TreeNode left; // the left child of the node + protected TreeNode right; // the right child of the node + protected byte[] leftHash; + protected byte[] rightHash; + protected boolean hasLeaf; + + /** Constructs an interior node with the given + * parent tree node {@code p} and its level {@code lvl} + * within the tree. + */ + public InteriorNode(TreeNode p, int lvl){ + + this.left = null; + this.right = null; + this.parent = p; + this.level = lvl; + this.leftHash = null; + this.rightHash = null; + this.hasLeaf = false; + this.name = ""; // for debugging + + } + + /** Protected constructor for an interior node specified + * with left and right subtrees {@code l} and {@code r} + * and their corresponding hashes {@code lh} and {@code rh}, + * the parent tree node {@code p}, level in tree {@code lvl}, and + * the flag {@code hasLeaf} indicating whether the interior node + * has at least one leaf node as a child. + */ + protected InteriorNode(TreeNode l, TreeNode r, TreeNode p, int lvl, byte[] lh, byte[] rh, + boolean hasLeaf){ + this.left = l; + this.right = r; + this.parent = p; + this.level = lvl; + this.leftHash = lh; + this.rightHash = rh; + this.hasLeaf = hasLeaf; + this.name = ""; // for debugging + } + + /** Gets this tree node's left subtree. + * + *@return The left subtree as a {@link TreeNode}. + */ + public TreeNode getLeft(){ + return left; + } + + /** Gets this tree node's right subtree. + * + *@return The right subtree as a {@link TreeNode}. + */ + public TreeNode getRight(){ + return right; + } + + /** Gets the hash of the left subtree. + * + *@return The hash of the left subtree as a {@code byte[]} (it + * may be {@code null}). + */ + public byte[] getLeftHash(){ + return this.leftHash; + } + + /** Gets the hash of the right subtree. + * + *@return The hash of the right subtree as a {@code byte[]} (it + * may be {@code null}). + */ + public byte[] getRightHash(){ + return this.rightHash; + } + + /** Checks whether the interior node has at least one + * leaf node child. + * + *@return {@code true} if the interior node has a leaf node child, + * {@code false} otherwise. + */ + public boolean hasLeaf() { + return this.hasLeaf; + } + + /** Sets the left and right subtrees of the interior node to + * tree nodes {@code l} and {@code r}, respectively. + */ + public void setChildren(TreeNode l, TreeNode r){ + this.left = l; + this.right = r; + } + + /** Sets the hashes of the left and right subtrees of + * the interior node to {@code byte[]}s {@code l} and {@code r}, + * respectively. + */ + public void setHashes(byte[] l, byte[] r){ + this.leftHash = l; + this.rightHash= r; + } + + /** Sets the parent of the interior node to tree node + * {@code n}. + */ + public void setParent(TreeNode n){ + this.parent = n; + } + + /** Sets the {@code hasLeaf} flag to the boolean value given + * by {@code l}. + */ + public void setHasLeaf(boolean l) { + this.hasLeaf = true; + } + + /** Clones (i.e. duplicates) this interior node with the + * given {@code parent} tree node. It then recursively + * calls this function on the original interior node's two subtrees. + *

+ * This function is called as part of the CONIKS Merkle tree + * rebuilding process at the beginning of every epoch. + *@return The cloned interior node. + */ + public TreeNode clone(TreeNode parent){ + InteriorNode cloneN = new InteriorNode(null, null, parent, this.level, + this.leftHash, this.rightHash, false); + if (this.left != null) + cloneN.left = this.left.clone(cloneN); + if (this.right != null) + cloneN.right = this.right.clone(cloneN); + + return cloneN; + + } + + /** Serializes the left and right hashes of this interior node + * into a {@code byte[]}. + * + *@return the serialized interior node + */ + protected byte[] serialize(){ + byte[] nodeBytes = new byte[this.leftHash.length+this.rightHash.length]; + + ByteBuffer arr = ByteBuffer.wrap(nodeBytes); + arr.put(this.leftHash); + arr.put(this.rightHash); + + return arr.array(); + } + + /** Hashes this interior and its children recursively. + * + *@return the hash of this interior node and its children + */ + public byte[] hash() { + if (this.leftHash == null) { + this.leftHash = this.left.hash(); + } + if (this.rightHash == null) { + this.rightHash = this.right.hash(); + } + return Digest.digest(this.serialize()); + } + +} // ends InteriorNode diff --git a/merkletree/src/main/java/org/coniks/merkletree/LeafNode.java b/merkletree/src/main/java/org/coniks/merkletree/LeafNode.java new file mode 100644 index 0000000..99efd4e --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/LeafNode.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2016, Princeton University. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Princeton University nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +package org.coniks.merkletree; + +/** Represents an generic leaf node in the CONIKS binary Merkle + * prefix tree. + * + *@author Marcela S. Melara (melara@cs.princeton.edu) + *@author Aaron Blankstein + */ +public class LeafNode extends TreeNode { + + /** Constructs a generic leaf node with the + * parent tree node {@code p}. + */ + public LeafNode(TreeNode p){ + this.parent = p; + } + + /** Constructs a generic leaf node with + * a {@code null} parent tree node + */ + public LeafNode(){ + this.parent = null; + } + + /** Gets this leaf node's parent tree node. + * + *@return The leaf node's parent node (may be {@code null}). + */ + public TreeNode getParent(){ + return this.parent; + } + + /** Sets this leaf node's parent to tree node {@code n}. + */ + public void setParent(TreeNode n){ + this.parent = n; + } + +} diff --git a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java new file mode 100644 index 0000000..5cbc0b7 --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java @@ -0,0 +1,142 @@ +/* + Copyright (c) 2016, Princeton University. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Princeton University nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +package org.coniks.merkletree; + +import java.io.Serializable; + +/** Represents an generic tree node in the CONIKS binary Merkle + * prefix tree. + * + *@author Marcela S. Melara (melara@cs.princeton.edu) + *@author Aaron Blankstein + */ +public class TreeNode implements Serializable { + + transient TreeNode parent; // the parent of the node + int level; // indicates the level in the tree + + /** Gets this tree node's parent. + * + *@return The parent as a {@link TreeNode}. Will be {@code null} + * if the tree node is a {@link RootNode}. + */ + public TreeNode getParent(){ + return parent; + } + + /** Gets this tree node's level in the Merkle tree. + * + *@return The level in the tree as an {@code int}. + */ + public int getLevel(){ + return level; + } + + /** Gets this tree node's name. + *

+ * This is used for debugging. + * + *@return The node's name as a {@code String}. + */ + public String getName() { + return name; + } + + /** Sets this tree node's left subtree to {@code n} + */ + public void setLeft(TreeNode n){ + this.left = n; + } + + /** Sets this tree node's right subtree to {@code n} + */ + public void setRight(TreeNode n){ + this.right = n; + } + + /** Sets this tree node's parent to {@code n} + */ + public void setParent(TreeNode n){ + this.parent = n; + } + + /** Sets this tree node's level to {@code l} + */ + public void setLevel(int l){ + this.level = l; + } + + /** Sets this tree node's name to {@code name} + */ + public void setName(String name) { + this.name = name; + } + + /** Cloning is not supported by generic {@link TreeNode}s, + * only by their sub classes as these specify the more + * specific data that needs to be duplicated when rebuilding + * the CONIKS Merkle tree. Therefore each sub class of + * TreeNode must specify its own cloning function. + * + *@throws An UnsupportedOperationException. + */ + public TreeNode clone(TreeNode parent){ + throw new UnsupportedOperationException(); + } + + /** Serialization is not supported by generic {@link TreeNode}s, + * only by their sub classes as these specify the more + * specific fields that need to be serialized when hashing + * the nodes. Therefore each sub class of + * TreeNode must specify its own serialization function. + * + *@throws An UnsupportedOperationException. + */ + public byte[] serialize(){ + throw new UnsupportedOperationException(); + } + + /** Hashing is not supported by generic {@link TreeNode}s, + * only by their sub classes as these specify the more + * specific fields that need to be included for hashing + * the nodes. Therefore each sub class of + * TreeNode must specify its own hashing function. + * + *@throws An UnsupportedOperationException. + */ + public byte[] hash() { + throw new UnsupportedOperationException(); + } + +} //ends TreeNode class diff --git a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java new file mode 100644 index 0000000..c0f35d5 --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java @@ -0,0 +1,138 @@ +/* + Copyright (c) 2016, Princeton University. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Princeton University nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +package org.coniks.merkletree; + +import java.io.Serializable; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPublicKey; + +// coniks-java imports +import org.coniks.crypto.Commitment; +import org.coniks.crypto.Digest; +import org.coniks.util.Convert; + +/** Represents a leaf node containing a user's entry in the CONIKS key directory + * in the CONIKS binary Merkle prefix tree. + * + *@author Marcela S. Melara (melara@cs.princeton.edu) + *@author Aaron Blankstein + *@author Michael Rochlin + */ +public class UserLeafNode extends TreeNoe { + + public static final String LEAF_IDENTIFIER = "L"; + + private String key; + private byte[] value; + private byte[] index; + private Commitment commit; + + public UserLeafNode(String k, byte[] v, byte[] idx, Commitment comm) { + this.parent = null; + this.level = 0; + this.key = k; + this.value = v; + this.index = idx; + this.commit = comm; + } + + /** Gets the {@link UserLeafNode}'s key. + * + *@return The key as a {@code String}. + */ + protected String getKey(){ + return this.key; + } + + /** Gets the {@link UserLeafNode}'s value. + * + *@return The value as a {@code byte[]} + */ + protected byte[] getValue(){ + return this.value; + } + + /** Gets the lookup index for the key in this {@link UserLeafNode}. + * + *@return The lookup index as a {@code byte[]}. + */ + public byte[] getIndex() { + return this.index; + } + + /** Gets the {@link UserLeafNode}'s commitment. + * + *@return The commitment as a {@link org.coniks.crypto.Commitment}. + */ + public Commitment getCommit() { + return this.commit; + } + + /** Clones (i.e. duplicates) this user leaf node with the + * given {@code parent} tree node. + *

+ * This function is called as part of the CONIKS Merkle tree + * rebuilding process at the beginning of every epoch. + *@return The cloned user leaf node. + */ + public UserLeafNode clone(TreeNode parent){ + + UserLeafNode cloneN = new UserLeafNode(this.key, this.value, + this.index, this.Commitment); + cloneN.parent = parent; + cloneN.level = this.level; + + return cloneN; + } + + protected byte[] serialize() { + + byte[] leafId = Convert.strToBytes(LEAF_IDENTIFIER); + byte[] lvlBytes = Convert.intToBytes(this.level); + byte[] commVal = this.commit.getValue(); + + byte[] leafBytes = new byte[this.leafId.length+nonce.length+this.index.length+ + lvlBytes.length+commVal.length]; + + ByteBuffer arr = ByteBuffer.wrap(leafBytes); + arr.put(leafId); + arr.put(nonce); + arr.put(this.index); + arr.put(lvlBytes); + arr.put(commVal); + + return arr.array(); + } + +} // ends UserLeafNode From 208d03291dbc6b0b4c0de8c3633b449e50f7fb47 Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Fri, 1 Sep 2017 12:56:57 -0400 Subject: [PATCH 3/5] Refactor UserLeafNode and create MerkleTree class --- .../org/coniks/merkletree/InteriorNode.java | 69 ++++-------------- .../org/coniks/merkletree/MerkleTree.java | 72 +++++++++++++++++++ .../java/org/coniks/merkletree/TreeNode.java | 28 ++++---- .../org/coniks/merkletree/UserLeafNode.java | 20 +++--- 4 files changed, 110 insertions(+), 79 deletions(-) create mode 100644 merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java diff --git a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java index fa8c70d..6ccc6f3 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java @@ -47,26 +47,22 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */ public class InteriorNode extends TreeNode { - protected TreeNode left; // the left child of the node - protected TreeNode right; // the right child of the node + protected TreeNode leftChild; // the left child of the node + protected TreeNode rightChild; // the right child of the node protected byte[] leftHash; protected byte[] rightHash; - protected boolean hasLeaf; /** Constructs an interior node with the given * parent tree node {@code p} and its level {@code lvl} * within the tree. */ public InteriorNode(TreeNode p, int lvl){ - - this.left = null; - this.right = null; - this.parent = p; - this.level = lvl; + super(p, lvl); + this.leftChild = null; + this.rightChild = null; this.leftHash = null; this.rightHash = null; - this.hasLeaf = false; - this.name = ""; // for debugging + this.setName(""); // for debugging } @@ -93,16 +89,16 @@ protected InteriorNode(TreeNode l, TreeNode r, TreeNode p, int lvl, byte[] lh, b * *@return The left subtree as a {@link TreeNode}. */ - public TreeNode getLeft(){ - return left; + public TreeNode getLeftChild(){ + return leftChild; } /** Gets this tree node's right subtree. * *@return The right subtree as a {@link TreeNode}. */ - public TreeNode getRight(){ - return right; + public TreeNode getRightChild(){ + return rightChild; } /** Gets the hash of the left subtree. @@ -123,47 +119,6 @@ public byte[] getRightHash(){ return this.rightHash; } - /** Checks whether the interior node has at least one - * leaf node child. - * - *@return {@code true} if the interior node has a leaf node child, - * {@code false} otherwise. - */ - public boolean hasLeaf() { - return this.hasLeaf; - } - - /** Sets the left and right subtrees of the interior node to - * tree nodes {@code l} and {@code r}, respectively. - */ - public void setChildren(TreeNode l, TreeNode r){ - this.left = l; - this.right = r; - } - - /** Sets the hashes of the left and right subtrees of - * the interior node to {@code byte[]}s {@code l} and {@code r}, - * respectively. - */ - public void setHashes(byte[] l, byte[] r){ - this.leftHash = l; - this.rightHash= r; - } - - /** Sets the parent of the interior node to tree node - * {@code n}. - */ - public void setParent(TreeNode n){ - this.parent = n; - } - - /** Sets the {@code hasLeaf} flag to the boolean value given - * by {@code l}. - */ - public void setHasLeaf(boolean l) { - this.hasLeaf = true; - } - /** Clones (i.e. duplicates) this interior node with the * given {@code parent} tree node. It then recursively * calls this function on the original interior node's two subtrees. @@ -205,10 +160,10 @@ protected byte[] serialize(){ */ public byte[] hash() { if (this.leftHash == null) { - this.leftHash = this.left.hash(); + this.leftHash = this.leftChild.hash(); } if (this.rightHash == null) { - this.rightHash = this.right.hash(); + this.rightHash = this.rightChild.hash(); } return Digest.digest(this.serialize()); } diff --git a/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java b/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java new file mode 100644 index 0000000..62743e8 --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java @@ -0,0 +1,72 @@ +/* + Copyright (c) 2017, Princeton University. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Princeton University nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ +package org.coniks.merkletree; + +import java.nio.ByteBuffer; + +import org.coniks.crypto.Digest; + +public class MerkleTree { + + byte[] nonce; + InteriorNode root; + byte[] hash; + + + public MerkleTree() { + // TODO: catch the NoSuchAlgorithmExeption + this.nonce = Digest.makeRand(); + this.root = new InteriorNode(null, 0); + this.hash = null; + } + + private MerkleTree(byte[] nonce) { + this.nonce = nonce; + this.root = null; + this.hash = null; + } + + public MerkleTree clone() { + MerkleTree cloneM = new MerkleTree(this.nonce); + cloneM.root = (InteriorNode)this.root.clone(null); + + // copy the hash into the new node + byte[] hashBytes = new byte[this.hash.length]; + ByteBuffer arr = ByteBuffer.wrap(hashBytes); + arr.put(this.hash); + cloneM.hash = arr.array(); + + return cloneM; + } + +} diff --git a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java index 5cbc0b7..654569e 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java @@ -45,6 +45,18 @@ public class TreeNode implements Serializable { transient TreeNode parent; // the parent of the node int level; // indicates the level in the tree + String name; // for debugging + + /** Construct a generic TreeNode with the parent {@code p} + * and at level {@code level}. + *@param p the node's parent node + *@param level the node's level in the Merkle tree + */ + public TreeNode(TreeNode parent, int level) { + this.parent = parent; + this.level = level; + this.name = "node"; + } /** Gets this tree node's parent. * @@ -73,18 +85,6 @@ public String getName() { return name; } - /** Sets this tree node's left subtree to {@code n} - */ - public void setLeft(TreeNode n){ - this.left = n; - } - - /** Sets this tree node's right subtree to {@code n} - */ - public void setRight(TreeNode n){ - this.right = n; - } - /** Sets this tree node's parent to {@code n} */ public void setParent(TreeNode n){ @@ -123,7 +123,7 @@ public TreeNode clone(TreeNode parent){ * *@throws An UnsupportedOperationException. */ - public byte[] serialize(){ + public byte[] serialize(MerkleTree tree){ throw new UnsupportedOperationException(); } @@ -135,7 +135,7 @@ public byte[] serialize(){ * *@throws An UnsupportedOperationException. */ - public byte[] hash() { + public byte[] hash(MerkleTree tree) { throw new UnsupportedOperationException(); } diff --git a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java index c0f35d5..81048ca 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java @@ -34,8 +34,6 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, package org.coniks.merkletree; import java.io.Serializable; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.RSAPublicKey; // coniks-java imports import org.coniks.crypto.Commitment; @@ -109,25 +107,27 @@ public Commitment getCommit() { public UserLeafNode clone(TreeNode parent){ UserLeafNode cloneN = new UserLeafNode(this.key, this.value, - this.index, this.Commitment); - cloneN.parent = parent; - cloneN.level = this.level; + this.index, this.commit); + cloneN.setParent(parent); + cloneN.setLevel(this.level); return cloneN; } - protected byte[] serialize() { + public byte[] serialize(MerkleTree tree) { byte[] leafId = Convert.strToBytes(LEAF_IDENTIFIER); byte[] lvlBytes = Convert.intToBytes(this.level); byte[] commVal = this.commit.getValue(); - byte[] leafBytes = new byte[this.leafId.length+nonce.length+this.index.length+ + byte[] leafBytes = new byte[this.leafId.length+ + tree.getNonce().length+ + this.index.length+ lvlBytes.length+commVal.length]; ByteBuffer arr = ByteBuffer.wrap(leafBytes); arr.put(leafId); - arr.put(nonce); + arr.put(tree.getNonce()); arr.put(this.index); arr.put(lvlBytes); arr.put(commVal); @@ -135,4 +135,8 @@ protected byte[] serialize() { return arr.array(); } + public byte[] hash(MerkleTree tree) { + return Digest.digest(this.serialize(tree)); + } + } // ends UserLeafNode From d1ff57a1edc245b59f2157922948d2ad2cd67169 Mon Sep 17 00:00:00 2001 From: Marcela M Date: Tue, 19 Sep 2017 15:06:40 -0400 Subject: [PATCH 4/5] Create MerkleNode interface --- .../org/coniks/merkletree/InteriorNode.java | 6 +-- .../org/coniks/merkletree/MerkleNode.java | 11 ++++++ .../java/org/coniks/merkletree/TreeNode.java | 37 +------------------ .../org/coniks/merkletree/UserLeafNode.java | 7 ++-- 4 files changed, 19 insertions(+), 42 deletions(-) create mode 100644 merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java diff --git a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java index 6ccc6f3..28eb68f 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java @@ -45,7 +45,8 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *@author Aaron Blankstein *@author Michael Rochlin */ -public class InteriorNode extends TreeNode { +public class InteriorNode extends TreeNode + implements MerkleNode { protected TreeNode leftChild; // the left child of the node protected TreeNode rightChild; // the right child of the node @@ -136,7 +137,6 @@ public TreeNode clone(TreeNode parent){ cloneN.right = this.right.clone(cloneN); return cloneN; - } /** Serializes the left and right hashes of this interior node @@ -144,7 +144,7 @@ public TreeNode clone(TreeNode parent){ * *@return the serialized interior node */ - protected byte[] serialize(){ + public byte[] serialize(){ byte[] nodeBytes = new byte[this.leftHash.length+this.rightHash.length]; ByteBuffer arr = ByteBuffer.wrap(nodeBytes); diff --git a/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java b/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java new file mode 100644 index 0000000..170e30e --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java @@ -0,0 +1,11 @@ +package org.coniks.merkletree; + +/** Defines the behavior of a TreeNode used in a MerkleTree. + */ +public interface MerkleNode { + + public TreeNode clone(TreeNode parent); + public byte[] serialize(MerkleTree tree); + public byte[] hash(MerkleTree tree); + +} diff --git a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java index 654569e..d3fe65f 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java @@ -45,6 +45,7 @@ public class TreeNode implements Serializable { transient TreeNode parent; // the parent of the node int level; // indicates the level in the tree + String name; // for debugging /** Construct a generic TreeNode with the parent {@code p} @@ -103,40 +104,4 @@ public void setName(String name) { this.name = name; } - /** Cloning is not supported by generic {@link TreeNode}s, - * only by their sub classes as these specify the more - * specific data that needs to be duplicated when rebuilding - * the CONIKS Merkle tree. Therefore each sub class of - * TreeNode must specify its own cloning function. - * - *@throws An UnsupportedOperationException. - */ - public TreeNode clone(TreeNode parent){ - throw new UnsupportedOperationException(); - } - - /** Serialization is not supported by generic {@link TreeNode}s, - * only by their sub classes as these specify the more - * specific fields that need to be serialized when hashing - * the nodes. Therefore each sub class of - * TreeNode must specify its own serialization function. - * - *@throws An UnsupportedOperationException. - */ - public byte[] serialize(MerkleTree tree){ - throw new UnsupportedOperationException(); - } - - /** Hashing is not supported by generic {@link TreeNode}s, - * only by their sub classes as these specify the more - * specific fields that need to be included for hashing - * the nodes. Therefore each sub class of - * TreeNode must specify its own hashing function. - * - *@throws An UnsupportedOperationException. - */ - public byte[] hash(MerkleTree tree) { - throw new UnsupportedOperationException(); - } - } //ends TreeNode class diff --git a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java index 81048ca..45c0819 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java @@ -40,14 +40,15 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, import org.coniks.crypto.Digest; import org.coniks.util.Convert; -/** Represents a leaf node containing a user's entry in the CONIKS key directory - * in the CONIKS binary Merkle prefix tree. +/** Represents a leaf node containing a user's entry in the CONIKS key + * directory in the CONIKS binary Merkle prefix tree. * *@author Marcela S. Melara (melara@cs.princeton.edu) *@author Aaron Blankstein *@author Michael Rochlin */ -public class UserLeafNode extends TreeNoe { +public class UserLeafNode extends TreeNode + implements MerkleNode { public static final String LEAF_IDENTIFIER = "L"; From 33f2df2be13b76c1bb13b7744d85c7eb6d61089f Mon Sep 17 00:00:00 2001 From: Marcela Melara Date: Sun, 25 Feb 2018 00:37:06 -0500 Subject: [PATCH 5/5] Finish implementing MerkleNode interface in tree nodes, implement set API following Go implementation --- .../java/org/coniks/crypto/Commitment.java | 14 ++ .../java/org/coniks/merkletree/EmptyNode.java | 58 ++++++++ .../org/coniks/merkletree/InteriorNode.java | 105 +++++++++----- .../org/coniks/merkletree/MerkleNode.java | 4 +- .../org/coniks/merkletree/MerkleTree.java | 133 ++++++++++++++++++ .../java/org/coniks/merkletree/TreeNode.java | 12 +- .../org/coniks/merkletree/UserLeafNode.java | 26 ++-- .../main/java/org/coniks/util/Convert.java | 14 ++ 8 files changed, 312 insertions(+), 54 deletions(-) create mode 100644 merkletree/src/main/java/org/coniks/merkletree/EmptyNode.java diff --git a/crypto/src/main/java/org/coniks/crypto/Commitment.java b/crypto/src/main/java/org/coniks/crypto/Commitment.java index cead0ee..b6ff9d6 100644 --- a/crypto/src/main/java/org/coniks/crypto/Commitment.java +++ b/crypto/src/main/java/org/coniks/crypto/Commitment.java @@ -64,6 +64,11 @@ public Commitment(byte[] data) this.value = Digest.digest(d); } + private Commitment(byte[] s, byte[] v) { + this.salt = s; + this.value = v; + } + /** Gets this commitment's random salt. * *@return the commitment salt @@ -104,4 +109,13 @@ public boolean verify(byte[] opening) } + /** Duplicates the commitment. + * + *@return A fresh copy of the commitment. + */ + public Commitment clone() { + Commitment cloneC = new Commitment(this.salt, this.value); + return cloneC; + } + } diff --git a/merkletree/src/main/java/org/coniks/merkletree/EmptyNode.java b/merkletree/src/main/java/org/coniks/merkletree/EmptyNode.java new file mode 100644 index 0000000..1df2a91 --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/EmptyNode.java @@ -0,0 +1,58 @@ +package org.coniks.merkletree; + +import java.nio.ByteBuffer; + +// coniks-java imports +import org.coniks.crypto.Digest; + +/** Represents an interior node in the CONIKS binary Merkle + * prefix tree. + * + *@author Marcela S. Melara (melara@cs.princeton.edu) + */ + +public class EmptyNode extends TreeNode implements MerkleNode { + + public static final String EMPTY_IDENTIFIER = "E"; + + private byte[] index; + + public EmptyNode(MerkleNode p, int lvl) { + super(p, lvl); + index = null; + } + + public byte[] getIndex() { + return index; + } + + public MerkleNode clone(InteriorNode parent) { + EmptyNode cloneN = new EmptyNode(parent, this.level); + + // FIXME: this needs to do a fully copy of the index array + cloneN.index = this.index; + } + + public byte[] hash(MerkleTree tree) { + byte[] emptyId = Convert.strToBytes(EMPTY_IDENTIFIER); + byte[] lvlBytes = Convert.intToBytes(this.level); + + byte[] emptyBytes = new byte[this.emptyId.length+ + tree.getNonce().length+ + this.index.length+ + lvlBytes.length]; + + ByteBuffer arr = ByteBuffer.wrap(emptyBytes); + arr.put(emptyId); + arr.put(tree.getNonce()); + arr.put(this.index); + arr.put(lvlBytes); + + return Digest.digest(arr.array()); + } + + public boolean isEmpty() { + return true; + } + +} diff --git a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java index 28eb68f..87edd96 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java @@ -48,23 +48,22 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, public class InteriorNode extends TreeNode implements MerkleNode { - protected TreeNode leftChild; // the left child of the node - protected TreeNode rightChild; // the right child of the node - protected byte[] leftHash; - protected byte[] rightHash; + private MerkleNode left; // the left child of the node + private MerkleNode right; // the right child of the node + private byte[] leftHash; + private byte[] rightHash; /** Constructs an interior node with the given * parent tree node {@code p} and its level {@code lvl} * within the tree. */ - public InteriorNode(TreeNode p, int lvl){ + public InteriorNode(MerkleNode p, int lvl){ super(p, lvl); - this.leftChild = null; - this.rightChild = null; + this.left = new EmptyNode(p, lvl); + this.right = new EmptyNode(p, lvl); this.leftHash = null; this.rightHash = null; this.setName(""); // for debugging - } /** Protected constructor for an interior node specified @@ -74,32 +73,46 @@ public InteriorNode(TreeNode p, int lvl){ * the flag {@code hasLeaf} indicating whether the interior node * has at least one leaf node as a child. */ - protected InteriorNode(TreeNode l, TreeNode r, TreeNode p, int lvl, byte[] lh, byte[] rh, - boolean hasLeaf){ + private InteriorNode(MerkleNode p, int lvl, MerkleNode l, MerkleNode r, + byte[] lh, byte[] rh) { + super(p, lvl); this.left = l; this.right = r; - this.parent = p; - this.level = lvl; this.leftHash = lh; this.rightHash = rh; - this.hasLeaf = hasLeaf; this.name = ""; // for debugging } /** Gets this tree node's left subtree. * - *@return The left subtree as a {@link TreeNode}. + *@return The left subtree as a {@link MerkleNode}. */ - public TreeNode getLeftChild(){ - return leftChild; + public MerkleNode getLeft(){ + return left; } /** Gets this tree node's right subtree. * - *@return The right subtree as a {@link TreeNode}. + *@return The right subtree as a {@link MerkleNode}. + */ + public MerkleNode getRight(){ + return right; + } + + /** Sets this tree node's left subtree. + * + *@param n The MerkleNode to set as the left subtree. + */ + public void setLeft(MerkleNode n){ + this.left = n; + } + + /** Sets this tree node's right subtree. + * + *@param n The MerkleNode to set as the right subtree. */ - public TreeNode getRightChild(){ - return rightChild; + public void setRight(MerkleNode n){ + this.right = n; } /** Gets the hash of the left subtree. @@ -120,6 +133,18 @@ public byte[] getRightHash(){ return this.rightHash; } + /** Sets the hash of the left subtree to {@code null}. + */ + public void resetLeftHash(){ + this.leftHash = null; + } + + /** Sets the hash of the right subtree to {@code null}. + */ + public void getRightHash(){ + this.rightHash = null; + } + /** Clones (i.e. duplicates) this interior node with the * given {@code parent} tree node. It then recursively * calls this function on the original interior node's two subtrees. @@ -128,14 +153,18 @@ public byte[] getRightHash(){ * rebuilding process at the beginning of every epoch. *@return The cloned interior node. */ - public TreeNode clone(TreeNode parent){ - InteriorNode cloneN = new InteriorNode(null, null, parent, this.level, - this.leftHash, this.rightHash, false); - if (this.left != null) - cloneN.left = this.left.clone(cloneN); - if (this.right != null) - cloneN.right = this.right.clone(cloneN); - + public MerkleNode clone(InteriorNode parent){ + // FIXME: this needs to do a full copy of the hashes + InteriorNode cloneN = new InteriorNode(parent, this.level, + null, null, + this.leftHash, + this.rightHash, false); + if (this.left == null || this.right == null) { + // FIXME + throw new UnsupportedOperationException("child is null!"); + } + cloneN.left = this.left.clone(cloneN); + cloneN.right = this.right.clone(cloneN); return cloneN; } @@ -145,7 +174,8 @@ public TreeNode clone(TreeNode parent){ *@return the serialized interior node */ public byte[] serialize(){ - byte[] nodeBytes = new byte[this.leftHash.length+this.rightHash.length]; + byte[] nodeBytes = new byte[this.leftHash.length+ + this.rightHash.length]; ByteBuffer arr = ByteBuffer.wrap(nodeBytes); arr.put(this.leftHash); @@ -158,14 +188,25 @@ public byte[] serialize(){ * *@return the hash of this interior node and its children */ - public byte[] hash() { + public byte[] hash(MerkleTree tree) { if (this.leftHash == null) { - this.leftHash = this.leftChild.hash(); + this.leftHash = this.left.hash(tree); } if (this.rightHash == null) { - this.rightHash = this.rightChild.hash(); + this.rightHash = this.right.hash(tree); } - return Digest.digest(this.serialize()); + + byte[] nodeBytes = new byte[this.leftHash.length+ + this.rightHash.length]; + ByteBuffer arr = ByteBuffer.wrap(nodeBytes); + arr.put(this.leftHash); + arr.put(this.rightHash); + + return Digest.digest(arr.array()); + } + + public boolean isEmpty() { + return false; } } // ends InteriorNode diff --git a/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java b/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java index 170e30e..4a2c749 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java @@ -4,8 +4,8 @@ */ public interface MerkleNode { - public TreeNode clone(TreeNode parent); - public byte[] serialize(MerkleTree tree); + public MerkleNode clone(InteriorNode parent); public byte[] hash(MerkleTree tree); + public boolean isEmpty(); } diff --git a/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java b/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java index 62743e8..a4d0c5a 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java +++ b/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java @@ -35,6 +35,7 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, import java.nio.ByteBuffer; import org.coniks.crypto.Digest; +import org.coniks.util.Convert; public class MerkleTree { @@ -56,7 +57,139 @@ private MerkleTree(byte[] nonce) { this.hash = null; } + /** Inserts or updates the value of the given index calculated from the + * key to the tree. It generates a new commitment for the leaf node. + * In the case of an update, the leaf node's value and commitment + * are replaced with the new value and newly generated commitment. + * + *@param idx The index in the tree to set. + *@param k The key used to compute the index. + *@param v The value assocaited with the key. + *@return -1 on an error, 0 otherwise. + */ + public int set(byte[] idx, String k, byte[] v) { + Commitment comm; + try { + comm = new Commitment(v); + } + catch(NoSuchAlgorithmException e) { + // FIXME: return actual error code + return -1; + } + + UserLeafNode toAdd = new UserLeafNode(null, 0, k, v, idx, comm); + this.insertNode(idx, toAdd); + return 0; + } + + // inserts a new user leaf node into the tree + private void insertNode(byte[] idx, UserLeafNode toAdd){ + boolean [] indexBits = Convert.bytesToBits(idx); + int depth = 0; + TreeNode curNode = this.root; + + curNode.setName("root"); + int counter = 1; + + insertLoop: + while(true){ + if (curNode instanceof UserLeafNode) { + // reached a "bottom" of the tree. + // add a new interior node and push the previous leaf down + // then continue insertion + UserLeafNode curNodeUL = (UserLeafNode) curNode; + if (curNodeUL.parent == null){ + throw new UnsupportedOperationException("parent is null!!"); + } + + // FIXME: Byte compare here + if (curNodeUL.getIndex().equals(toAdd.getIndex())) { + // replace the value + toAdd.setParent(currentNodeUL.getParent()); + toAdd.setLevel(currentNodeUL.getLevel()); + currentNodeUL = toAdd; + return; + } + + InteriorNode newInt = new InteriorNode(curNode.getParent(), + curNode.getLevel()); + + // direction here is going to be false = left, + // true = right + boolean direction = Convert.getNthBit(currentNodeUL.getIndex(), depth); + + if (direction) { + newInt.setRight(curNodeUL); + } + else { + newInt.setLeft(curNodeUL); + } + curNodeUL.setLevel(depth + 1); + curNode.setParent(newInt); + + if (newInt.getParent().getLeft() == curNode) { + newInt.parent.setLeft(newInt); + } + else { + newInt.parent.setRight(newInt); + } + curNode = newInt; + } + else (curNode instanceof InteriorNode) { + InteriorNode curNodeI = (InteriorNode) curNode; + // direction here is going to be false = left, + // true = right + boolean direction = indexBits[depth]; + + if (direction) { + // mark right tree as needing hash recompute + curNodeI.resetRightHash(); + if (curNodeI.getRight().isEmpty()){ + curNodeI.setRight(toAdd); + toAdd.setLevel(depth+1); + toAdd.setParent(curNodeI); + break insertLoop; + } + else { + curNode = curNodeI.getRight(); + } + } + else { + // mark left tree as needing hash recompute + curNodeI.resetLeftHash(); + if (curNodeI.left.isEmpty()){ + curNodeI.setLeft(toAdd); + toAdd.setLevel(depth+1); + toAdd.setParent(curNodeI); + break insertLoop; + } + else { + curNode = curNodeI.getLeft(); + } + } + depth += 1; + } + else { + throw new UnsupportedOperationException("Invalid tree"); + } + curNode.setName("n"+counter); + counter++; + } + } + + // Compute the hashes of the left and right subtrees + // of the Merkle tree root + // Wrapper for innerComputeHash + private void recomputeHash() { + this.hash = this.root.hash(this); + } + + /** Duplicates the tree returning a fresh copy of the tree. + * + *@return a new copy of the cloned tree. + */ public MerkleTree clone() { + // FIXME: this needs to create a full copy the nonce MerkleTree cloneM = new MerkleTree(this.nonce); cloneM.root = (InteriorNode)this.root.clone(null); diff --git a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java index d3fe65f..b27488f 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java @@ -43,8 +43,8 @@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */ public class TreeNode implements Serializable { - transient TreeNode parent; // the parent of the node - int level; // indicates the level in the tree + transient MerkleNode parent; // the parent of the node + private int level; // indicates the level in the tree String name; // for debugging @@ -53,7 +53,7 @@ public class TreeNode implements Serializable { *@param p the node's parent node *@param level the node's level in the Merkle tree */ - public TreeNode(TreeNode parent, int level) { + public TreeNode(MerkleNode parent, int level) { this.parent = parent; this.level = level; this.name = "node"; @@ -61,10 +61,10 @@ public TreeNode(TreeNode parent, int level) { /** Gets this tree node's parent. * - *@return The parent as a {@link TreeNode}. Will be {@code null} + *@return The parent as a {@link MerkleNode}. Will be {@code null} * if the tree node is a {@link RootNode}. */ - public TreeNode getParent(){ + public MerkleNode getParent(){ return parent; } @@ -88,7 +88,7 @@ public String getName() { /** Sets this tree node's parent to {@code n} */ - public void setParent(TreeNode n){ + public void setParent(MerkleNode n){ this.parent = n; } diff --git a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java index 45c0819..3aa8584 100644 --- a/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java +++ b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java @@ -57,9 +57,9 @@ public class UserLeafNode extends TreeNode private byte[] index; private Commitment commit; - public UserLeafNode(String k, byte[] v, byte[] idx, Commitment comm) { - this.parent = null; - this.level = 0; + public UserLeafNode(MerkleNode p, int lvl, String k, byte[] v, + byte[] idx, Commitment comm) { + super(p, lvl); this.key = k; this.value = v; this.index = idx; @@ -105,18 +105,16 @@ public Commitment getCommit() { * rebuilding process at the beginning of every epoch. *@return The cloned user leaf node. */ - public UserLeafNode clone(TreeNode parent){ - - UserLeafNode cloneN = new UserLeafNode(this.key, this.value, - this.index, this.commit); - cloneN.setParent(parent); - cloneN.setLevel(this.level); + public MerkleNode clone(InteriorNode parent){ + // FIXME: this needs to do a full copy of the index and value + UserLeafNode cloneN = new UserLeafNode(parent, this.level, this.key, + this.value, this.index, + this.commit.clone()); return cloneN; } - public byte[] serialize(MerkleTree tree) { - + public byte[] hash(MerkleTree tree) { byte[] leafId = Convert.strToBytes(LEAF_IDENTIFIER); byte[] lvlBytes = Convert.intToBytes(this.level); byte[] commVal = this.commit.getValue(); @@ -133,11 +131,11 @@ public byte[] serialize(MerkleTree tree) { arr.put(lvlBytes); arr.put(commVal); - return arr.array(); + return Digest.digest(arr.array()); } - public byte[] hash(MerkleTree tree) { - return Digest.digest(this.serialize(tree)); + public boolean isEmpty() { + return false; } } // ends UserLeafNode diff --git a/util/src/main/java/org/coniks/util/Convert.java b/util/src/main/java/org/coniks/util/Convert.java index 8597acc..f450084 100644 --- a/util/src/main/java/org/coniks/util/Convert.java +++ b/util/src/main/java/org/coniks/util/Convert.java @@ -95,6 +95,20 @@ public static boolean getNthBit(byte[] arr, int offset){ return (maskedBit != 0); } + /** Converts a byte array into an array of bits. + * + *@param buf the byte buffer to convert. + *@return a boolean array representing each bit of the + * given byte array. + */ + public static boolean[] bytesToBits(byte[] buf) { + boolean[] bits = new boolean[buf.length*8]; + for (int i = 0; i < bits.length; i++) { + bits[i] = (buf[i/8] & (1 << (7 - (i%8)))) != 0; + } + return bits; + } + /** Gets the 16-bit prefix of a byte array {@code arr}. * *@return the first 16 bits of {@code arr} or all zeros if the length