1 package ca
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8 "sync"
9 "time"
10
11 "github.com/jmhodges/clock"
12 "github.com/miekg/pkcs11"
13 "github.com/prometheus/client_golang/prometheus"
14 "golang.org/x/crypto/ocsp"
15
16 capb "github.com/letsencrypt/boulder/ca/proto"
17 "github.com/letsencrypt/boulder/core"
18 berrors "github.com/letsencrypt/boulder/errors"
19 "github.com/letsencrypt/boulder/issuance"
20 blog "github.com/letsencrypt/boulder/log"
21 )
22
23
24 type ocspIssuerMaps struct {
25 byID map[issuance.IssuerID]*issuance.Issuer
26 byNameID map[issuance.IssuerNameID]*issuance.Issuer
27 }
28
29
30 type ocspImpl struct {
31 capb.UnimplementedOCSPGeneratorServer
32 issuers ocspIssuerMaps
33 ocspLifetime time.Duration
34 ocspLogQueue *ocspLogQueue
35 log blog.Logger
36 signatureCount *prometheus.CounterVec
37 signErrorCount *prometheus.CounterVec
38 clk clock.Clock
39 }
40
41
42
43
44
45 func makeOCSPIssuerMaps(issuers []*issuance.Issuer) ocspIssuerMaps {
46 issuersByID := make(map[issuance.IssuerID]*issuance.Issuer, len(issuers))
47 issuersByNameID := make(map[issuance.IssuerNameID]*issuance.Issuer, len(issuers))
48 for _, issuer := range issuers {
49 issuersByID[issuer.ID()] = issuer
50 issuersByNameID[issuer.Cert.NameID()] = issuer
51 }
52 return ocspIssuerMaps{issuersByID, issuersByNameID}
53 }
54
55 func NewOCSPImpl(
56 issuers []*issuance.Issuer,
57 ocspLifetime time.Duration,
58 ocspLogMaxLength int,
59 ocspLogPeriod time.Duration,
60 logger blog.Logger,
61 stats prometheus.Registerer,
62 signatureCount *prometheus.CounterVec,
63 signErrorCount *prometheus.CounterVec,
64 clk clock.Clock,
65 ) (*ocspImpl, error) {
66 issuersByID := make(map[issuance.IssuerID]*issuance.Issuer, len(issuers))
67 for _, issuer := range issuers {
68 issuersByID[issuer.ID()] = issuer
69 }
70
71 var ocspLogQueue *ocspLogQueue
72 if ocspLogMaxLength > 0 {
73 ocspLogQueue = newOCSPLogQueue(ocspLogMaxLength, ocspLogPeriod, stats, logger)
74 }
75
76 issuerMaps := makeOCSPIssuerMaps(issuers)
77
78 oi := &ocspImpl{
79 issuers: issuerMaps,
80 ocspLifetime: ocspLifetime,
81 ocspLogQueue: ocspLogQueue,
82 log: logger,
83 signatureCount: signatureCount,
84 signErrorCount: signErrorCount,
85 clk: clk,
86 }
87 return oi, nil
88 }
89
90
91
92 func (oi *ocspImpl) LogOCSPLoop() {
93 if oi.ocspLogQueue != nil {
94 oi.ocspLogQueue.loop()
95 }
96 }
97
98
99
100
101
102 func (oi *ocspImpl) Stop() {
103 if oi.ocspLogQueue != nil {
104 oi.ocspLogQueue.stop()
105 }
106 }
107
108
109 func (oi *ocspImpl) GenerateOCSP(ctx context.Context, req *capb.GenerateOCSPRequest) (*capb.OCSPResponse, error) {
110
111 if core.IsAnyNilOrZero(req, req.Serial, req.IssuerID) {
112 return nil, berrors.InternalServerError("Incomplete generate OCSP request")
113 }
114
115 serialInt, err := core.StringToSerial(req.Serial)
116 if err != nil {
117 return nil, err
118 }
119 serial := serialInt
120
121 issuer, ok := oi.issuers.byNameID[issuance.IssuerNameID(req.IssuerID)]
122 if !ok {
123
124 issuer, ok = oi.issuers.byID[issuance.IssuerID(req.IssuerID)]
125 if !ok {
126 return nil, fmt.Errorf("This CA doesn't have an issuer cert with ID %d", req.IssuerID)
127 }
128 }
129
130 now := oi.clk.Now().Truncate(time.Hour)
131 tbsResponse := ocsp.Response{
132 Status: ocspStatusToCode[req.Status],
133 SerialNumber: serial,
134 ThisUpdate: now,
135 NextUpdate: now.Add(oi.ocspLifetime - time.Second),
136 }
137 if tbsResponse.Status == ocsp.Revoked {
138 tbsResponse.RevokedAt = time.Unix(0, req.RevokedAtNS)
139 tbsResponse.RevocationReason = int(req.Reason)
140 }
141
142 if oi.ocspLogQueue != nil {
143 oi.ocspLogQueue.enqueue(serial.Bytes(), now, tbsResponse.Status, tbsResponse.RevocationReason)
144 }
145
146 ocspResponse, err := ocsp.CreateResponse(issuer.Cert.Certificate, issuer.Cert.Certificate, tbsResponse, issuer.Signer)
147 if err == nil {
148 oi.signatureCount.With(prometheus.Labels{"purpose": "ocsp", "issuer": issuer.Name()}).Inc()
149 } else {
150 var pkcs11Error *pkcs11.Error
151 if errors.As(err, &pkcs11Error) {
152 oi.signErrorCount.WithLabelValues("HSM").Inc()
153 }
154 }
155 return &capb.OCSPResponse{Response: ocspResponse}, err
156 }
157
158
159
160
161
162
163
164
165
166
167
168 type ocspLogQueue struct {
169
170 maxLogLen int
171
172 period time.Duration
173 queue chan ocspLog
174
175 wg sync.WaitGroup
176 depth prometheus.Gauge
177 logger blog.Logger
178 clk clock.Clock
179 }
180
181 type ocspLog struct {
182 serial []byte
183 time time.Time
184 status int
185 reason int
186 }
187
188 func newOCSPLogQueue(
189 maxLogLen int,
190 period time.Duration,
191 stats prometheus.Registerer,
192 logger blog.Logger,
193 ) *ocspLogQueue {
194 depth := prometheus.NewGauge(
195 prometheus.GaugeOpts{
196 Name: "ocsp_log_queue_depth",
197 Help: "Number of OCSP generation log entries waiting to be written",
198 })
199 stats.MustRegister(depth)
200 olq := ocspLogQueue{
201 maxLogLen: maxLogLen,
202 period: period,
203 queue: make(chan ocspLog),
204 wg: sync.WaitGroup{},
205 depth: depth,
206 logger: logger,
207 clk: clock.New(),
208 }
209 olq.wg.Add(1)
210 return &olq
211 }
212
213 func (olq *ocspLogQueue) enqueue(serial []byte, time time.Time, status, reason int) {
214 olq.queue <- ocspLog{
215 serial: append([]byte{}, serial...),
216 time: time,
217 status: status,
218 reason: reason,
219 }
220 }
221
222
223
224 const ocspSingleLogEntryLen = 39
225
226
227
228
229 func (olq *ocspLogQueue) loop() {
230 defer olq.wg.Done()
231 done := false
232 for !done {
233 var builder strings.Builder
234 deadline := olq.clk.After(olq.period)
235 inner:
236 for {
237 olq.depth.Set(float64(len(olq.queue)))
238 select {
239 case ol, ok := <-olq.queue:
240 if !ok {
241
242 done = true
243 break inner
244 }
245 reasonStr := "_"
246 if ol.status == ocsp.Revoked {
247 reasonStr = fmt.Sprintf("%d", ol.reason)
248 }
249 fmt.Fprintf(&builder, "%x:%s,", ol.serial, reasonStr)
250 case <-deadline:
251 break inner
252 }
253 if builder.Len()+ocspSingleLogEntryLen >= olq.maxLogLen {
254 break
255 }
256 }
257 if builder.Len() > 0 {
258 olq.logger.AuditInfof("OCSP signed: %s", builder.String())
259 }
260 }
261 }
262
263
264
265
266
267
268
269 func (olq *ocspLogQueue) stop() {
270 close(olq.queue)
271 olq.wg.Wait()
272 }
273
274
275
276
277 type OCSPGenerator interface {
278 capb.OCSPGeneratorServer
279 LogOCSPLoop()
280 Stop()
281 }
282
View as plain text