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/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/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/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. + + + 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 new file mode 100644 index 0000000..87edd96 --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/InteriorNode.java @@ -0,0 +1,212 @@ +/* + 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 + implements MerkleNode { + + 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(MerkleNode p, int lvl){ + super(p, lvl); + 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 + * 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. + */ + private InteriorNode(MerkleNode p, int lvl, MerkleNode l, MerkleNode r, + byte[] lh, byte[] rh) { + super(p, lvl); + this.left = l; + this.right = r; + this.leftHash = lh; + this.rightHash = rh; + this.name = ""; // for debugging + } + + /** Gets this tree node's left subtree. + * + *@return The left subtree as a {@link MerkleNode}. + */ + public MerkleNode getLeft(){ + return left; + } + + /** Gets this tree node's right subtree. + * + *@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 void setRight(MerkleNode n){ + this.right = n; + } + + /** 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; + } + + /** 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. + *

+ * 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 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; + } + + /** Serializes the left and right hashes of this interior node + * into a {@code byte[]}. + * + *@return the serialized interior node + */ + public 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(MerkleTree tree) { + if (this.leftHash == null) { + this.leftHash = this.left.hash(tree); + } + if (this.rightHash == null) { + this.rightHash = this.right.hash(tree); + } + + 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/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/MerkleNode.java b/merkletree/src/main/java/org/coniks/merkletree/MerkleNode.java new file mode 100644 index 0000000..4a2c749 --- /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 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 new file mode 100644 index 0000000..a4d0c5a --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/MerkleTree.java @@ -0,0 +1,205 @@ +/* + 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; +import org.coniks.util.Convert; + +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; + } + + /** 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); + + // 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 new file mode 100644 index 0000000..b27488f --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/TreeNode.java @@ -0,0 +1,107 @@ +/* + 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 MerkleNode parent; // the parent of the node + private 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(MerkleNode parent, int level) { + this.parent = parent; + this.level = level; + this.name = "node"; + } + + /** Gets this tree node's parent. + * + *@return The parent as a {@link MerkleNode}. Will be {@code null} + * if the tree node is a {@link RootNode}. + */ + public MerkleNode 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 parent to {@code n} + */ + public void setParent(MerkleNode 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; + } + +} //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..3aa8584 --- /dev/null +++ b/merkletree/src/main/java/org/coniks/merkletree/UserLeafNode.java @@ -0,0 +1,141 @@ +/* + 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; + +// 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 TreeNode + implements MerkleNode { + + public static final String LEAF_IDENTIFIER = "L"; + + private String key; + private byte[] value; + private byte[] index; + private Commitment commit; + + 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; + 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 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[] hash(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+ + tree.getNonce().length+ + this.index.length+ + lvlBytes.length+commVal.length]; + + ByteBuffer arr = ByteBuffer.wrap(leafBytes); + arr.put(leafId); + arr.put(tree.getNonce()); + arr.put(this.index); + arr.put(lvlBytes); + arr.put(commVal); + + return Digest.digest(arr.array()); + } + + 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