1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ctutil
16
17 import (
18 "context"
19 "crypto/sha256"
20 "fmt"
21 "net/http"
22 "strings"
23 "sync"
24 "time"
25
26 ct "github.com/google/certificate-transparency-go"
27 "github.com/google/certificate-transparency-go/client"
28 "github.com/google/certificate-transparency-go/jsonclient"
29 "github.com/google/certificate-transparency-go/loglist3"
30 "github.com/google/certificate-transparency-go/x509"
31 "github.com/transparency-dev/merkle/proof"
32 "github.com/transparency-dev/merkle/rfc6962"
33 )
34
35
36
37 type LogInfo struct {
38 Description string
39 Client client.CheckLogClient
40 MMD time.Duration
41 Verifier *ct.SignatureVerifier
42 PublicKey []byte
43
44 mu sync.RWMutex
45 lastSTH *ct.SignedTreeHead
46 }
47
48
49 func NewLogInfo(log *loglist3.Log, hc *http.Client) (*LogInfo, error) {
50 url := log.URL
51 if !strings.HasPrefix(url, "https://") {
52 url = "https://" + url
53 }
54 lc, err := client.New(url, hc, jsonclient.Options{PublicKeyDER: log.Key, UserAgent: "ct-go-logclient"})
55 if err != nil {
56 return nil, fmt.Errorf("failed to create client for log %q: %v", log.Description, err)
57 }
58 return newLogInfo(log, lc)
59 }
60
61 func newLogInfo(log *loglist3.Log, lc client.CheckLogClient) (*LogInfo, error) {
62 logKey, err := x509.ParsePKIXPublicKey(log.Key)
63 if err != nil {
64 return nil, fmt.Errorf("failed to parse public key data for log %q: %v", log.Description, err)
65 }
66 verifier, err := ct.NewSignatureVerifier(logKey)
67 if err != nil {
68 return nil, fmt.Errorf("failed to build verifier log %q: %v", log.Description, err)
69 }
70 mmd := time.Duration(log.MMD) * time.Second
71 return &LogInfo{
72 Description: log.Description,
73 Client: lc,
74 MMD: mmd,
75 Verifier: verifier,
76 PublicKey: log.Key,
77 }, nil
78 }
79
80
81 type LogInfoByHash map[[sha256.Size]byte]*LogInfo
82
83
84 func LogInfoByKeyHash(ll *loglist3.LogList, hc *http.Client) (LogInfoByHash, error) {
85 return logInfoByKeyHash(ll, hc, NewLogInfo)
86 }
87
88 func logInfoByKeyHash(ll *loglist3.LogList, hc *http.Client, infoFactory func(*loglist3.Log, *http.Client) (*LogInfo, error)) (map[[sha256.Size]byte]*LogInfo, error) {
89 result := make(map[[sha256.Size]byte]*LogInfo)
90 for _, operator := range ll.Operators {
91 for _, log := range operator.Logs {
92 h := sha256.Sum256(log.Key)
93 li, err := infoFactory(log, hc)
94 if err != nil {
95 return nil, err
96 }
97 result[h] = li
98 }
99 }
100 return result, nil
101 }
102
103
104 func (li *LogInfo) LastSTH() *ct.SignedTreeHead {
105 li.mu.RLock()
106 defer li.mu.RUnlock()
107 return li.lastSTH
108 }
109
110
111 func (li *LogInfo) SetSTH(sth *ct.SignedTreeHead) {
112 li.mu.Lock()
113 defer li.mu.Unlock()
114 li.lastSTH = sth
115 }
116
117
118
119 func (li *LogInfo) VerifySCTSignature(sct ct.SignedCertificateTimestamp, leaf ct.MerkleTreeLeaf) error {
120 leaf.TimestampedEntry.Timestamp = sct.Timestamp
121 if err := li.Verifier.VerifySCTSignature(sct, ct.LogEntry{Leaf: leaf}); err != nil {
122 return fmt.Errorf("failed to verify SCT signature from log %q: %v", li.Description, err)
123 }
124 return nil
125 }
126
127
128
129
130 func (li *LogInfo) VerifyInclusionLatest(ctx context.Context, leaf ct.MerkleTreeLeaf, timestamp uint64) (int64, error) {
131 sth := li.LastSTH()
132 if sth == nil {
133 var err error
134 sth, err = li.Client.GetSTH(ctx)
135 if err != nil {
136 return -1, fmt.Errorf("failed to get current STH for %q log: %v", li.Description, err)
137 }
138 li.SetSTH(sth)
139 }
140 return li.VerifyInclusionAt(ctx, leaf, timestamp, sth.TreeSize, sth.SHA256RootHash[:])
141 }
142
143
144
145
146 func (li *LogInfo) VerifyInclusion(ctx context.Context, leaf ct.MerkleTreeLeaf, timestamp uint64) (int64, error) {
147 sth, err := li.Client.GetSTH(ctx)
148 if err != nil {
149 return -1, fmt.Errorf("failed to get current STH for %q log: %v", li.Description, err)
150 }
151 li.SetSTH(sth)
152 return li.VerifyInclusionAt(ctx, leaf, timestamp, sth.TreeSize, sth.SHA256RootHash[:])
153 }
154
155
156
157
158 func (li *LogInfo) VerifyInclusionAt(ctx context.Context, leaf ct.MerkleTreeLeaf, timestamp, treeSize uint64, rootHash []byte) (int64, error) {
159 leaf.TimestampedEntry.Timestamp = timestamp
160 leafHash, err := ct.LeafHashForLeaf(&leaf)
161 if err != nil {
162 return -1, fmt.Errorf("failed to create leaf hash: %v", err)
163 }
164
165 rsp, err := li.Client.GetProofByHash(ctx, leafHash[:], treeSize)
166 if err != nil {
167 return -1, fmt.Errorf("failed to GetProofByHash(sct,size=%d): %v", treeSize, err)
168 }
169
170 if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(rsp.LeafIndex), treeSize, leafHash[:], rsp.AuditPath, rootHash); err != nil {
171 return -1, fmt.Errorf("failed to verify inclusion proof at size %d: %v", treeSize, err)
172 }
173 return rsp.LeafIndex, nil
174 }
175
View as plain text