11package torchwood
22
33import (
4+ "crypto/sha256"
45 "fmt"
56 "math/bits"
67
@@ -12,7 +13,7 @@ import (
1213// where the subtree has start index 0. A [tlog.RecordProof] is a special case
1314// of a SubtreeProof where the subtree has size 1.
1415//
15- // draft-ietf-plants-merkle-tree-certs-04 calls this a "Subtree Consistency
16+ // draft-ietf-plants-merkle-tree-certs calls this a "Subtree Consistency
1617// Proof".
1718type SubtreeProof []tlog.Hash
1819
@@ -22,6 +23,10 @@ func ProveSubtree(t, start, end int64, r tlog.HashReader) (SubtreeProof, error)
2223 if t < 0 || t > maxN || end > t || ! ValidSubtree (start , end ) {
2324 return nil , fmt .Errorf ("tlog: invalid inputs in ProveSubtree" )
2425 }
26+ if start == end {
27+ // The proof for an empty subtree is empty.
28+ return SubtreeProof {}, nil
29+ }
2530 indexes := subtreeProofIndex (0 , t , start , end , true , nil )
2631 if len (indexes ) == 0 {
2732 return SubtreeProof {}, nil
@@ -44,7 +49,7 @@ func ProveSubtree(t, start, end int64, r tlog.HashReader) (SubtreeProof, error)
4449// subtreeProofIndex builds the list of indexes needed to construct the proof
4550// that the subtree [start, end) is contained in the node with leaves [lo, hi).
4651// It appends those indexes to need and returns the result. See
47- // draft-ietf-plants-merkle-tree-certs-04 , Section 4.4. b reports whether the
52+ // draft-ietf-plants-merkle-tree-certs, Section 4.4. b reports whether the
4853// verifier already knows the hash of the subtree portion in [lo, hi); it starts
4954// true and becomes false past the first straddled node.
5055func subtreeProofIndex (lo , hi , start , end int64 , b bool , need []int64 ) []int64 {
@@ -115,6 +120,14 @@ func CheckSubtree(p SubtreeProof, t int64, th tlog.Hash, start, end int64, sh tl
115120 if t < 0 || t > maxN || end > t || ! ValidSubtree (start , end ) {
116121 return fmt .Errorf ("tlog: invalid inputs in CheckSubtree" )
117122 }
123+ if start == end {
124+ // An empty subtree is contained in every tree, so th is not checked:
125+ // the proof must be empty and sh must be the hash of the empty tree.
126+ if len (p ) != 0 || sh != emptyHash {
127+ return errProofFailed
128+ }
129+ return nil
130+ }
118131 sh2 , th2 , err := runSubtreeProof (p , 0 , t , start , end , true , sh )
119132 if err != nil {
120133 return err
@@ -179,7 +192,7 @@ func runSubtreeProof(p SubtreeProof, lo, hi, start, end int64, b bool, sh tlog.H
179192// contains a particular record. A [tlog.RecordProof] is a special case of a
180193// RecordInSubtreeProof where the subtree has start index 0.
181194//
182- // draft-ietf-plants-merkle-tree-certs-04 calls this a "Subtree Inclusion Proof".
195+ // draft-ietf-plants-merkle-tree-certs calls this a "Subtree Inclusion Proof".
183196type RecordInSubtreeProof []tlog.Hash
184197
185198// ProveRecordInSubtree returns the proof that the subtree [start, end) contains
@@ -224,16 +237,20 @@ func CheckRecordInSubtree(p RecordInSubtreeProof, start, end int64, sh tlog.Hash
224237}
225238
226239// SubtreeHash computes the hash for the subtree [start, end) using the
227- // HashReader to obtain previously stored hashes. SubtreeHash makes a single
228- // call to ReadHash requesting at most 1 + log₂(end - start) hashes.
240+ // HashReader to obtain previously stored hashes. SubtreeHash makes at most a
241+ // single call to ReadHash requesting at most 1 + log₂(end - start) hashes.
229242//
230243// A [tlog.TreeHash] is a special case of a SubtreeHash where start is 0.
244+ // The hash of an empty subtree is the hash of the empty string.
231245//
232- // See draft-ietf-plants-merkle-tree-certs-04 , Section 4.
246+ // See draft-ietf-plants-merkle-tree-certs, Section 4.
233247func SubtreeHash (start , end int64 , r tlog.HashReader ) (tlog.Hash , error ) {
234248 if ! ValidSubtree (start , end ) {
235249 return tlog.Hash {}, fmt .Errorf ("tlog: invalid inputs in SubtreeHash" )
236250 }
251+ if start == end {
252+ return emptyHash , nil
253+ }
237254 indexes := subTreeIndex (start , end , nil )
238255 hashes , err := r .ReadHashes (indexes )
239256 if err != nil {
@@ -249,31 +266,38 @@ func SubtreeHash(start, end int64, r tlog.HashReader) (tlog.Hash, error) {
249266 return hash , nil
250267}
251268
252- // ValidSubtree reports whether [start, end) is a valid subtree.
269+ // ValidSubtree reports whether [start, end) is a valid subtree. Note that
270+ // empty subtrees [x, x) are valid.
253271//
254- // See draft-ietf-plants-merkle-tree-certs-04 , Section 4.
272+ // See draft-ietf-plants-merkle-tree-certs, Section 4.
255273func ValidSubtree (start , end int64 ) bool {
256- if start < 0 || end <= start || end - start > maxN {
274+ if start < 0 || end < start || end - start > maxN {
257275 return false
258276 }
259- return start & (bitCeil (end - start )- 1 ) == 0
277+ return start == end || start & (bitCeil (end - start )- 1 ) == 0
260278}
261279
280+ // emptyHash is the hash of the empty tree and of empty subtrees, per
281+ // RFC 6962, Section 2.1. It is the hash of the empty string.
282+ var emptyHash = tlog .Hash (sha256 .Sum256 (nil ))
283+
262284// CoverInterval returns leftStart and mid for the two subtrees [leftStart, mid)
263285// and [mid, end) that cover the interval [start, end) as efficiently as
264286// possible.
265287//
266288// The subtrees are adjacent and the second ends at end, but the first may begin
267- // before start. See draft-ietf-plants-merkle-tree-certs-04, Section 4.5.
289+ // before start. If the interval has size zero or one, the first subtree is the
290+ // interval itself and the second is empty. If [start, end) is a subtree of size
291+ // larger than one, the two subtrees are its children. See
292+ // draft-ietf-plants-merkle-tree-certs, Section 4.5.
268293//
269- // It is an error if start < 0, end <= start, or [start, end) is already a
270- // subtree.
294+ // It is an error if start < 0 or end < start.
271295func CoverInterval (start , end int64 ) (leftStart , mid int64 , err error ) {
272- if start < 0 || end <= start {
296+ if start < 0 || end < start {
273297 return 0 , 0 , fmt .Errorf ("tlog: invalid interval in CoverInterval" )
274298 }
275- if ValidSubtree ( start , end ) {
276- return 0 , 0 , fmt . Errorf ( "tlog: interval is already a subtree in CoverInterval" )
299+ if end - start <= 1 {
300+ return start , end , nil
277301 }
278302 last := end - 1
279303 split := bits .Len64 (uint64 (start ^ last )) - 1
0 commit comments