1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package core
16
17 import (
18 "context"
19 "crypto/sha256"
20 "encoding/binary"
21 "errors"
22 "fmt"
23 "time"
24
25 ct "github.com/google/certificate-transparency-go"
26 "github.com/google/certificate-transparency-go/scanner"
27 "github.com/google/certificate-transparency-go/trillian/migrillian/configpb"
28 "github.com/google/certificate-transparency-go/x509"
29 "github.com/google/trillian"
30 "github.com/google/trillian/client/backoff"
31 "github.com/google/trillian/types"
32 "google.golang.org/grpc/codes"
33 "google.golang.org/grpc/status"
34 "k8s.io/klog/v2"
35 )
36
37 var errRetry = errors.New("retry")
38
39
40
41 type PreorderedLogClient struct {
42 cli trillian.TrillianLogClient
43 treeID int64
44 idFunc func(int64, *ct.RawLogEntry) []byte
45 prefix string
46 }
47
48
49 func NewPreorderedLogClient(
50 cli trillian.TrillianLogClient,
51 tree *trillian.Tree,
52 idFuncType configpb.IdentityFunction,
53 prefix string,
54 ) (*PreorderedLogClient, error) {
55 if tree == nil {
56 return nil, errors.New("missing Tree")
57 }
58 if got, want := tree.TreeType, trillian.TreeType_PREORDERED_LOG; got != want {
59 return nil, fmt.Errorf("tree %d is %v, want %v", tree.TreeId, got, want)
60 }
61 ret := PreorderedLogClient{cli: cli, treeID: tree.TreeId, prefix: prefix}
62
63 switch idFuncType {
64 case configpb.IdentityFunction_SHA256_CERT_DATA:
65 ret.idFunc = idHashCertData
66 case configpb.IdentityFunction_SHA256_LEAF_INDEX:
67 ret.idFunc = idHashLeafIndex
68 default:
69 return nil, fmt.Errorf("unknown identity function: %v", idFuncType)
70 }
71
72 return &ret, nil
73 }
74
75
76 func (c *PreorderedLogClient) getRoot(ctx context.Context) (uint64, []byte, error) {
77 req := trillian.GetLatestSignedLogRootRequest{LogId: c.treeID}
78 rsp, err := c.cli.GetLatestSignedLogRoot(ctx, &req)
79 if err != nil {
80 return 0, nil, err
81 } else if rsp == nil || rsp.SignedLogRoot == nil {
82 return 0, nil, errors.New("missing SignedLogRoot")
83 }
84 var logRoot types.LogRootV1
85 if err := logRoot.UnmarshalBinary(rsp.SignedLogRoot.LogRoot); err != nil {
86 return 0, nil, err
87 }
88 return logRoot.TreeSize, logRoot.RootHash, nil
89 }
90
91
92
93
94
95
96
97
98 func (c *PreorderedLogClient) addSequencedLeaves(ctx context.Context, b *scanner.EntryBatch) error {
99
100 leaves := make([]*trillian.LogLeaf, len(b.Entries))
101 for i, e := range b.Entries {
102 var err error
103 if leaves[i], err = c.buildLogLeaf(b.Start+int64(i), &e); err != nil {
104 return err
105 }
106 }
107 req := trillian.AddSequencedLeavesRequest{LogId: c.treeID, Leaves: leaves}
108
109
110 bo := backoff.Backoff{
111 Min: 1 * time.Second,
112 Max: 1 * time.Minute,
113 Factor: 3,
114 Jitter: true,
115 }
116
117 var err error
118 boerr := bo.Retry(ctx, func() error {
119 var rsp *trillian.AddSequencedLeavesResponse
120 rsp, err = c.cli.AddSequencedLeaves(ctx, &req)
121 switch status.Code(err) {
122 case codes.ResourceExhausted:
123 end := b.Start + int64(len(b.Entries))
124 klog.Errorf("%d: retrying batch [%d, %d) due to error: %v", c.treeID, b.Start, end, err)
125 return errRetry
126 case codes.OK:
127 if rsp == nil {
128 err = errors.New("missing AddSequencedLeaves response")
129 }
130
131 return nil
132 default:
133 return nil
134 }
135 })
136 if err != nil {
137
138 return err
139 }
140
141 return boerr
142 }
143
144 func (c *PreorderedLogClient) buildLogLeaf(index int64, entry *ct.LeafEntry) (*trillian.LogLeaf, error) {
145 rle, err := ct.RawLogEntryFromLeaf(index, entry)
146 if err != nil {
147 return nil, err
148 }
149
150
151
152 if _, err = rle.ToLogEntry(); x509.IsFatal(err) {
153 klog.Errorf("%s: index=%d: x509 fatal error: %v", c.prefix, index, err)
154 } else if err != nil {
155 klog.Infof("%s: index=%d: x509 non-fatal error: %v", c.prefix, index, err)
156 }
157
158
159 leafIDHash := c.idFunc(index, rle)
160 return &trillian.LogLeaf{
161 LeafValue: entry.LeafInput,
162 ExtraData: entry.ExtraData,
163 LeafIndex: index,
164 LeafIdentityHash: leafIDHash[:],
165 }, nil
166 }
167
168 func idHashCertData(_ int64, entry *ct.RawLogEntry) []byte {
169 hash := sha256.Sum256(entry.Cert.Data)
170 return hash[:]
171 }
172
173 func idHashLeafIndex(index int64, _ *ct.RawLogEntry) []byte {
174 data := make([]byte, 8)
175 binary.LittleEndian.PutUint64(data, uint64(index))
176 hash := sha256.Sum256(data)
177 return hash[:]
178 }
179
View as plain text