1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package client
19
20 import (
21 "context"
22 "encoding/base64"
23 "fmt"
24 "net/http"
25 "strconv"
26
27 ct "github.com/google/certificate-transparency-go"
28 "github.com/google/certificate-transparency-go/jsonclient"
29 "github.com/google/certificate-transparency-go/tls"
30 )
31
32
33 type LogClient struct {
34 jsonclient.JSONClient
35 }
36
37
38 type CheckLogClient interface {
39 BaseURI() string
40 GetSTH(context.Context) (*ct.SignedTreeHead, error)
41 GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error)
42 GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*ct.GetProofByHashResponse, error)
43 }
44
45
46
47
48
49
50
51 func New(uri string, hc *http.Client, opts jsonclient.Options) (*LogClient, error) {
52 logClient, err := jsonclient.New(uri, hc, opts)
53 if err != nil {
54 return nil, err
55 }
56 return &LogClient{*logClient}, err
57 }
58
59
60 type RspError = jsonclient.RspError
61
62
63
64
65 func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
66 var resp ct.AddChainResponse
67 var req ct.AddChainRequest
68 for _, link := range chain {
69 req.Chain = append(req.Chain, link.Data)
70 }
71
72 httpRsp, body, err := c.PostAndParseWithRetry(ctx, path, &req, &resp)
73 if err != nil {
74 return nil, err
75 }
76
77 var ds ct.DigitallySigned
78 if rest, err := tls.Unmarshal(resp.Signature, &ds); err != nil {
79 return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
80 } else if len(rest) > 0 {
81 return nil, RspError{
82 Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)),
83 StatusCode: httpRsp.StatusCode,
84 Body: body,
85 }
86 }
87
88 exts, err := base64.StdEncoding.DecodeString(resp.Extensions)
89 if err != nil {
90 return nil, RspError{
91 Err: fmt.Errorf("invalid base64 data in Extensions (%q): %v", resp.Extensions, err),
92 StatusCode: httpRsp.StatusCode,
93 Body: body,
94 }
95 }
96
97 var logID ct.LogID
98 copy(logID.KeyID[:], resp.ID)
99 sct := &ct.SignedCertificateTimestamp{
100 SCTVersion: resp.SCTVersion,
101 LogID: logID,
102 Timestamp: resp.Timestamp,
103 Extensions: ct.CTExtensions(exts),
104 Signature: ds,
105 }
106 if err := c.VerifySCTSignature(*sct, ctype, chain); err != nil {
107 return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
108 }
109 return sct, nil
110 }
111
112
113 func (c *LogClient) AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
114 return c.addChainWithRetry(ctx, ct.X509LogEntryType, ct.AddChainPath, chain)
115 }
116
117
118 func (c *LogClient) AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
119 return c.addChainWithRetry(ctx, ct.PrecertLogEntryType, ct.AddPreChainPath, chain)
120 }
121
122
123
124
125 func (c *LogClient) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) {
126 var resp ct.GetSTHResponse
127 httpRsp, body, err := c.GetAndParse(ctx, ct.GetSTHPath, nil, &resp)
128 if err != nil {
129 return nil, err
130 }
131
132 sth, err := resp.ToSignedTreeHead()
133 if err != nil {
134 return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
135 }
136
137 if err := c.VerifySTHSignature(*sth); err != nil {
138 return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
139 }
140 return sth, nil
141 }
142
143
144
145 func (c *LogClient) VerifySTHSignature(sth ct.SignedTreeHead) error {
146 if c.Verifier == nil {
147
148 return nil
149 }
150 return c.Verifier.VerifySTHSignature(sth)
151 }
152
153
154 func (c *LogClient) VerifySCTSignature(sct ct.SignedCertificateTimestamp, ctype ct.LogEntryType, certData []ct.ASN1Cert) error {
155 if c.Verifier == nil {
156
157 return nil
158 }
159 leaf, err := ct.MerkleTreeLeafFromRawChain(certData, ctype, sct.Timestamp)
160 if err != nil {
161 return fmt.Errorf("failed to build MerkleTreeLeaf: %v", err)
162 }
163 entry := ct.LogEntry{Leaf: *leaf}
164 return c.Verifier.VerifySCTSignature(sct, entry)
165 }
166
167
168 func (c *LogClient) GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) {
169 base10 := 10
170 params := map[string]string{
171 "first": strconv.FormatUint(first, base10),
172 "second": strconv.FormatUint(second, base10),
173 }
174 var resp ct.GetSTHConsistencyResponse
175 if _, _, err := c.GetAndParse(ctx, ct.GetSTHConsistencyPath, params, &resp); err != nil {
176 return nil, err
177 }
178 return resp.Consistency, nil
179 }
180
181
182 func (c *LogClient) GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*ct.GetProofByHashResponse, error) {
183 b64Hash := base64.StdEncoding.EncodeToString(hash)
184 base10 := 10
185 params := map[string]string{
186 "tree_size": strconv.FormatUint(treeSize, base10),
187 "hash": b64Hash,
188 }
189 var resp ct.GetProofByHashResponse
190 if _, _, err := c.GetAndParse(ctx, ct.GetProofByHashPath, params, &resp); err != nil {
191 return nil, err
192 }
193 return &resp, nil
194 }
195
196
197 func (c *LogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) {
198 var resp ct.GetRootsResponse
199 httpRsp, body, err := c.GetAndParse(ctx, ct.GetRootsPath, nil, &resp)
200 if err != nil {
201 return nil, err
202 }
203 var roots []ct.ASN1Cert
204 for _, cert64 := range resp.Certificates {
205 cert, err := base64.StdEncoding.DecodeString(cert64)
206 if err != nil {
207 return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
208 }
209 roots = append(roots, ct.ASN1Cert{Data: cert})
210 }
211 return roots, nil
212 }
213
214
215 func (c *LogClient) GetEntryAndProof(ctx context.Context, index, treeSize uint64) (*ct.GetEntryAndProofResponse, error) {
216 base10 := 10
217 params := map[string]string{
218 "leaf_index": strconv.FormatUint(index, base10),
219 "tree_size": strconv.FormatUint(treeSize, base10),
220 }
221 var resp ct.GetEntryAndProofResponse
222 if _, _, err := c.GetAndParse(ctx, ct.GetEntryAndProofPath, params, &resp); err != nil {
223 return nil, err
224 }
225 return &resp, nil
226 }
227
View as plain text