Skip to content

Commit b2ef37c

Browse files
author
Diana Smetters
committed
Documentation
1 parent 20cd73e commit b2ef37c

File tree

5 files changed

+305
-132
lines changed

5 files changed

+305
-132
lines changed

javasrc/src/org/ccnx/ccn/impl/security/crypto/CCNMerkleTree.java

+84-26
Original file line numberDiff line numberDiff line change
@@ -36,31 +36,40 @@
3636

3737

3838
/**
39-
* This class extends your basic Merkle tree to
40-
* incorporate the block name at each node, so that
39+
* This class extends the basic MerkleTree for use in CCN.
40+
* It incorporates the CCN ContentName for an object at each node, so that
4141
* names are authenticated as well as content in a
4242
* way that intermediary CCN nodes can verify.
4343
*
44-
* For each content node in the CCNMerkleTree, we compute its
45-
* digest in the same way we would compute the digest of a leaf
46-
* node for signing (incorporating the name, authentication metadata,
47-
* and content). We then combine all these together into a MerkleTree,
44+
* For each leaf node in the CCNMerkleTree, we compute its
45+
* digest in exactly the same way we would compute the digest of a ContentObject
46+
* node for signing on its own (incorporating the name, authentication metadata,
47+
* and content). We then combine all these leaf digests together into a MerkleTree,
4848
* and sign the root node.
4949
*
5050
* To generate a leaf block digest, therefore, we need to know
5151
* - the content of the block
52-
* - the name for the block (which, for fragmented content, includes the fragment
52+
* - the name for the block (which, for segmented content, includes the segmented
5353
* number. If we're buffering content and building trees per buffer, the
5454
* fragment numbers may carry across buffers (e.g. leaf 0 of this tree might
55-
* be fragment 37 of the content as a whole)
55+
* be fragment 37 of the content as a whole)
56+
*
5657
* - the authentication metadata. In the case of fragmented content, this is
5758
* likely to be the same for all blocks. In the case of other content, the
5859
* publisher is likely to be the same, but the timestamp and even maybe the
5960
* type could be different -- i.e. you could use a CCNMerkleTree to amortize
6061
* signature costs over any collection of data, not just a set of fragments.
6162
*
62-
* So, we either need to hand in all the names, or a function to call to get
63-
* the name for each block.
63+
* So, we either need to hand in all the names, or have a function to call to get
64+
* the name for each block.
65+
*
66+
* Note: There is no requirement that a CCNMerkleTree be built only from the segments
67+
* of a single piece of content, although that is the most common use. One
68+
* can build and verify a CCNMerkleTree built out of an arbitrary set of
69+
* ContentObjects; this may be a useful way of limiting the number of
70+
* signatures generated on constrained platforms. Eventually the CCNSegmenter
71+
* will be extended to handle such collections of arbitrary objects.
72+
*
6473
*/
6574
public class CCNMerkleTree extends MerkleTree {
6675

@@ -70,15 +79,15 @@ public class CCNMerkleTree extends MerkleTree {
7079
ContentObject [] _segmentObjects = null;
7180

7281
/**
73-
* Build a CCNMerkleTree from a set of leaf content objects.
82+
* Build a CCNMerkleTree from a set of leaf ContentObjects.
7483
* @param contentObjects must be at least 2 blocks, or will throw IllegalArgumentException.
75-
* @param signingKey key to sign root with
84+
* @param signingKey key to sign the root with
7685
* @throws NoSuchAlgorithmException if key or DEFAULT_DIGEST_ALGORITHM are unknown
7786
* @throws InvalidKeyException if signingKey is invalid
7887
* @throws SignatureException if we cannot sign
7988
*/
8089
public CCNMerkleTree(ContentObject [] contentObjects,
81-
PrivateKey signingKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
90+
PrivateKey signingKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
8291

8392
super(CCNDigestHelper.DEFAULT_DIGEST_ALGORITHM, ((null != contentObjects) ? contentObjects.length : 0));
8493
_segmentObjects = contentObjects;
@@ -115,7 +124,12 @@ public ContentName segmentName(int leafIndex) {
115124
return _segmentObjects[leafIndex].name();
116125
return null;
117126
}
118-
127+
128+
/**
129+
* Return the SignedInfo for a given segment.
130+
* @param leafIndex the index of the leaf whose SignedInfo we want
131+
* @return the SignedInfo
132+
*/
119133
public SignedInfo segmentSignedInfo(int leafIndex) {
120134
if ((leafIndex < 0) || (leafIndex > _segmentObjects.length))
121135
throw new IllegalArgumentException("Index out of range!");
@@ -125,7 +139,12 @@ public SignedInfo segmentSignedInfo(int leafIndex) {
125139
}
126140
return null;
127141
}
128-
142+
143+
/**
144+
* Set the signature for a particular segment.
145+
* @param leafIndex the leaf segment to set the signature for
146+
* @return the Signature
147+
*/
129148
public Signature segmentSignature(int leafIndex) {
130149
if ((leafIndex < 0) || (leafIndex > _segmentObjects.length))
131150
throw new IllegalArgumentException("Index out of range!");
@@ -141,36 +160,74 @@ public Signature segmentSignature(int leafIndex) {
141160
}
142161

143162
/**
144-
* Sets the signatures of all the blockObjects.
145-
* TODO DKS refactor this class to remove unused stuff.
163+
* Sets the signatures of all the contained ContentObjects.
146164
*/
147165
public void setSignatures() {
148166
for (int i=0; i < numLeaves(); ++i) {
149167
segmentSignature(i); // DKS TODO refactor, sets signature as a side effect
150168
}
151169
}
152-
170+
171+
/**
172+
* A version of initializeTree to go with the CCNMerkleTree(ContentObject []) constructor.
173+
* @param contentObjects objects to build into the tree
174+
* @throws NoSuchAlgorithmException if the default digest algorithm unknown
175+
*/
153176
protected void initializeTree(ContentObject [] contentObjects) throws NoSuchAlgorithmException {
154177
if (contentObjects.length < numLeaves())
155178
throw new IllegalArgumentException("MerkleTree: cannot build tree from more blocks than given! Have " + contentObjects.length + " blocks, asked to use: " + (numLeaves()));
156179

157180
computeLeafValues(contentObjects);
158181
computeNodeValues();
159182
}
160-
183+
184+
/**
185+
* Construct the Signature for a given leaf. This is composed of the rootSignature(),
186+
* which is the same for all nodes, and the DER encoded MerklePath for this leaf as the
187+
* witness.
188+
* @param leafIndex the leaf to compute the signature for
189+
* @return the signature
190+
*/
161191
protected Signature computeSignature(int leafIndex) {
162192
MerklePath path = path(leafIndex);
163193
return new Signature(path.derEncodedPath(), rootSignature());
164194
}
165-
195+
196+
/**
197+
* Compute the signature on the root node. It's already a digest, so in
198+
* theory we could just wrap it up in some PKCS#1 padding, encrypt it
199+
* with our private key, and voila! A signature. But there are basically
200+
* know crypto software packages that provide signature primitives that take
201+
* already-digested data and just do the padding and encryption, and so we'd
202+
* be asking anyone attepmpting to implement CCN MHT signing (including ourselves)
203+
* to re-implement a very complicated wheel, across a number of signature algorithms.
204+
* We might also want to sign with a key that does not support the digest algorithm
205+
* we used to compute the root (for example, DSA).
206+
* So take the computationally very slightly more expensive, but vastly simpler
207+
* (implementation-wise) approach of taking our digest and signing it with
208+
* a standard signing API -- which means digesting it one more time for the
209+
* signature. So we sign (digest + encrypt) the root digest.
210+
*
211+
* @param root the root digest to sign
212+
* @param signingKey the key to sign with
213+
* @return the bytes of the signature
214+
* @throws InvalidKeyException
215+
* @throws SignatureException
216+
* @throws NoSuchAlgorithmException
217+
*/
166218
protected static byte [] computeRootSignature(byte [] root, PrivateKey signingKey) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
167219
// Given the root of the authentication tree, compute a signature over it
168220
// Right now, this will digest again. It's actually quite hard to get at the raw
169221
// signature guts for various platforms to avoid re-digesting; too dependent on
170222
// the sig alg used.
171223
return CCNSignatureHelper.sign(null, root, signingKey);
172224
}
173-
225+
226+
/**
227+
* Compute the leaf values of the ContentObjects in this tree
228+
* @param contentObjects the content
229+
* @throws NoSuchAlgorithmException if the digestAlgorithm unknown
230+
*/
174231
protected void computeLeafValues(ContentObject [] contentObjects) throws NoSuchAlgorithmException {
175232
// Hash the leaves
176233
for (int i=0; i < numLeaves(); ++i) {
@@ -185,11 +242,12 @@ protected void computeLeafValues(ContentObject [] contentObjects) throws NoSuchA
185242
* We need to incorporate the name of the content block
186243
* and the signedInfo into the leaf digest of the tree.
187244
* Essentially, we want the leaf digest to be the same thing
188-
* we would use for signing a stand-alone leaf.
189-
* @param leafIndex
190-
* @param contentBlocks
191-
* @return
192-
* @throws
245+
* we would use for signing a stand-alone ContentObject.
246+
* @param leafIndex the index of the leaf to sign
247+
* @param content the content array containing the leaf content
248+
* @param offset the offset into content where the leaf start
249+
* @param length the length of content for this leaf
250+
* @return the block digest
193251
*/
194252
@Override
195253
protected byte [] computeBlockDigest(int leafIndex, byte [] content, int offset, int length) {

javasrc/src/org/ccnx/ccn/impl/security/crypto/MerklePath.java

+46-15
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
import org.ccnx.ccn.impl.security.crypto.util.CryptoUtil;
3333
import org.ccnx.ccn.impl.security.crypto.util.OIDLookup;
3434

35-
35+
36+
/**
37+
* A representation of a path through a MerkleTree.
38+
*/
3639
public class MerklePath {
3740

3841
int _leafNodeIndex;
@@ -41,12 +44,22 @@ public class MerklePath {
4144
// DKS TODO: implement lookup mechanism to get MHT
4245
// OID from component digest OID. Right now just pull
4346
// from CCNMerkleTree.
44-
47+
48+
/**
49+
* Create a MerklePath for a given leaf
50+
* @param leafNodeIndex the leaf index
51+
* @param path the node digests necessary to verify that leaf
52+
*/
4553
public MerklePath(int leafNodeIndex, DEROctetString [] path) {
4654
_leafNodeIndex = leafNodeIndex;
4755
_path = path;
4856
}
49-
57+
58+
/**
59+
* Decode a DER encoded MerklePath
60+
* @param derEncodedPath the encoded path
61+
* @throws CertificateEncodingException if there is a decoding error
62+
*/
5063
public MerklePath(byte [] derEncodedPath) throws CertificateEncodingException {
5164
DERObject decoded = CryptoUtil.decode(derEncodedPath);
5265
ASN1Sequence seq = (ASN1Sequence)decoded;
@@ -60,7 +73,14 @@ public MerklePath(byte [] derEncodedPath) throws CertificateEncodingException {
6073
_path[i++] = (DEROctetString)en.nextElement();
6174
}
6275
}
63-
76+
77+
/**
78+
* Compute the parent digest of the current node
79+
* @param node the current node
80+
* @param length the length of the path at this point
81+
* @param pathDigest the previously computed digest along this path
82+
* @return the parent digest
83+
*/
6484
protected byte [] computeParent(int node, int length, byte [] pathDigest) {
6585
byte [] parentDigest = null;
6686
if (MerkleTree.isRight(node)) {
@@ -73,12 +93,14 @@ public MerklePath(byte [] derEncodedPath) throws CertificateEncodingException {
7393

7494
/**
7595
* Take the content block for which this is the MerklePath,
76-
* and verify that it matches. The caller then needs
77-
* to check whether the root is authentic.
96+
* and compute the root digest for verification. The caller then needs
97+
* to check whether it matches the root, and
98+
* the root is authentic (signed by a trusted key).
7899
* @param nodeContent either the content of the block or its
79100
* digest. If a subclass of MerkleTree overrides computeBlockDigest,
80-
* a caller must hand in the digest, as this uses the MerkleTree default.
81-
* @return
101+
* a caller must hand in the digest, as this uses the MerkleTree default.
102+
* @param isDigest was this node already digested, or do we need to digest it
103+
* @return the computed root digest
82104
*/
83105
public byte [] root(byte [] nodeContent, boolean isDigest) {
84106
if ((leafNodeIndex() < MerkleTree.ROOT_NODE) || (_path == null) ||
@@ -108,16 +130,20 @@ public MerklePath(byte [] derEncodedPath) throws CertificateEncodingException {
108130
}
109131

110132
/**
111-
* Entry in the path, where i is the index into the path array.
112-
* @param i
113-
* @return
133+
* Get an in the path, where i is the index into the path array.
134+
* @param i the entry we want
135+
* @return the entry
114136
*/
115137
public DEROctetString entry(int i) {
116138
if ((i < 0) || (i >= _path.length))
117139
return null;
118140
return _path[i];
119141
}
120-
142+
143+
/**
144+
* Return the leaf node this path is for
145+
* @return the leaf node index
146+
*/
121147
public int leafNodeIndex() { return _leafNodeIndex; }
122148

123149
public int pathLength() {
@@ -128,7 +154,8 @@ public int pathLength() {
128154

129155
/**
130156
* DER-encode the path. Embed it in a DigestInfo
131-
* with the appropriate algorithm identifier.
157+
* with the appropriate algorithm identifier.
158+
* @return the DER-encoded path
132159
*/
133160
public byte [] derEncodedPath() {
134161

@@ -147,7 +174,12 @@ public int pathLength() {
147174
// Wrap it up in a DigestInfo
148175
return CCNDigestHelper.digestEncoder(CCNMerkleTree.DEFAULT_MHT_ALGORITHM, encodedPath);
149176
}
150-
177+
178+
/**
179+
* Determine whether a given DigestInfo contains a MerklePath
180+
* @param info the DigestInfo
181+
* @return true if this is a MerklePath, false otherwise
182+
*/
151183
public static boolean isMerklePath(DigestInfo info) {
152184
AlgorithmIdentifier digestAlg =
153185
new AlgorithmIdentifier(OIDLookup.getDigestOID(CCNMerkleTree.DEFAULT_MHT_ALGORITHM));
@@ -178,5 +210,4 @@ public boolean equals(Object obj) {
178210
return false;
179211
return true;
180212
}
181-
182213
}

0 commit comments

Comments
 (0)