Implementation of a Merkle hash tree. More...
Public Member Functions | |
MerkleTree (String digestAlgorithm, byte contentBlocks[][], boolean isDigest, int blockCount, int baseBlockIndex, int lastBlockLength) throws NoSuchAlgorithmException | |
Build a MerkleTree. | |
MerkleTree (byte[] content, int offset, int length, int blockWidth) | |
Segment content and build a MerkleTree. | |
MerkleTree (String digestAlgorithm, byte[] content, int offset, int length, int blockWidth) throws NoSuchAlgorithmException | |
Segment content and build a MerkleTree. | |
MerkleTree (byte contentBlocks[][], boolean isDigest, int blockCount, int baseBlockIndex, int lastBlockLength) | |
Build a MerkleTree. | |
String | digestAlgorithm () |
Returns the digest algorithm used by this tree. | |
int | leftChild (int nodeIndex) |
Find the index of the left child of a given node. | |
int | rightChild (int nodeIndex) |
Find the index of the right child of a given node. | |
byte[] | root () |
Return the root digest. | |
DEROctetString | derRoot () |
Get the DEROctetString wrapped digest of the root node. | |
int | size () |
Get the size of the tree, in nodes. | |
byte[] | get (int nodeIndex) |
Returns the digest at the specified node. | |
DEROctetString | derGet (int nodeIndex) |
Returns the digest at the specified node as a DEROctetString. | |
int | numLeaves () |
Get the number of leaves in the tree. | |
int | nodeCount () |
Calculates the number of nodes in this tree. | |
int | firstLeaf () |
Returns the node index of the first leaf. | |
int | leafNodeIndex (int leafIndex) |
Get the node index of a given leaf. | |
byte[] | leaf (int leafIndex) |
Retrieve the digest of a given leaf node. | |
MerklePath | path (int leafNum) |
Generate a MerklePath for a given leaf, to use in verifying that leaf. | |
int | maxDepth () |
Get the maximum depth of this MerkleTree. | |
int | getNodeIndex (DEROctetString node) |
Function for validating paths. | |
byte[] | getRootAsEncodedDigest () |
Get the root node as an encoded PKCS#1 DigestInfo. | |
Static Public Member Functions | |
static int | parent (int nodeIndex) |
Find the index of the parent of this node. | |
static int | sibling (int nodeIndex) |
Find the index of this node's sibling. | |
static boolean | isRight (int nodeIndex) |
Check internal node index (not translated to leaves) to see if it is a left or right child. | |
static boolean | isLeft (int nodeIndex) |
Check internal node index (not translated to leaves) to see if it is a left or right child. | |
static int | nodeCount (int numLeaves) |
Calculate the number of nodes in a tree with a given number of leaves. | |
static int | maxPathLength (int nodeIndex) |
What is the maximum path length to a node with this node index, including its sibling but not including the root? | |
static int | maxDepth (int numLeaves) |
What is the maximum depth of a Merkle tree with a given number of leaves. | |
static byte[] | computeBlockDigest (String digestAlgorithm, byte[] content) throws NoSuchAlgorithmException |
Compute the digest of a leaf node. | |
static byte[] | computeBlockDigest (String digestAlgorithm, byte[] content, int offset, int length) throws NoSuchAlgorithmException |
Compute the digest of a leaf node. | |
static byte[] | computeBlockDigest (byte[] block) |
Compute the digest of a block using CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM. | |
static byte[] | computeNodeDigest (String algorithm, byte[] left, byte[] right) throws NoSuchAlgorithmException |
Compute the digest for an intermediate node. | |
static byte[] | computeNodeDigest (byte[] left, byte[] right) |
Compute the digest for an intermediate node with two children. | |
static boolean | isMerkleTree (AlgorithmIdentifier algorithmId) |
Does this algorithm identifier indicate a Merkle tree? | |
static double | log2 (int arg) |
Helper method. | |
static int | blockCount (int length, int blockWidth) |
The number of blocks of blockWidth (bytes) necessary to hold length (bytes). | |
Protected Member Functions | |
MerkleTree (String digestAlgorithm, int numLeaves) | |
Subclass constructor. | |
void | initializeTree (byte contentBlocks[][], boolean isDigest, int baseBlockIndex, int lastBlockLength) throws NoSuchAlgorithmException |
Method called by constructors to fill leaf nodes with digests and compute intermediate node values up the tree. | |
void | initializeTree (byte[] content, int offset, int length, int blockWidth) throws NoSuchAlgorithmException |
Method called by constructors to fill leaf nodes with digests and compute intermediate node values up the tree. | |
void | computeLeafValues (byte contentBlocks[][], boolean isDigest, int baseBlockIndex, int lastBlockLength) throws NoSuchAlgorithmException |
Compute the raw digest of the leaf content blocks, and format them appropriately. | |
void | computeLeafValues (byte[] content, int offset, int length, int blockWidth) throws NoSuchAlgorithmException |
Compute the raw digest of the leaf content blocks, and format them appropriately. | |
void | computeNodeValues () throws NoSuchAlgorithmException |
Compute the intermediate node values by digesting the concatenation of the left and right children (or the left child alone if there is no right child). | |
byte[] | computeBlockDigest (int leafIndex, byte contentBlocks[][], int baseBlockIndex, int lastBlockLength) throws NoSuchAlgorithmException |
Compute the digest of a leaf node. | |
byte[] | computeBlockDigest (int leafIndex, byte[] content, int offset, int length) throws NoSuchAlgorithmException |
Compute the digest of a leaf node. | |
Protected Attributes | |
DEROctetString[] | _tree |
int | _numLeaves |
String | _digestAlgorithm |
Static Protected Attributes | |
static final int | ROOT_NODE = 1 |
Node index of 1 (array index of 0). | |
static final String | MERKLE_OID_PREFIX = "1.2.840.113550.11.1.2" |
The OID prefix we use to represent Merkle trees. |
Implementation of a Merkle hash tree.
Representation based on Knuth, Vol 1, section 2.3.4.5. We represent trees as a special sublcass of extended binary trees, where empty subtrees are only present in one end of the tree.
Tree nodes are numbered starting with 1, which is the root.
Tree nodes are stored in an array, with node i stored at index i-1 into the array.
Incomplete binary trees are represented as multi-level extended binary trees -- lower-numbered leaves are represented in the upper half of the tree, in a layer one closer to the root than leaves in the complete subtree.
Total number of nodes in the tree = 2n + 1, where n is the number of leaves.
Taken in terms of node indices (where root == 1), the parent of node k is node floor(k/2), and the children of node k are nodes 2k and 2k+1. Leaves are numbered from node n+1 through 2n+1, where n is the number of leaves.
The sibling index of node k is (k xor 1).
Should we want to get fancy, we could have t-ary trees; the construction above works for tree with internal nodes (non-leaves) {1,2,...,n}.
The parent of node k is the node floor((k+t-2)/t) = ceil((k-1)/t). The children of node k are: t(k-1)+2, t(k-1)+3,..., tk+1
In the methods below, we refer to nodes as having a "nodeIndex" -- their 1-based index into the node array as described above. Leaf nodes also have a "leafIndex" -- their index into the set of n leaves. Convenience methods are provided to convert between the two.
Store node digests internally as DEROctetStrings for more efficient encoding.
org.ccnx.ccn.impl.security.crypto.MerkleTree.MerkleTree | ( | String | digestAlgorithm, | |
byte | contentBlocks[][], | |||
boolean | isDigest, | |||
int | blockCount, | |||
int | baseBlockIndex, | |||
int | lastBlockLength | |||
) | throws NoSuchAlgorithmException |
Build a MerkleTree.
This initializes the tree with content, builds the leaf and intermediate digests, and derives the root digest.
digestAlgorithm | the digest algorithm to use for computing leaf and interior node digests of this tree | |
contentBlocks | the segmented leaf content to be hashed into this Merkle hash tree. One block per leaf. | |
isDigest | are the content blocks raw content (false), or are they already digested with digestAlgorithm? (default algorithm: CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM) | |
blockCount | the number of those blocks to include (e.g. we may not have filled our contentBlocks buffers prior to building the tree). Must be at least 2. | |
baseBlockIndex | the offset into the contentBlocks array at which to start. | |
lastBlockLength | the number of bytes of the last block to use |
NoSuchAlgorithmException |
org.ccnx.ccn.impl.security.crypto.MerkleTree.MerkleTree | ( | byte[] | content, | |
int | offset, | |||
int | length, | |||
int | blockWidth | |||
) |
Segment content and build a MerkleTree.
This initializes the tree with content, builds the leaf and intermediate digests, and derives the root digest. Uses CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM.
content | the content to segment into leaves and hash into this Merkle hash tree. One blockWidth of content per leaf, except for the last leaf which may be shorter. | |
offset | offset into content at which to start processing data. | |
length | number of bytes of content to process | |
blockWidth | the length of leaf blocks to create |
org.ccnx.ccn.impl.security.crypto.MerkleTree.MerkleTree | ( | String | digestAlgorithm, | |
byte[] | content, | |||
int | offset, | |||
int | length, | |||
int | blockWidth | |||
) | throws NoSuchAlgorithmException |
Segment content and build a MerkleTree.
This initializes the tree with content, builds the leaf and intermediate digests, and derives the root digest.
digestAlgorithm | the digest algorithm to use for computing leaf and interior node digests of this tree | |
content | the content to segment into leaves and hash into this Merkle hash tree. One blockWidth of content per leaf, except for the last leaf which may be shorter. | |
offset | offset into content at which to start processing data. | |
length | number of bytes of content to process | |
blockWidth | the length of leaf blocks to create |
org.ccnx.ccn.impl.security.crypto.MerkleTree.MerkleTree | ( | byte | contentBlocks[][], | |
boolean | isDigest, | |||
int | blockCount, | |||
int | baseBlockIndex, | |||
int | lastBlockLength | |||
) |
Build a MerkleTree.
This initializes the tree with content, builds the leaf and intermediate digests, and derives the root digest. Uses CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM.
contentBlocks | the segmented leaf content to be hashed into this Merkle hash tree. One block per leaf. | |
isDigest | are the content blocks raw content (false), or are they already digested with digestAlgorithm? (default algorithm: CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM) | |
blockCount | the number of those blocks to include (e.g. we may not have filled our contentBlocks buffers prior to building the tree). Must be at least 2. | |
baseBlockIndex | the offset into the contentBlocks array at which to start. | |
lastBlockLength | the amount of the last block to use |
NoSuchAlgorithmException |
org.ccnx.ccn.impl.security.crypto.MerkleTree.MerkleTree | ( | String | digestAlgorithm, | |
int | numLeaves | |||
) | [protected] |
Subclass constructor.
digestAlgorithm | digest algorithm to use. If null, use CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM. | |
numLeaves | the number of leaf nodes to reserve space for |
static int org.ccnx.ccn.impl.security.crypto.MerkleTree.blockCount | ( | int | length, | |
int | blockWidth | |||
) | [static] |
The number of blocks of blockWidth (bytes) necessary to hold length (bytes).
length | the buffer length | |
blockWidth | the segment with |
static byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeBlockDigest | ( | byte[] | block | ) | [static] |
Compute the digest of a block using CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM.
DKS TODO - check -- was being by MerklePath to compute digest for root without properly recovering OID from encoded path.
block | block to digest |
static byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeBlockDigest | ( | String | digestAlgorithm, | |
byte[] | content, | |||
int | offset, | |||
int | length | |||
) | throws NoSuchAlgorithmException [static] |
Compute the digest of a leaf node.
digestAlgorithm | the digest algorithm to use | |
content | the content to segment into leaves and hash into this Merkle hash tree. | |
offset | offset into content at which this leaf starts | |
length | number of bytes of content in this leaf |
NoSuchAlgorithmException | if digestAlgorithm is unknown |
static byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeBlockDigest | ( | String | digestAlgorithm, | |
byte[] | content | |||
) | throws NoSuchAlgorithmException [static] |
Compute the digest of a leaf node.
digestAlgorithm | the digest algorithm to use | |
content | the content of this leaf |
NoSuchAlgorithmException | if digestAlgorithm is unknown |
byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeBlockDigest | ( | int | leafIndex, | |
byte[] | content, | |||
int | offset, | |||
int | length | |||
) | throws NoSuchAlgorithmException [protected] |
Compute the digest of a leaf node.
Separate this out so that it can be overridden.
leafIndex | The index of the leaf we are computing the digest of. | |
content | the content to segment into leaves and hash into this Merkle hash tree. | |
offset | offset into content at which this leaf starts | |
length | number of bytes of content in this leaf |
NoSuchAlgorithmException | if digestAlgorithm is unknown |
Reimplemented in org.ccnx.ccn.impl.security.crypto.CCNMerkleTree.
byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeBlockDigest | ( | int | leafIndex, | |
byte | contentBlocks[][], | |||
int | baseBlockIndex, | |||
int | lastBlockLength | |||
) | throws NoSuchAlgorithmException [protected] |
Compute the digest of a leaf node.
Separate this out so that it can be overridden.
leafIndex | The index of the leaf we are computing the digest of. | |
contentBlocks | The array of content blocks containing the leaf content. | |
baseBlockIndex | The first content block in the array containing leaf content (if rolling buffers). numLeaves() blocks contain leaf content, so the last block used is blockOffset+numLeaves(). | |
lastBlockLength | the number of bytes of the last block to use, can be smaller than the number available |
NoSuchAlgorithmException |
void org.ccnx.ccn.impl.security.crypto.MerkleTree.computeLeafValues | ( | byte[] | content, | |
int | offset, | |||
int | length, | |||
int | blockWidth | |||
) | throws NoSuchAlgorithmException [protected] |
Compute the raw digest of the leaf content blocks, and format them appropriately.
uses computeBlockDigest(int, byte[], int, int) to compute the leaf digest.
content | the content to segment into leaves and hash into this Merkle hash tree. One blockWidth of content per leaf, except for the last leaf which may be shorter. | |
offset | offset into content at which to start processing data. | |
length | number of bytes of content to process | |
blockWidth | the length of leaf blocks to create |
NoSuchAlgorithmException | if digestAlgorithm is unknown |
void org.ccnx.ccn.impl.security.crypto.MerkleTree.computeLeafValues | ( | byte | contentBlocks[][], | |
boolean | isDigest, | |||
int | baseBlockIndex, | |||
int | lastBlockLength | |||
) | throws NoSuchAlgorithmException [protected] |
Compute the raw digest of the leaf content blocks, and format them appropriately.
contentBlocks | the leaf content, one leaf per array | |
isDigest | have these been digested already, or do we need to digest them using computeBlockDigest(int, byte [][], int, int)? | |
baseBlockIndex | first block in the array to use | |
lastBlockLength | number of bytes of the last block to use; N/A if isDigest is true |
NoSuchAlgorithmException | if digestAlgorithm is unknown |
static byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeNodeDigest | ( | byte[] | left, | |
byte[] | right | |||
) | [static] |
Compute the digest for an intermediate node with two children.
left | left child | |
right | right child |
static byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.computeNodeDigest | ( | String | algorithm, | |
byte[] | left, | |||
byte[] | right | |||
) | throws NoSuchAlgorithmException [static] |
Compute the digest for an intermediate node.
If this is a last left child (right is null), simply hash left alone.
NoSuchAlgorithmException |
void org.ccnx.ccn.impl.security.crypto.MerkleTree.computeNodeValues | ( | ) | throws NoSuchAlgorithmException [protected] |
Compute the intermediate node values by digesting the concatenation of the left and right children (or the left child alone if there is no right child).
NoSuchAlgorithmException | if digestAlgorithm is unknown |
DEROctetString org.ccnx.ccn.impl.security.crypto.MerkleTree.derGet | ( | int | nodeIndex | ) |
Returns the digest at the specified node as a DEROctetString.
nodeIndex | 1-based node index |
DEROctetString org.ccnx.ccn.impl.security.crypto.MerkleTree.derRoot | ( | ) |
Get the DEROctetString wrapped digest of the root node.
String org.ccnx.ccn.impl.security.crypto.MerkleTree.digestAlgorithm | ( | ) |
Returns the digest algorithm used by this tree.
int org.ccnx.ccn.impl.security.crypto.MerkleTree.firstLeaf | ( | ) |
Returns the node index of the first leaf.
The node index of the first leaf is either size()-numleaves(), or nodeIndex = numLeaves.
byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.get | ( | int | nodeIndex | ) |
Returns the digest at the specified node.
nodeIndex | 1-based node index |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.getNodeIndex | ( | DEROctetString | node | ) |
Function for validating paths.
Given a digest, it returns what node in the tree has that digest. If no node has that digest, returns 0. If argument is null, returns -1. Slow.
node | the node digest to validate |
byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.getRootAsEncodedDigest | ( | ) |
Get the root node as an encoded PKCS#1 DigestInfo.
void org.ccnx.ccn.impl.security.crypto.MerkleTree.initializeTree | ( | byte[] | content, | |
int | offset, | |||
int | length, | |||
int | blockWidth | |||
) | throws NoSuchAlgorithmException [protected] |
Method called by constructors to fill leaf nodes with digests and compute intermediate node values up the tree.
Does its work by calling computeLeafValues(byte [], int, int, int) and computeNodeValues(). Separate this out to allow subclasses to initialize members before building tree.
NoSuchAlgorithmException | if the digestAlgorithm specified for this tree is unknown |
void org.ccnx.ccn.impl.security.crypto.MerkleTree.initializeTree | ( | byte | contentBlocks[][], | |
boolean | isDigest, | |||
int | baseBlockIndex, | |||
int | lastBlockLength | |||
) | throws NoSuchAlgorithmException [protected] |
Method called by constructors to fill leaf nodes with digests and compute intermediate node values up the tree.
Does its work by calling computeLeafValues(byte [][], boolean, int, int) and computeNodeValues(). Separate this out to allow subclasses to initialize members before building tree.
NoSuchAlgorithmException | if the digestAlgorithm specified for this tree is unknown |
static boolean org.ccnx.ccn.impl.security.crypto.MerkleTree.isLeft | ( | int | nodeIndex | ) | [static] |
Check internal node index (not translated to leaves) to see if it is a left or right child.
Internal nodes for a layer always start with an even index, as 1 is the root and the only layer with one member. Every other layer has an even number of nodes (except for possibly a dangling child at the end). So, left nodes have even indices, and right nodes have odd ones.
nodeIndex | node to check whether it is a left child |
static boolean org.ccnx.ccn.impl.security.crypto.MerkleTree.isMerkleTree | ( | AlgorithmIdentifier | algorithmId | ) | [static] |
Does this algorithm identifier indicate a Merkle tree?
algorithmId | the algorithm identifier |
static boolean org.ccnx.ccn.impl.security.crypto.MerkleTree.isRight | ( | int | nodeIndex | ) | [static] |
Check internal node index (not translated to leaves) to see if it is a left or right child.
Internal nodes for a layer always start with an even index, as 1 is the root and the only layer with one member. Every other layer has an even number of nodes (except for possibly a dangling child at the end). So, left nodes have even indices, and right nodes have odd ones.
nodeIndex | node to check whether it is a right child |
byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.leaf | ( | int | leafIndex | ) |
Retrieve the digest of a given leaf node.
Returns null if there is no leaf leafIndex.
leafIndex | leaf index, starting at 0 for the first leaf. |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.leafNodeIndex | ( | int | leafIndex | ) |
Get the node index of a given leaf.
leafIndex | the index of a leaf |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.leftChild | ( | int | nodeIndex | ) |
Find the index of the left child of a given node.
nodeIndex | the (1-based) index of the node whose child we want to find |
static double org.ccnx.ccn.impl.security.crypto.MerkleTree.log2 | ( | int | arg | ) | [static] |
Helper method.
arg |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.maxDepth | ( | ) |
Get the maximum depth of this MerkleTree.
static int org.ccnx.ccn.impl.security.crypto.MerkleTree.maxDepth | ( | int | numLeaves | ) | [static] |
What is the maximum depth of a Merkle tree with a given number of leaves.
If the tree isn't balanced, many nodes may have shorter paths than maxDepth.
numLeaves | the number of leaves in the tree. |
static int org.ccnx.ccn.impl.security.crypto.MerkleTree.maxPathLength | ( | int | nodeIndex | ) | [static] |
What is the maximum path length to a node with this node index, including its sibling but not including the root?
nodeIndex | the node to find the path length for |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.nodeCount | ( | ) |
Calculates the number of nodes in this tree.
static int org.ccnx.ccn.impl.security.crypto.MerkleTree.nodeCount | ( | int | numLeaves | ) | [static] |
Calculate the number of nodes in a tree with a given number of leaves.
numLeaves | the number of leaves |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.numLeaves | ( | ) |
Get the number of leaves in the tree.
static int org.ccnx.ccn.impl.security.crypto.MerkleTree.parent | ( | int | nodeIndex | ) | [static] |
Find the index of the parent of this node.
nodeIndex | is a (1-based) node index whose parent we want to find. |
MerklePath org.ccnx.ccn.impl.security.crypto.MerkleTree.path | ( | int | leafNum | ) |
Generate a MerklePath for a given leaf, to use in verifying that leaf.
There are a variety of traversal algorithms for computing/reading Merkle hash trees.
We need to represent the leaves so that the user a) knows what order they come in, and b) also knows which is the leaf being represented. The cheapest way to do that is to represent the leaves in order, and also start out with an indication of whether this leaf is the left or right of the last pair. To make this most general and easy to use, we will represent this path as
MerklePath ::= SEQUENCE { nodeIndex INTEGER, nodes NodeList }
NodeList ::= SEQUENCE OF OCTET STRING
the nodeIndex here is the index of the leaf node in the tree as a whole (not just among the leaves), and the nodes list contains neither the digest of the leaf itself nor the root of the tree.
We could probably save a few bytes by not encoding this as DER, and simply packing in the bytes to represent this data -- this encoding offers a fair amount of ease of parsing and clarity, at the cost of probably 5 + 2*pathLength bytes of overhead, or 20 bytes in typical paths. At some point this may seem too much, and we will move to a more compact encoding.
leafNum | the leaf index of the leaf |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.rightChild | ( | int | nodeIndex | ) |
Find the index of the right child of a given node.
nodeIndex | the (1-based) index of the node whose child we want to find |
byte [] org.ccnx.ccn.impl.security.crypto.MerkleTree.root | ( | ) |
Return the root digest.
static int org.ccnx.ccn.impl.security.crypto.MerkleTree.sibling | ( | int | nodeIndex | ) | [static] |
Find the index of this node's sibling.
Everything always has a sibling, in this formulation of (not-necessarily-complete binary trees). For root, returns 0.
nodeIndex | the (1-based) index of the node whose sibling we want to find |
int org.ccnx.ccn.impl.security.crypto.MerkleTree.size | ( | ) |
Get the size of the tree, in nodes.
(This is the number of nodes, not the number of leaves.)
final String org.ccnx.ccn.impl.security.crypto.MerkleTree.MERKLE_OID_PREFIX = "1.2.840.113550.11.1.2" [static, protected] |
The OID prefix we use to represent Merkle trees.
Derived from PARC-s sub-arc of Xerox's OID.