1 package wfe2
2
3 import (
4 "context"
5 "crypto/ecdsa"
6 "crypto/rsa"
7 "encoding/base64"
8 "encoding/json"
9 "errors"
10 "fmt"
11 "io"
12 "net/http"
13 "net/url"
14 "strconv"
15 "strings"
16
17 "github.com/prometheus/client_golang/prometheus"
18 "google.golang.org/grpc/status"
19 "gopkg.in/go-jose/go-jose.v2"
20
21 "github.com/letsencrypt/boulder/core"
22 berrors "github.com/letsencrypt/boulder/errors"
23 "github.com/letsencrypt/boulder/goodkey"
24 "github.com/letsencrypt/boulder/grpc"
25 nb "github.com/letsencrypt/boulder/grpc/noncebalancer"
26 "github.com/letsencrypt/boulder/nonce"
27 noncepb "github.com/letsencrypt/boulder/nonce/proto"
28 "github.com/letsencrypt/boulder/probs"
29 sapb "github.com/letsencrypt/boulder/sa/proto"
30 "github.com/letsencrypt/boulder/web"
31 )
32
33 const (
34
35 expectedJWSContentType = "application/jose+json"
36
37 maxRequestSize = 50000
38 )
39
40 func sigAlgorithmForKey(key *jose.JSONWebKey) (jose.SignatureAlgorithm, error) {
41 switch k := key.Key.(type) {
42 case *rsa.PublicKey:
43 return jose.RS256, nil
44 case *ecdsa.PublicKey:
45 switch k.Params().Name {
46 case "P-256":
47 return jose.ES256, nil
48 case "P-384":
49 return jose.ES384, nil
50 case "P-521":
51 return jose.ES512, nil
52 }
53 }
54 return "", errors.New("JWK contains unsupported key type (expected RSA, or ECDSA P-256, P-384, or P-521)")
55 }
56
57 var supportedAlgs = map[string]bool{
58 string(jose.RS256): true,
59 string(jose.ES256): true,
60 string(jose.ES384): true,
61 string(jose.ES512): true,
62 }
63
64
65
66
67
68 func checkAlgorithm(key *jose.JSONWebKey, header jose.Header) error {
69 sigHeaderAlg := header.Algorithm
70 if !supportedAlgs[sigHeaderAlg] {
71 return fmt.Errorf(
72 "JWS signature header contains unsupported algorithm %q, expected one of RS256, ES256, ES384 or ES512",
73 header.Algorithm,
74 )
75 }
76
77 expectedAlg, err := sigAlgorithmForKey(key)
78 if err != nil {
79 return err
80 }
81 if sigHeaderAlg != string(expectedAlg) {
82 return fmt.Errorf("JWS signature header algorithm %q does not match expected algorithm %q for JWK", sigHeaderAlg, string(expectedAlg))
83 }
84 if key.Algorithm != "" && key.Algorithm != string(expectedAlg) {
85 return fmt.Errorf("JWK key header algorithm %q does not match expected algorithm %q for JWK", key.Algorithm, string(expectedAlg))
86 }
87 return nil
88 }
89
90
91
92
93 type jwsAuthType int
94
95 const (
96 embeddedJWK jwsAuthType = iota
97 embeddedKeyID
98 invalidAuthType
99 )
100
101
102
103
104
105
106
107
108
109 func checkJWSAuthType(header jose.Header) (jwsAuthType, *probs.ProblemDetails) {
110
111 if header.KeyID != "" && header.JSONWebKey != nil {
112 return invalidAuthType, probs.Malformed(
113 "jwk and kid header fields are mutually exclusive")
114 } else if header.KeyID != "" {
115 return embeddedKeyID, nil
116 } else if header.JSONWebKey != nil {
117 return embeddedJWK, nil
118 }
119
120 return invalidAuthType, nil
121 }
122
123
124
125
126
127 func (wfe *WebFrontEndImpl) enforceJWSAuthType(
128 header jose.Header,
129 expectedAuthType jwsAuthType) *probs.ProblemDetails {
130
131 authType, prob := checkJWSAuthType(header)
132 if prob != nil {
133 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSAuthTypeInvalid"}).Inc()
134 return prob
135 }
136
137
138 if authType != expectedAuthType {
139 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSAuthTypeWrong"}).Inc()
140 switch expectedAuthType {
141 case embeddedKeyID:
142 return probs.Malformed("No Key ID in JWS header")
143 case embeddedJWK:
144 return probs.Malformed("No embedded JWK in JWS header")
145 }
146 }
147 return nil
148 }
149
150
151
152
153 func (wfe *WebFrontEndImpl) validPOSTRequest(request *http.Request) *probs.ProblemDetails {
154
155 if _, present := request.Header["Content-Length"]; !present {
156 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": "ContentLengthRequired"}).Inc()
157 return probs.ContentLengthRequired()
158 }
159
160
161
162 if _, present := request.Header["Content-Type"]; !present {
163 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": "NoContentType"}).Inc()
164 return probs.InvalidContentType(fmt.Sprintf("No Content-Type header on POST. Content-Type must be %q",
165 expectedJWSContentType))
166 }
167 if contentType := request.Header.Get("Content-Type"); contentType != expectedJWSContentType {
168 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": "WrongContentType"}).Inc()
169 return probs.InvalidContentType(fmt.Sprintf("Invalid Content-Type header on POST. Content-Type must be %q",
170 expectedJWSContentType))
171 }
172
173
174
175 if _, present := request.Header["Replay-Nonce"]; present {
176 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": "ReplayNonceOutsideJWS"}).Inc()
177 return probs.Malformed("HTTP requests should NOT contain Replay-Nonce header. Use JWS nonce field")
178 }
179
180
181 if request.Body == nil {
182 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": "NoPOSTBody"}).Inc()
183 return probs.Malformed("No body on POST")
184 }
185
186 return nil
187 }
188
189
190
191
192 func nonceWellFormed(nonceHeader string, prefixLen int) *probs.ProblemDetails {
193 errBadNonce := probs.BadNonce(fmt.Sprintf("JWS has an invalid anti-replay nonce: %q", nonceHeader))
194 if len(nonceHeader) <= prefixLen {
195
196
197
198 return errBadNonce
199 }
200 body, err := base64.RawURLEncoding.DecodeString(nonceHeader[prefixLen:])
201 if err != nil {
202
203 return errBadNonce
204 }
205 if len(body) != nonce.NonceLen {
206
207 return errBadNonce
208 }
209 return nil
210 }
211
212
213
214
215
216 func (wfe *WebFrontEndImpl) validNonce(ctx context.Context, header jose.Header) *probs.ProblemDetails {
217 if len(header.Nonce) == 0 {
218 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMissingNonce"}).Inc()
219 return probs.BadNonce("JWS has no anti-replay nonce")
220 }
221 var valid bool
222 var err error
223 if wfe.noncePrefixMap == nil {
224
225 prob := nonceWellFormed(header.Nonce, nonce.PrefixLen)
226 if prob != nil {
227 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMalformedNonce"}).Inc()
228 return prob
229 }
230
231
232
233
234 ctx = context.WithValue(ctx, nonce.PrefixCtxKey{}, header.Nonce[:nonce.PrefixLen])
235 ctx = context.WithValue(ctx, nonce.HMACKeyCtxKey{}, wfe.rncKey)
236
237 resp, err := wfe.rnc.Redeem(ctx, &noncepb.NonceMessage{Nonce: header.Nonce})
238 if err != nil {
239 rpcStatus, ok := status.FromError(err)
240 if !ok || rpcStatus != nb.ErrNoBackendsMatchPrefix {
241 return web.ProblemDetailsForError(err, "failed to redeem nonce")
242 }
243
244
245
246
247
248 resp = &noncepb.ValidMessage{Valid: false}
249 wfe.stats.nonceNoMatchingBackendCount.Inc()
250 }
251 valid = resp.Valid
252 } else {
253
254
255
256 prob := nonceWellFormed(header.Nonce, nonce.DeprecatedPrefixLen)
257 if prob != nil {
258 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMalformedNonce"}).Inc()
259 return prob
260 }
261
262 valid, err = nonce.RemoteRedeem(ctx, wfe.noncePrefixMap, header.Nonce)
263 if err != nil {
264 return web.ProblemDetailsForError(err, "failed to redeem nonce")
265 }
266 }
267 if !valid {
268 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSInvalidNonce"}).Inc()
269 return probs.BadNonce(fmt.Sprintf("JWS has an invalid anti-replay nonce: %q", header.Nonce))
270 }
271 return nil
272 }
273
274
275
276
277
278 func (wfe *WebFrontEndImpl) validPOSTURL(
279 request *http.Request,
280 header jose.Header) *probs.ProblemDetails {
281 extraHeaders := header.ExtraHeaders
282
283 if len(extraHeaders) == 0 {
284 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSNoExtraHeaders"}).Inc()
285 return probs.Malformed("JWS header parameter 'url' required")
286 }
287
288 headerURL, ok := extraHeaders[jose.HeaderKey("url")].(string)
289 if !ok || len(headerURL) == 0 {
290 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMissingURL"}).Inc()
291 return probs.Malformed("JWS header parameter 'url' required")
292 }
293
294 expectedURL := url.URL{
295 Scheme: requestProto(request),
296 Host: request.Host,
297 Path: request.RequestURI,
298 }
299
300
301 if expectedURL.String() != headerURL {
302 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMismatchedURL"}).Inc()
303 return probs.Malformed(fmt.Sprintf(
304 "JWS header parameter 'url' incorrect. Expected %q got %q",
305 expectedURL.String(), headerURL))
306 }
307 return nil
308 }
309
310
311
312
313 func (wfe *WebFrontEndImpl) matchJWSURLs(outer, inner jose.Header) *probs.ProblemDetails {
314
315
316
317
318
319 outerURL, ok := outer.ExtraHeaders[jose.HeaderKey("url")].(string)
320 if !ok || len(outerURL) == 0 {
321 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverOuterJWSNoURL"}).Inc()
322 return probs.Malformed("Outer JWS header parameter 'url' required")
323 }
324
325
326 innerURL, ok := inner.ExtraHeaders[jose.HeaderKey("url")].(string)
327 if !ok || len(innerURL) == 0 {
328 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverInnerJWSNoURL"}).Inc()
329 return probs.Malformed("Inner JWS header parameter 'url' required")
330 }
331
332
333 if outerURL != innerURL {
334 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverMismatchedURLs"}).Inc()
335 return probs.Malformed(fmt.Sprintf(
336 "Outer JWS 'url' value %q does not match inner JWS 'url' value %q",
337 outerURL, innerURL))
338 }
339
340 return nil
341 }
342
343
344
345
346 type bJSONWebSignature struct {
347 *jose.JSONWebSignature
348 }
349
350
351
352
353
354 func (wfe *WebFrontEndImpl) parseJWS(body []byte) (*bJSONWebSignature, *probs.ProblemDetails) {
355
356
357
358
359
360
361 var unprotected struct {
362 Header map[string]string
363 Signatures []interface{}
364 }
365 err := json.Unmarshal(body, &unprotected)
366 if err != nil {
367 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSUnmarshalFailed"}).Inc()
368 return nil, probs.Malformed("Parse error reading JWS")
369 }
370
371
372
373 if unprotected.Header != nil {
374 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSUnprotectedHeaders"}).Inc()
375 return nil, probs.Malformed(
376 "JWS \"header\" field not allowed. All headers must be in \"protected\" field")
377 }
378
379
380
381 if len(unprotected.Signatures) > 0 {
382 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSMultiSig"}).Inc()
383 return nil, probs.Malformed(
384 "JWS \"signatures\" field not allowed. Only the \"signature\" field should contain a signature")
385 }
386
387
388
389 bodyStr := string(body)
390 parsedJWS, err := jose.ParseSigned(bodyStr)
391 if err != nil {
392 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSParseError"}).Inc()
393 return nil, probs.Malformed("Parse error reading JWS")
394 }
395 if len(parsedJWS.Signatures) > 1 {
396 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSTooManySignatures"}).Inc()
397 return nil, probs.Malformed("Too many signatures in POST body")
398 }
399 if len(parsedJWS.Signatures) == 0 {
400 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSNoSignatures"}).Inc()
401 return nil, probs.Malformed("POST JWS not signed")
402 }
403 if len(parsedJWS.Signatures) == 1 && len(parsedJWS.Signatures[0].Signature) == 0 {
404 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSEmptySignature"}).Inc()
405 return nil, probs.Malformed("POST JWS not signed")
406 }
407
408 return &bJSONWebSignature{parsedJWS}, nil
409 }
410
411
412 func (wfe *WebFrontEndImpl) parseJWSRequest(request *http.Request) (*bJSONWebSignature, *probs.ProblemDetails) {
413
414 if prob := wfe.validPOSTRequest(request); prob != nil {
415 return nil, prob
416 }
417
418
419
420 bodyBytes, err := io.ReadAll(http.MaxBytesReader(nil, request.Body, maxRequestSize))
421 if err != nil {
422 if err.Error() == "http: request body too large" {
423 return nil, probs.Unauthorized("request body too large")
424 }
425 wfe.stats.httpErrorCount.With(prometheus.Labels{"type": "UnableToReadReqBody"}).Inc()
426 return nil, probs.ServerInternal("unable to read request body")
427 }
428
429 jws, prob := wfe.parseJWS(bodyBytes)
430 if prob != nil {
431 return nil, prob
432 }
433
434 return jws, nil
435 }
436
437
438
439
440
441
442 func (wfe *WebFrontEndImpl) extractJWK(header jose.Header) (*jose.JSONWebKey, *probs.ProblemDetails) {
443
444
445 if prob := wfe.enforceJWSAuthType(header, embeddedJWK); prob != nil {
446 return nil, prob
447 }
448
449
450
451 key := header.JSONWebKey
452
453
454 if !key.Valid() {
455 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWKInvalid"}).Inc()
456 return nil, probs.Malformed("Invalid JWK in JWS header")
457 }
458
459 return key, nil
460 }
461
462
463
464
465 func (wfe *WebFrontEndImpl) acctIDFromURL(acctURL string, request *http.Request) (int64, *probs.ProblemDetails) {
466
467
468 expectedURLPrefix := web.RelativeEndpoint(request, acctPath)
469
470
471
472
473
474 var accountIDStr string
475 if strings.HasPrefix(acctURL, expectedURLPrefix) {
476 accountIDStr = strings.TrimPrefix(acctURL, expectedURLPrefix)
477 } else if strings.HasPrefix(acctURL, wfe.LegacyKeyIDPrefix) {
478 accountIDStr = strings.TrimPrefix(acctURL, wfe.LegacyKeyIDPrefix)
479 } else {
480 return 0, probs.Malformed(
481 fmt.Sprintf("KeyID header contained an invalid account URL: %q", acctURL))
482 }
483
484
485
486 accountID, err := strconv.ParseInt(accountIDStr, 10, 64)
487 if err != nil {
488 return 0, probs.Malformed("Malformed account ID in KeyID header URL: %q", acctURL)
489 }
490 return accountID, nil
491 }
492
493
494
495
496
497
498 func (wfe *WebFrontEndImpl) lookupJWK(
499 header jose.Header,
500 ctx context.Context,
501 request *http.Request,
502 logEvent *web.RequestEvent) (*jose.JSONWebKey, *core.Registration, *probs.ProblemDetails) {
503
504
505 if prob := wfe.enforceJWSAuthType(header, embeddedKeyID); prob != nil {
506 return nil, nil, prob
507 }
508
509 accountURL := header.KeyID
510 accountID, prob := wfe.acctIDFromURL(accountURL, request)
511 if prob != nil {
512 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSInvalidKeyID"}).Inc()
513 return nil, nil, prob
514 }
515
516
517 account, err := wfe.accountGetter.GetRegistration(ctx, &sapb.RegistrationID{Id: accountID})
518 if err != nil {
519
520 if errors.Is(err, berrors.NotFound) {
521 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSKeyIDNotFound"}).Inc()
522 return nil, nil, probs.AccountDoesNotExist(fmt.Sprintf(
523 "Account %q not found", accountURL))
524 }
525
526
527
528 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSKeyIDLookupFailed"}).Inc()
529
530 logEvent.AddError("calling SA.GetRegistration: %s", err)
531 return nil, nil, web.ProblemDetailsForError(err, fmt.Sprintf("Error retrieving account %q", accountURL))
532 }
533
534
535 if core.AcmeStatus(account.Status) != core.StatusValid {
536 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSKeyIDAccountInvalid"}).Inc()
537 return nil, nil, probs.Unauthorized(
538 fmt.Sprintf("Account is not valid, has status %q", account.Status))
539 }
540
541
542 logEvent.Requester = account.Id
543
544 acct, err := grpc.PbToRegistration(account)
545 if err != nil {
546 return nil, nil, probs.ServerInternal(fmt.Sprintf(
547 "Error unmarshalling account %q", accountURL))
548 }
549 return acct.Key, &acct, nil
550 }
551
552
553
554
555
556
557
558 func (wfe *WebFrontEndImpl) validJWSForKey(
559 ctx context.Context,
560 jws *bJSONWebSignature,
561 jwk *jose.JSONWebKey,
562 request *http.Request) ([]byte, *probs.ProblemDetails) {
563
564
565 err := checkAlgorithm(jwk, jws.Signatures[0].Header)
566 if err != nil {
567 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSAlgorithmCheckFailed"}).Inc()
568 return nil, probs.BadSignatureAlgorithm(err.Error())
569 }
570
571
572
573
574
575
576
577 payload, err := jws.Verify(jwk)
578 if err != nil {
579 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSVerifyFailed"}).Inc()
580 return nil, probs.Malformed("JWS verification error")
581 }
582
583
584 if prob := wfe.validNonce(ctx, jws.Signatures[0].Header); prob != nil {
585 return nil, prob
586 }
587
588
589 if prob := wfe.validPOSTURL(request, jws.Signatures[0].Header); prob != nil {
590 return nil, prob
591 }
592
593
594
595
596
597
598 var parsedBody struct{}
599 err = json.Unmarshal(payload, &parsedBody)
600 if string(payload) != "" && err != nil {
601 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSBodyUnmarshalFailed"}).Inc()
602 return nil, probs.Malformed("Request payload did not parse as JSON")
603 }
604
605 return payload, nil
606 }
607
608
609
610
611
612
613
614
615 func (wfe *WebFrontEndImpl) validJWSForAccount(
616 jws *bJSONWebSignature,
617 request *http.Request,
618 ctx context.Context,
619 logEvent *web.RequestEvent) ([]byte, *bJSONWebSignature, *core.Registration, *probs.ProblemDetails) {
620
621 pubKey, account, prob := wfe.lookupJWK(jws.Signatures[0].Header, ctx, request, logEvent)
622 if prob != nil {
623 return nil, nil, nil, prob
624 }
625
626
627 payload, prob := wfe.validJWSForKey(ctx, jws, pubKey, request)
628 if prob != nil {
629 return nil, nil, nil, prob
630 }
631
632 return payload, jws, account, nil
633 }
634
635
636
637
638
639
640 func (wfe *WebFrontEndImpl) validPOSTForAccount(
641 request *http.Request,
642 ctx context.Context,
643 logEvent *web.RequestEvent) ([]byte, *bJSONWebSignature, *core.Registration, *probs.ProblemDetails) {
644
645 jws, prob := wfe.parseJWSRequest(request)
646 if prob != nil {
647 return nil, nil, nil, prob
648 }
649 return wfe.validJWSForAccount(jws, request, ctx, logEvent)
650 }
651
652
653
654
655
656
657
658
659 func (wfe *WebFrontEndImpl) validPOSTAsGETForAccount(
660 request *http.Request,
661 ctx context.Context,
662 logEvent *web.RequestEvent) (*core.Registration, *probs.ProblemDetails) {
663
664 body, _, reg, prob := wfe.validPOSTForAccount(request, ctx, logEvent)
665 if prob != nil {
666 return nil, prob
667 }
668
669 if string(body) != "" {
670 return nil, probs.Malformed("POST-as-GET requests must have an empty payload")
671 }
672
673
674
675 logEvent.Method = "POST-as-GET"
676 return reg, prob
677 }
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 func (wfe *WebFrontEndImpl) validSelfAuthenticatedJWS(
696 ctx context.Context,
697 jws *bJSONWebSignature,
698 request *http.Request) ([]byte, *jose.JSONWebKey, *probs.ProblemDetails) {
699
700 pubKey, prob := wfe.extractJWK(jws.Signatures[0].Header)
701 if prob != nil {
702 return nil, nil, prob
703 }
704
705
706 payload, prob := wfe.validJWSForKey(ctx, jws, pubKey, request)
707 if prob != nil {
708 return nil, nil, prob
709 }
710
711 return payload, pubKey, nil
712 }
713
714
715
716
717 func (wfe *WebFrontEndImpl) validSelfAuthenticatedPOST(
718 ctx context.Context,
719 request *http.Request) ([]byte, *jose.JSONWebKey, *probs.ProblemDetails) {
720
721 jws, prob := wfe.parseJWSRequest(request)
722 if prob != nil {
723 return nil, nil, prob
724 }
725
726
727 payload, pubKey, prob := wfe.validSelfAuthenticatedJWS(ctx, jws, request)
728 if prob != nil {
729 return nil, nil, prob
730 }
731
732
733 err := wfe.keyPolicy.GoodKey(ctx, pubKey.Key)
734 if err != nil {
735 if errors.Is(err, goodkey.ErrBadKey) {
736 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWKRejectedByGoodKey"}).Inc()
737 return nil, nil, probs.BadPublicKey(err.Error())
738 }
739 return nil, nil, probs.ServerInternal("error checking key quality")
740 }
741
742 return payload, pubKey, nil
743 }
744
745
746
747
748 type rolloverRequest struct {
749 OldKey jose.JSONWebKey
750 Account string
751 }
752
753
754
755 type rolloverOperation struct {
756 rolloverRequest
757 NewKey jose.JSONWebKey
758 }
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778 func (wfe *WebFrontEndImpl) validKeyRollover(
779 ctx context.Context,
780 outerJWS *bJSONWebSignature,
781 innerJWS *bJSONWebSignature,
782 oldKey *jose.JSONWebKey) (*rolloverOperation, *probs.ProblemDetails) {
783
784
785 jwk, prob := wfe.extractJWK(innerJWS.Signatures[0].Header)
786 if prob != nil {
787 return nil, prob
788 }
789
790
791 err := wfe.keyPolicy.GoodKey(ctx, jwk.Key)
792 if err != nil {
793 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverJWKRejectedByGoodKey"}).Inc()
794 return nil, probs.BadPublicKey(err.Error())
795 }
796
797
798 err = checkAlgorithm(jwk, innerJWS.Signatures[0].Header)
799 if err != nil {
800 return nil, probs.Malformed(err.Error())
801 }
802
803
804
805
806
807 innerPayload, err := innerJWS.Verify(jwk)
808 if err != nil {
809 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverJWSVerifyFailed"}).Inc()
810 return nil, probs.Malformed("Inner JWS does not verify with embedded JWK")
811 }
812
813
814
815
816
817 if prob := wfe.matchJWSURLs(outerJWS.Signatures[0].Header, innerJWS.Signatures[0].Header); prob != nil {
818 return nil, prob
819 }
820
821 var req rolloverRequest
822 if json.Unmarshal(innerPayload, &req) != nil {
823 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverUnmarshalFailed"}).Inc()
824 return nil, probs.Malformed(
825 "Inner JWS payload did not parse as JSON key rollover object")
826 }
827
828
829
830 if req.OldKey.Key == nil {
831 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverWrongOldKey"}).Inc()
832 return nil, probs.Malformed("Inner JWS does not contain old key field matching current account key")
833 }
834
835
836
837 if keysEqual, err := core.PublicKeysEqual(req.OldKey.Key, oldKey.Key); err != nil {
838 return nil, probs.Malformed("Unable to compare new and old keys: %s", err.Error())
839 } else if !keysEqual {
840 wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverWrongOldKey"}).Inc()
841 return nil, probs.Malformed("Inner JWS does not contain old key field matching current account key")
842 }
843
844
845
846 return &rolloverOperation{
847 rolloverRequest: rolloverRequest{
848 OldKey: *oldKey,
849 Account: req.Account,
850 },
851 NewKey: *jwk,
852 }, nil
853 }
854
View as plain text