aboutsummaryrefslogtreecommitdiff
path: root/internal/logutil/logutil.go
blob: 27c3a7341bfdc708ea5dcc6fcbc5b46b784566a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// Package logutil wraps functions related to a log's Merkle tree.  All log
// queries use a context where the deadline is set to a hardcoded value.
package logutil

import (
	"context"
	"crypto/sha256"
	"fmt"
	"time"

	ct "github.com/google/certificate-transparency-go"
	"github.com/google/certificate-transparency-go/client"
	"github.com/transparency-dev/merkle/compact"
	"github.com/transparency-dev/merkle/rfc6962"
	"gitlab.torproject.org/rgdd/ct/pkg/merkle"
	"rgdd.se/silent-ct/internal/ioutil"
)

const timeout = 10 * time.Second

func GetSignedTreeHead(ctx context.Context, cli client.CheckLogClient) (*ct.SignedTreeHead, error) {
	rctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	return cli.GetSTH(rctx)
}

func GetConsistencyProof(ctx context.Context, cli client.CheckLogClient, oldSize, newSize uint64) ([][sha256.Size]byte, error) {
	if oldSize > newSize {
		return nil, fmt.Errorf("old size %d is larger than new size %d", oldSize, newSize)
	}
	if oldSize == 0 || oldSize == newSize {
		return [][sha256.Size]byte{}, nil
	}

	rctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	proof, err := cli.GetSTHConsistency(rctx, oldSize, newSize)
	if err != nil {
		return nil, err
	}
	return ioutil.UnsliceHashes(proof), nil
}

func GetEntry(ctx context.Context, cli *client.LogClient, index uint64) (*ct.LeafEntry, error) {
	rctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	rsp, err := cli.GetRawEntries(rctx, int64(index), int64(index))
	if err != nil {
		return nil, err
	}
	if got, want := len(rsp.Entries), 1; got != want {
		return nil, fmt.Errorf("too many log entries: %d (want one)", got)
	}
	return &rsp.Entries[0], nil
}

// GetCompactRange constructs the compact range [0, index) by doing a
// get-proof-by-hash query to obtain the necessary tree hashes
func GetCompactRange(ctx context.Context, cli *client.LogClient, entry *ct.LeafEntry, index uint64) (*compact.Range, error) {
	rctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	leafHash := merkle.HashLeafNode(entry.LeafInput)
	proof, err := cli.GetProofByHash(rctx, leafHash[:], index+1)
	if err != nil {
		return nil, err
	}
	if got, want := uint64(proof.LeafIndex), index; got != want {
		return nil, fmt.Errorf("invalid leaf index: %d (want %d)", got, want)
	}
	tree := ioutil.UnsliceHashes(reverse(proof.AuditPath))
	return AppendCompactRange(tree, index, [][sha256.Size]byte{leafHash})
}

// RootHash panics if the compact range is malformed
func RootHash(cr *compact.Range) (ret [sha256.Size]byte) {
	rootHash, err := cr.GetRootHash(nil)
	if err != nil {
		panic(fmt.Sprintf("bug: compact: %v", err))
	}
	if got, want := len(rootHash), sha256.Size; got != want {
		panic(fmt.Sprintf("bug: invalid root hash: size %d", got))
	}
	copy(ret[:], rootHash[:])
	return
}

// AppendCompactRange appends a list of leaf hashes to the compact range [0, size)
func AppendCompactRange(tree [][sha256.Size]byte, size uint64, pending [][sha256.Size]byte) (*compact.Range, error) {
	rf := compact.RangeFactory{Hash: rfc6962.DefaultHasher.HashChildren}
	cr, err := rf.NewRange(0, size, ioutil.SliceHashes(tree))
	if err != nil {
		return nil, fmt.Errorf("compact: %v", err)
	}
	for _, leafHash := range pending {
		dst := leafHash
		cr.Append(dst[:], nil)
	}
	return cr, nil
}

func reverse(hashes [][]byte) (ret [][]byte) {
	for i := len(hashes) - 1; i >= 0; i-- {
		ret = append(ret, hashes[i])
	}
	return
}