1 package main
2
3 import (
4 "bytes"
5 "crypto"
6 "crypto/ecdsa"
7 "crypto/elliptic"
8 "crypto/rand"
9 "crypto/sha1"
10 "crypto/sha256"
11 "crypto/x509"
12 "encoding/base64"
13 "encoding/binary"
14 "encoding/json"
15 "encoding/pem"
16 "errors"
17 "fmt"
18 "io"
19 mrand "math/rand"
20 "net/http"
21 "time"
22
23 "github.com/letsencrypt/boulder/core"
24 "github.com/letsencrypt/boulder/identifier"
25 "github.com/letsencrypt/boulder/probs"
26 "github.com/letsencrypt/boulder/test/load-generator/acme"
27 "golang.org/x/crypto/ocsp"
28 "gopkg.in/go-jose/go-jose.v2"
29 )
30
31 var (
32
33
34 stringToOperation = map[string]func(*State, *acmeCache) error{
35 "newAccount": newAccount,
36 "getAccount": getAccount,
37 "newOrder": newOrder,
38 "fulfillOrder": fulfillOrder,
39 "finalizeOrder": finalizeOrder,
40 "revokeCertificate": revokeCertificate,
41 }
42 )
43
44
45
46
47
48
49 type OrderJSON struct {
50
51
52 URL string
53 Status core.AcmeStatus `json:"status"`
54 Expires time.Time `json:"expires"`
55 Identifiers []identifier.ACMEIdentifier `json:"identifiers"`
56 Authorizations []string `json:"authorizations"`
57 Finalize string `json:"finalize"`
58 Certificate string `json:"certificate,omitempty"`
59 Error *probs.ProblemDetails `json:"error,omitempty"`
60 }
61
62
63
64 func getAccount(s *State, c *acmeCache) error {
65 s.rMu.RLock()
66 defer s.rMu.RUnlock()
67
68
69 if len(s.accts) == 0 {
70 return errors.New("no accounts to return")
71 }
72
73
74 c.acct = s.accts[mrand.Intn(len(s.accts))]
75 c.ns = &nonceSource{s: s}
76 return nil
77 }
78
79
80
81
82
83
84 func newAccount(s *State, c *acmeCache) error {
85
86
87 if s.maxRegs != 0 && s.numAccts() >= s.maxRegs {
88 return getAccount(s, c)
89 }
90
91
92 signKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
93 if err != nil {
94 return err
95 }
96 c.acct = &account{
97 key: signKey,
98 }
99 c.ns = &nonceSource{s: s}
100
101
102 reqBody := struct {
103 ToSAgreed bool `json:"termsOfServiceAgreed"`
104 Contact []string
105 }{
106 ToSAgreed: true,
107 }
108
109 if s.email != "" {
110 reqBody.Contact = []string{fmt.Sprintf("mailto:%s", s.email)}
111 }
112 reqBodyStr, err := json.Marshal(&reqBody)
113 if err != nil {
114 return err
115 }
116
117
118
119 newAccountURL := s.directory.EndpointURL(acme.NewAccountEndpoint)
120 jws, err := c.signEmbeddedV2Request(reqBodyStr, newAccountURL)
121 if err != nil {
122 return err
123 }
124 bodyBuf := []byte(jws.FullSerialize())
125
126 resp, err := s.post(
127 newAccountURL,
128 bodyBuf,
129 c.ns,
130 string(acme.NewAccountEndpoint),
131 http.StatusCreated)
132 if err != nil {
133 return fmt.Errorf("%s, post failed: %s", newAccountURL, err)
134 }
135 defer resp.Body.Close()
136
137
138
139 locHeader := resp.Header.Get("Location")
140 if locHeader == "" {
141 return fmt.Errorf("%s, bad response - no Location header with account ID", newAccountURL)
142 }
143 c.acct.id = locHeader
144
145
146 s.addAccount(c.acct)
147 return nil
148 }
149
150
151
152 func randDomain(base string) string {
153
154
155 n := time.Now().UnixNano()
156 b := new(bytes.Buffer)
157 binary.Write(b, binary.LittleEndian, n)
158 return fmt.Sprintf("%x.%s", sha1.Sum(b.Bytes()), base)
159 }
160
161
162
163 func newOrder(s *State, c *acmeCache) error {
164
165
166 orderSize := 1 + mrand.Intn(s.maxNamesPerCert-1)
167
168
169 dnsNames := []identifier.ACMEIdentifier{}
170 for i := 0; i <= orderSize; i++ {
171 dnsNames = append(dnsNames, identifier.ACMEIdentifier{
172 Type: identifier.DNS,
173 Value: randDomain(s.domainBase),
174 })
175 }
176
177
178 initOrder := struct {
179 Identifiers []identifier.ACMEIdentifier
180 }{
181 Identifiers: dnsNames,
182 }
183 initOrderStr, err := json.Marshal(&initOrder)
184 if err != nil {
185 return err
186 }
187
188
189 newOrderURL := s.directory.EndpointURL(acme.NewOrderEndpoint)
190 jws, err := c.signKeyIDV2Request(initOrderStr, newOrderURL)
191 if err != nil {
192 return err
193 }
194 bodyBuf := []byte(jws.FullSerialize())
195
196 resp, err := s.post(
197 newOrderURL,
198 bodyBuf,
199 c.ns,
200 string(acme.NewOrderEndpoint),
201 http.StatusCreated)
202 if err != nil {
203 return fmt.Errorf("%s, post failed: %s", newOrderURL, err)
204 }
205 defer resp.Body.Close()
206 body, err := io.ReadAll(resp.Body)
207 if err != nil {
208 return fmt.Errorf("%s, bad response: %s", newOrderURL, body)
209 }
210
211
212 var orderJSON OrderJSON
213 err = json.Unmarshal(body, &orderJSON)
214 if err != nil {
215 return err
216 }
217
218
219 orderURL := resp.Header.Get("Location")
220 if orderURL == "" {
221 return fmt.Errorf("%s, bad response - no Location header with order ID", newOrderURL)
222 }
223 orderJSON.URL = orderURL
224
225
226 c.pendingOrders = append(c.pendingOrders, &orderJSON)
227 return nil
228 }
229
230
231
232 func popPendingOrder(c *acmeCache) *OrderJSON {
233 orderIndex := mrand.Intn(len(c.pendingOrders))
234 order := c.pendingOrders[orderIndex]
235 c.pendingOrders = append(c.pendingOrders[:orderIndex], c.pendingOrders[orderIndex+1:]...)
236 return order
237 }
238
239
240
241 func getAuthorization(s *State, c *acmeCache, url string) (*core.Authorization, error) {
242 latencyTag := "/acme/authz/{ID}"
243 resp, err := postAsGet(s, c, url, latencyTag)
244
245 if err != nil {
246 return nil, fmt.Errorf("%s bad response: %s", url, err)
247 }
248
249
250 defer resp.Body.Close()
251 body, err := io.ReadAll(resp.Body)
252 if err != nil {
253 return nil, err
254 }
255
256
257 var authz core.Authorization
258 err = json.Unmarshal(body, &authz)
259 if err != nil {
260 return nil, fmt.Errorf("%s response: %s", url, body)
261 }
262
263
264 authz.ID = url
265 return &authz, nil
266 }
267
268
269
270
271
272 func completeAuthorization(authz *core.Authorization, s *State, c *acmeCache) error {
273
274 if authz.Status != core.StatusPending {
275 return nil
276 }
277
278
279
280 chalToSolve, err := s.challStrat.PickChallenge(authz)
281 if err != nil {
282 return err
283 }
284
285
286 jwk := &jose.JSONWebKey{Key: &c.acct.key.PublicKey}
287 thumbprint, err := jwk.Thumbprint(crypto.SHA256)
288 if err != nil {
289 return err
290 }
291 authStr := fmt.Sprintf("%s.%s", chalToSolve.Token, base64.RawURLEncoding.EncodeToString(thumbprint))
292
293
294 switch chalToSolve.Type {
295 case core.ChallengeTypeHTTP01:
296 s.challSrv.AddHTTPOneChallenge(chalToSolve.Token, authStr)
297 defer s.challSrv.DeleteHTTPOneChallenge(chalToSolve.Token)
298 case core.ChallengeTypeDNS01:
299
300 h := sha256.New()
301 h.Write([]byte(authStr))
302 authorizedKeysDigest := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
303 domain := "_acme-challenge." + authz.Identifier.Value + "."
304 s.challSrv.AddDNSOneChallenge(domain, authorizedKeysDigest)
305 defer s.challSrv.DeleteDNSOneChallenge(domain)
306 case core.ChallengeTypeTLSALPN01:
307 s.challSrv.AddTLSALPNChallenge(authz.Identifier.Value, authStr)
308 defer s.challSrv.DeleteTLSALPNChallenge(authz.Identifier.Value)
309 default:
310 return fmt.Errorf("challenge strategy picked challenge with unknown type: %q", chalToSolve.Type)
311 }
312
313
314 jws, err := c.signKeyIDV2Request([]byte(`{}`), chalToSolve.URL)
315 if err != nil {
316 return err
317 }
318 requestPayload := []byte(jws.FullSerialize())
319
320 resp, err := s.post(
321 chalToSolve.URL,
322 requestPayload,
323 c.ns,
324 "/acme/challenge/{ID}",
325 http.StatusOK,
326 )
327 if err != nil {
328 return err
329 }
330
331
332 defer resp.Body.Close()
333 _, err = io.ReadAll(resp.Body)
334 if err != nil {
335 return err
336 }
337
338
339
340 err = pollAuthorization(authz, s, c)
341 if err != nil {
342 return err
343 }
344
345
346 return nil
347 }
348
349
350
351
352
353
354 func pollAuthorization(authz *core.Authorization, s *State, c *acmeCache) error {
355 authzURL := authz.ID
356 for i := 0; i < 3; i++ {
357
358 authz, err := getAuthorization(s, c, authzURL)
359 if err != nil {
360 return nil
361 }
362
363 if authz.Status == "invalid" {
364 return fmt.Errorf("Authorization %q failed challenge and is status invalid", authzURL)
365 }
366
367 if authz.Status == "valid" {
368 return nil
369 }
370
371 time.Sleep(3 * time.Second)
372 }
373 return fmt.Errorf("Timed out polling authorization %q", authzURL)
374 }
375
376
377
378
379
380 func fulfillOrder(s *State, c *acmeCache) error {
381
382 if len(c.pendingOrders) == 0 {
383 return errors.New("no pending orders to fulfill")
384 }
385
386
387 order := popPendingOrder(c)
388
389
390 for _, url := range order.Authorizations {
391
392 authz, err := getAuthorization(s, c, url)
393 if err != nil {
394 return err
395 }
396
397
398 err = completeAuthorization(authz, s, c)
399 if err != nil {
400 return err
401 }
402 }
403
404
405
406 c.fulfilledOrders = append(c.fulfilledOrders, order.URL)
407 return nil
408 }
409
410
411
412 func getOrder(s *State, c *acmeCache, url string) (*OrderJSON, error) {
413 latencyTag := "/acme/order/{ID}"
414
415 resp, err := postAsGet(s, c, url, latencyTag)
416
417 if err != nil {
418 return nil, fmt.Errorf("%s bad response: %s", url, err)
419 }
420
421 defer resp.Body.Close()
422 body, err := io.ReadAll(resp.Body)
423 if err != nil {
424 return nil, fmt.Errorf("%s, bad response: %s", url, body)
425 }
426
427
428 var orderJSON OrderJSON
429 err = json.Unmarshal(body, &orderJSON)
430 if err != nil {
431 return nil, err
432 }
433
434
435 orderJSON.URL = url
436 return &orderJSON, nil
437 }
438
439
440
441
442
443 func pollOrderForCert(order *OrderJSON, s *State, c *acmeCache) (*OrderJSON, error) {
444 for i := 0; i < 3; i++ {
445
446 order, err := getOrder(s, c, order.URL)
447 if err != nil {
448 return nil, err
449 }
450
451 if order.Status == "invalid" {
452 return nil, fmt.Errorf("Order %q failed and is status invalid", order.URL)
453 }
454
455 if order.Status == "valid" {
456 return order, nil
457 }
458
459 time.Sleep(3 * time.Second)
460 }
461 return nil, fmt.Errorf("Timed out polling order %q", order.URL)
462 }
463
464
465
466 func popFulfilledOrder(c *acmeCache) string {
467 orderIndex := mrand.Intn(len(c.fulfilledOrders))
468 order := c.fulfilledOrders[orderIndex]
469 c.fulfilledOrders = append(c.fulfilledOrders[:orderIndex], c.fulfilledOrders[orderIndex+1:]...)
470 return order
471 }
472
473
474
475
476
477
478 func finalizeOrder(s *State, c *acmeCache) error {
479
480 if len(c.fulfilledOrders) < 1 {
481 return errors.New("No fulfilled orders in the context ready to be finalized")
482 }
483
484
485 orderID := popFulfilledOrder(c)
486 order, err := getOrder(s, c, orderID)
487 if err != nil {
488 return err
489 }
490
491 if order.Status != core.StatusReady {
492 return fmt.Errorf("order %s was status %q, expected %q",
493 orderID, order.Status, core.StatusReady)
494 }
495
496
497 finalizeURL := order.Finalize
498
499
500 dnsNames := make([]string, len(order.Identifiers))
501 for i, ident := range order.Identifiers {
502 dnsNames[i] = ident.Value
503 }
504
505
506 csr, err := x509.CreateCertificateRequest(
507 rand.Reader,
508 &x509.CertificateRequest{DNSNames: dnsNames},
509 s.certKey,
510 )
511 if err != nil {
512 return err
513 }
514
515
516 request := fmt.Sprintf(
517 `{"csr":"%s"}`,
518 base64.RawURLEncoding.EncodeToString(csr),
519 )
520
521
522 jws, err := c.signKeyIDV2Request([]byte(request), finalizeURL)
523 if err != nil {
524 return err
525 }
526 requestPayload := []byte(jws.FullSerialize())
527
528 resp, err := s.post(
529 finalizeURL,
530 requestPayload,
531 c.ns,
532 "/acme/order/finalize",
533 http.StatusOK,
534 )
535 if err != nil {
536 return err
537 }
538 defer resp.Body.Close()
539
540
541 _, err = io.ReadAll(resp.Body)
542 if err != nil {
543 return err
544 }
545
546
547 completedOrder, err := pollOrderForCert(order, s, c)
548 if err != nil {
549 return err
550 }
551
552
553 certURL := completedOrder.Certificate
554 if certURL == "" {
555 return fmt.Errorf("Order %q was finalized but has no cert URL", order.URL)
556 }
557
558
559 c.certs = append(c.certs, certURL)
560 c.finalizedOrders = append(c.finalizedOrders, order.URL)
561 return nil
562 }
563
564
565
566
567
568
569
570 func postAsGet(s *State, c *acmeCache, url string, latencyTag string) (*http.Response, error) {
571
572 jws, err := c.signKeyIDV2Request([]byte(""), url)
573 if err != nil {
574 return nil, err
575 }
576 requestPayload := []byte(jws.FullSerialize())
577
578 return s.post(url, requestPayload, c.ns, latencyTag, http.StatusOK)
579 }
580
581 func popCertificate(c *acmeCache) string {
582 certIndex := mrand.Intn(len(c.certs))
583 certURL := c.certs[certIndex]
584 c.certs = append(c.certs[:certIndex], c.certs[certIndex+1:]...)
585 return certURL
586 }
587
588 func getCert(s *State, c *acmeCache, url string) ([]byte, error) {
589 latencyTag := "/acme/cert/{serial}"
590 resp, err := postAsGet(s, c, url, latencyTag)
591 if err != nil {
592 return nil, fmt.Errorf("%s bad response: %s", url, err)
593 }
594 defer resp.Body.Close()
595 return io.ReadAll(resp.Body)
596 }
597
598
599
600
601
602 func revokeCertificate(s *State, c *acmeCache) error {
603 if len(c.certs) < 1 {
604 return errors.New("No certificates in the context that can be revoked")
605 }
606
607 if r := mrand.Float32(); r > s.revokeChance {
608 return nil
609 }
610
611 certURL := popCertificate(c)
612 certPEM, err := getCert(s, c, certURL)
613 if err != nil {
614 return err
615 }
616
617 pemBlock, _ := pem.Decode(certPEM)
618 revokeObj := struct {
619 Certificate string
620 Reason int
621 }{
622 Certificate: base64.URLEncoding.EncodeToString(pemBlock.Bytes),
623 Reason: ocsp.Unspecified,
624 }
625
626 revokeJSON, err := json.Marshal(revokeObj)
627 if err != nil {
628 return err
629 }
630 revokeURL := s.directory.EndpointURL(acme.RevokeCertEndpoint)
631
632
633 jws, err := c.signKeyIDV2Request(revokeJSON, revokeURL)
634 if err != nil {
635 return err
636 }
637 requestPayload := []byte(jws.FullSerialize())
638
639 resp, err := s.post(
640 revokeURL,
641 requestPayload,
642 c.ns,
643 "/acme/revoke-cert",
644 http.StatusOK,
645 )
646 if err != nil {
647 return err
648 }
649 defer resp.Body.Close()
650
651 _, err = io.ReadAll(resp.Body)
652 if err != nil {
653 return err
654 }
655
656 return nil
657 }
658
View as plain text