diff --git a/ra/ra.go b/ra/ra.go index e16df13de9a..a9e45ddd0a9 100644 --- a/ra/ra.go +++ b/ra/ra.go @@ -1525,6 +1525,14 @@ func (ra *RegistrationAuthorityImpl) PerformValidation( return nil, berrors.MalformedError("authorization must be pending") } + // A challenge which is already processing has a validation attempt in + // flight. Return the same conflict error that the SA would return below, + // rather than letting CheckPending turn this into a less helpful + // "malformed" error. + if ch.Status == core.StatusProcessing { + return nil, berrors.ConflictError("Authorization is already being validated. This may indicate your client attempted the same challenge multiple times, possibly due to a client bug.") + } + // Compute the key authorization field based on the registration key regPB, err := ra.SA.GetRegistration(ctx, &sapb.RegistrationID{Id: authz.RegistrationID}) if err != nil { @@ -1544,9 +1552,11 @@ func (ra *RegistrationAuthorityImpl) PerformValidation( return nil, berrors.MalformedError("cannot validate challenge: %s", cErr.Error()) } - // Set the authorization to "processing", to prevent parallel attempts. + // Set the authorization to "processing", to prevent parallel attempts, and + // record which challenge is being validated so that it can be rendered with + // the "processing" status while the authorization remains pending. if features.Get().SetAuthzProcessing { - _, err = ra.SA.SetAuthzProcessing(ctx, &sapb.AuthorizationID2{Id: authz.ID}) + _, err = ra.SA.SetAuthzProcessing(ctx, &sapb.SetAuthzProcessingRequest{Id: authz.ID, Attempted: string(ch.Type)}) if err != nil { return nil, fmt.Errorf("failed to mark authz as processing: %w", err) } diff --git a/ra/ra_test.go b/ra/ra_test.go index d993cfa36cb..caa2ff892ff 100644 --- a/ra/ra_test.go +++ b/ra/ra_test.go @@ -19,6 +19,7 @@ import ( "math/big" mrand "math/rand/v2" "net/netip" + "os" "regexp" "strings" "sync" @@ -594,6 +595,116 @@ func TestPerformValidationSuccess(t *testing.T) { } } +// mockSAWithProcessingCapture is a mock sapb.StorageAuthorityClient that +// forwards all method calls to an inner SA, but also records the request +// passed to SetAuthzProcessing and the authorization as the inner SA renders it +// immediately afterwards (i.e. while it is still processing). +type mockSAWithProcessingCapture struct { + sapb.StorageAuthorityClient + req *sapb.SetAuthzProcessingRequest + authz *corepb.Authorization +} + +func (msa *mockSAWithProcessingCapture) SetAuthzProcessing(ctx context.Context, req *sapb.SetAuthzProcessingRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { + res, err := msa.StorageAuthorityClient.SetAuthzProcessing(ctx, req) + if err != nil { + return res, err + } + msa.req = req + msa.authz, err = msa.StorageAuthorityClient.GetAuthorization2(ctx, &sapb.AuthorizationID2{Id: req.Id}) + if err != nil { + return nil, err + } + return res, nil +} + +func TestPerformValidationSetsChallengeProcessing(t *testing.T) { + if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" { + t.Skip("TestPerformValidationSetsChallengeProcessing requires config-next") + } + + va, sa, ra, _, fc, registration, cleanUp := initAuthorities(t) + defer cleanUp() + + features.Set(features.Config{SetAuthzProcessing: true}) + defer features.Reset() + + msa := &mockSAWithProcessingCapture{StorageAuthorityClient: sa} + ra.SA = msa + + authzPB := createPendingAuthorization(t, sa, registration.Id, identifier.NewDNS("example.com"), fc.Now().Add(12*time.Hour)) + + va.doDCVResult = &vapb.ValidationResult{Problem: nil} + va.doCAAResponse = &vapb.IsCAAValidResponse{Problem: nil} + + challIdx := dnsChallIdx(t, authzPB.Challenges) + _, err := ra.PerformValidation(ctx, &rapb.PerformValidationRequest{ + Authz: authzPB, + ChallengeIndex: challIdx, + }) + test.AssertNotError(t, err, "PerformValidation failed") + + // The RA should have told the SA which challenge it is validating. + test.AssertNotNil(t, msa.req, "SetAuthzProcessing was not called") + test.AssertEquals(t, msa.req.Id, authzPB.Id) + test.AssertEquals(t, msa.req.Attempted, string(core.ChallengeTypeDNS01)) + + // While validation is in flight, the authz is still pending but the + // attempted challenge is processing. + test.AssertNotNil(t, msa.authz, "authz was not fetched after SetAuthzProcessing") + test.AssertEquals(t, msa.authz.Status, string(core.StatusPending)) + test.AssertEquals(t, len(msa.authz.Challenges), 1) + test.AssertEquals(t, msa.authz.Challenges[0].Type, string(core.ChallengeTypeDNS01)) + test.AssertEquals(t, msa.authz.Challenges[0].Status, string(core.StatusProcessing)) + + select { + case <-va.doDCVRequest: + case <-time.After(time.Second): + t.Fatal("Timed out waiting for DummyValidationAuthority.PerformValidation to complete") + } + + // Sleep so the RA has a chance to write to the SA + time.Sleep(100 * time.Millisecond) + + // Once validation completes, the challenge should be valid. + dbAuthzPB := getAuthorization(t, authzPB.Id, sa) + test.AssertEquals(t, dbAuthzPB.Status, string(core.StatusValid)) + test.AssertEquals(t, dbAuthzPB.Challenges[0].Status, string(core.StatusValid)) +} + +func TestPerformValidationChallengeProcessing(t *testing.T) { + _, _, ra, _, fc, _, cleanUp := initAuthorities(t) + defer cleanUp() + + // An authz whose challenge is already processing has a validation attempt + // in flight, so a second attempt should be rejected as a conflict rather + // than as a malformed request. + exp := fc.Now().Add(10 * time.Hour) + authz := core.Authorization{ + ID: 1337, + Identifier: identifier.NewDNS("not-example.com"), + RegistrationID: 1, + Status: core.StatusPending, + Challenges: []core.Challenge{ + { + Status: core.StatusProcessing, + Type: core.ChallengeTypeDNS01, + Token: "exampleToken", + }, + }, + Expires: &exp, + } + authzPB, err := bgrpc.AuthzToPB(authz) + test.AssertNotError(t, err, "AuthzToPB failed") + + _, err = ra.PerformValidation(context.Background(), &rapb.PerformValidationRequest{ + Authz: authzPB, + ChallengeIndex: 0, + }) + test.AssertError(t, err, "ra.PerformValidation allowed a second attempt at a processing challenge") + test.AssertErrorIs(t, err, berrors.Conflict) +} + // mockSAWithSyncPause is a mock sapb.StorageAuthorityClient that forwards all // method calls to an inner SA, but also performs a blocking write to a channel // when PauseIdentifiers is called to allow the tests to synchronize. diff --git a/sa/model.go b/sa/model.go index 5d93f0bc87b..1c0ac0cb5f8 100644 --- a/sa/model.go +++ b/sa/model.go @@ -748,8 +748,18 @@ func authzPBToModel(authz *corepb.Authorization) (*authzModel, error) { } // populateAttemptedFields takes a challenge and populates it with the validation fields status, -// validation records, and error (the latter only if the validation failed) from an authzModel. +// validation records, error (the latter only if the validation failed), and validated time from an +// authzModel. If the authorization is still pending, the challenge is the one currently being +// validated, so it is only given the status core.StatusProcessing. func populateAttemptedFields(am authzModel, challenge *corepb.Challenge) error { + if am.Status == statusUint(core.StatusPending) { + // The RA has begun validating this challenge, but has not yet + // finalized the authorization. There are no validation results to + // populate yet. + challenge.Status = string(core.StatusProcessing) + return nil + } + if len(am.ValidationError) != 0 { // If the error is non-empty the challenge must be invalid. challenge.Status = string(core.StatusInvalid) @@ -789,6 +799,12 @@ func populateAttemptedFields(am authzModel, challenge *corepb.Challenge) error { return err } } + // Get the attemptedAt time and assign to the challenge validated time. + var validated *timestamppb.Timestamp + if am.AttemptedAt != nil { + validated = timestamppb.New(*am.AttemptedAt) + } + challenge.Validated = validated return nil } @@ -811,44 +827,33 @@ func modelToAuthzPB(am authzModel) (*corepb.Authorization, error) { Expires: timestamppb.New(am.Expires), CertificateProfileName: profile, } - // Populate authorization challenge array. We do this by iterating through - // the challenge type bitmap and creating a challenge of each type if its - // bit is set. Each of these challenges has the token from the authorization - // model and has its status set to core.StatusPending by default. If the - // challenge type is equal to that in the 'attempted' row we set the status - // to core.StatusValid or core.StatusInvalid depending on if there is anything - // in ValidationError and populate the ValidationRecord and ValidationError - // fields. - for pos := range uint8(8) { - if (am.Challenges>>pos)&1 == 1 { - challType := uintToChallType[pos] - challenge := &corepb.Challenge{ - Type: challType, - Status: string(core.StatusPending), - Token: base64.RawURLEncoding.EncodeToString(am.Token), - } - // If the challenge type matches the attempted type it must be either - // valid or invalid and we need to populate extra fields. - // Also, once any challenge has been attempted, we consider the other - // challenges "gone" per https://tools.ietf.org/html/rfc8555#section-7.1.4 - if am.Attempted != nil { - if uintToChallType[*am.Attempted] == challType { - err := populateAttemptedFields(am, challenge) - if err != nil { - return nil, err - } - // Get the attemptedAt time and assign to the challenge validated time. - var validated *timestamppb.Timestamp - if am.AttemptedAt != nil { - validated = timestamppb.New(*am.AttemptedAt) - } - challenge.Validated = validated - pb.Challenges = append(pb.Challenges, challenge) - } - } else { - // When no challenge has been attempted yet, all challenges are still - // present. - pb.Challenges = append(pb.Challenges, challenge) + // Populate authorization challenge array. Once any challenge has been + // attempted, we consider the other challenges "gone" per + // https://tools.ietf.org/html/rfc8555#section-7.1.4, so the array holds + // only the attempted challenge, populated with its status and validation + // results by populateAttemptedFields. Otherwise we iterate through the + // challenge type bitmap and create a pending challenge of each type whose + // bit is set. Each of these challenges has the token from the + // authorization model. + token := base64.RawURLEncoding.EncodeToString(am.Token) + if am.Attempted != nil { + challenge := &corepb.Challenge{ + Type: uintToChallType[*am.Attempted], + Token: token, + } + err := populateAttemptedFields(am, challenge) + if err != nil { + return nil, err + } + pb.Challenges = append(pb.Challenges, challenge) + } else { + for pos := range uint8(8) { + if (am.Challenges>>pos)&1 == 1 { + pb.Challenges = append(pb.Challenges, &corepb.Challenge{ + Type: uintToChallType[pos], + Status: string(core.StatusPending), + Token: token, + }) } } } diff --git a/sa/model_test.go b/sa/model_test.go index 8b7bdcd4d45..6c083fcaaa8 100644 --- a/sa/model_test.go +++ b/sa/model_test.go @@ -224,6 +224,48 @@ func TestAuthzModel(t *testing.T) { } } +// TestAuthzModelProcessing tests that a pending authorization whose attempted +// column has been set (which is what SetAuthzProcessing does) renders the +// attempted challenge with the "processing" status and no validation results, +// while the authorization itself remains pending. +func TestAuthzModelProcessing(t *testing.T) { + attempted := challTypeToUint[string(core.ChallengeTypeDNS01)] + model := authzModel{ + ID: 1, + IdentifierType: identifierTypeToUint[string(identifier.TypeDNS)], + IdentifierValue: "example.com", + RegistrationID: 1, + Status: statusUint(core.StatusPending), + Expires: time.Now().Add(time.Hour), + Challenges: 1< core.Identifier - 59, // 1: sa.GetValidAuthorizationsRequest.validUntil:type_name -> google.protobuf.Timestamp + 59, // 0: sa.GetValidAuthorizationsRequest.identifiers:type_name -> core.Identifier + 60, // 1: sa.GetValidAuthorizationsRequest.validUntil:type_name -> google.protobuf.Timestamp 6, // 2: sa.SerialsMetadata.metadata:type_name -> sa.SerialMetadata - 59, // 3: sa.SerialMetadata.created:type_name -> google.protobuf.Timestamp - 59, // 4: sa.SerialMetadata.expires:type_name -> google.protobuf.Timestamp - 59, // 5: sa.Range.earliest:type_name -> google.protobuf.Timestamp - 59, // 6: sa.Range.latest:type_name -> google.protobuf.Timestamp - 59, // 7: sa.Timestamps.timestamps:type_name -> google.protobuf.Timestamp - 58, // 8: sa.CountInvalidAuthorizationsRequest.identifier:type_name -> core.Identifier + 60, // 3: sa.SerialMetadata.created:type_name -> google.protobuf.Timestamp + 60, // 4: sa.SerialMetadata.expires:type_name -> google.protobuf.Timestamp + 60, // 5: sa.Range.earliest:type_name -> google.protobuf.Timestamp + 60, // 6: sa.Range.latest:type_name -> google.protobuf.Timestamp + 60, // 7: sa.Timestamps.timestamps:type_name -> google.protobuf.Timestamp + 59, // 8: sa.CountInvalidAuthorizationsRequest.identifier:type_name -> core.Identifier 7, // 9: sa.CountInvalidAuthorizationsRequest.range:type_name -> sa.Range - 58, // 10: sa.CountFQDNSetsRequest.identifiers:type_name -> core.Identifier - 60, // 11: sa.CountFQDNSetsRequest.window:type_name -> google.protobuf.Duration - 59, // 12: sa.AddSerialRequest.created:type_name -> google.protobuf.Timestamp - 59, // 13: sa.AddSerialRequest.expires:type_name -> google.protobuf.Timestamp - 59, // 14: sa.AddCertificateRequest.issued:type_name -> google.protobuf.Timestamp - 59, // 15: sa.NewOrderRequest.expires:type_name -> google.protobuf.Timestamp - 58, // 16: sa.NewOrderRequest.identifiers:type_name -> core.Identifier - 58, // 17: sa.NewAuthzRequest.identifier:type_name -> core.Identifier - 59, // 18: sa.NewAuthzRequest.expires:type_name -> google.protobuf.Timestamp + 59, // 10: sa.CountFQDNSetsRequest.identifiers:type_name -> core.Identifier + 61, // 11: sa.CountFQDNSetsRequest.window:type_name -> google.protobuf.Duration + 60, // 12: sa.AddSerialRequest.created:type_name -> google.protobuf.Timestamp + 60, // 13: sa.AddSerialRequest.expires:type_name -> google.protobuf.Timestamp + 60, // 14: sa.AddCertificateRequest.issued:type_name -> google.protobuf.Timestamp + 60, // 15: sa.NewOrderRequest.expires:type_name -> google.protobuf.Timestamp + 59, // 16: sa.NewOrderRequest.identifiers:type_name -> core.Identifier + 59, // 17: sa.NewAuthzRequest.identifier:type_name -> core.Identifier + 60, // 18: sa.NewAuthzRequest.expires:type_name -> google.protobuf.Timestamp 16, // 19: sa.NewOrderAndAuthzsRequest.newOrder:type_name -> sa.NewOrderRequest 17, // 20: sa.NewOrderAndAuthzsRequest.newAuthzs:type_name -> sa.NewAuthzRequest - 61, // 21: sa.SetOrderErrorRequest.error:type_name -> core.ProblemDetails - 58, // 22: sa.GetOrderForNamesRequest.identifiers:type_name -> core.Identifier - 58, // 23: sa.GetAuthorizationsRequest.identifiers:type_name -> core.Identifier - 59, // 24: sa.GetAuthorizationsRequest.validUntil:type_name -> google.protobuf.Timestamp - 62, // 25: sa.Authorizations.authzs:type_name -> core.Authorization - 59, // 26: sa.RevokeCertificateRequest.date:type_name -> google.protobuf.Timestamp - 59, // 27: sa.RevokeCertificateRequest.backdate:type_name -> google.protobuf.Timestamp - 59, // 28: sa.FinalizeAuthorizationRequest.expires:type_name -> google.protobuf.Timestamp - 63, // 29: sa.FinalizeAuthorizationRequest.validationRecords:type_name -> core.ValidationRecord - 61, // 30: sa.FinalizeAuthorizationRequest.validationError:type_name -> core.ProblemDetails - 59, // 31: sa.FinalizeAuthorizationRequest.attemptedAt:type_name -> google.protobuf.Timestamp - 59, // 32: sa.AddBlockedKeyRequest.added:type_name -> google.protobuf.Timestamp - 59, // 33: sa.Incident.renewBy:type_name -> google.protobuf.Timestamp - 31, // 34: sa.Incidents.incidents:type_name -> sa.Incident - 59, // 35: sa.CreateIncidentRequest.renewBy:type_name -> google.protobuf.Timestamp - 59, // 36: sa.UpdateIncidentRequest.renewBy:type_name -> google.protobuf.Timestamp - 37, // 37: sa.AddSerialsToIncidentRequest.metadata:type_name -> sa.AddSerialsToIncidentMetadata - 38, // 38: sa.AddSerialsToIncidentRequest.batch:type_name -> sa.AddSerialsToIncidentBatch - 59, // 39: sa.IncidentSerial.lastNoticeSent:type_name -> google.protobuf.Timestamp - 59, // 40: sa.GetRevokedCertsByShardRequest.revokedBefore:type_name -> google.protobuf.Timestamp - 59, // 41: sa.GetRevokedCertsByShardRequest.expiresAfter:type_name -> google.protobuf.Timestamp - 59, // 42: sa.RevocationStatus.revokedDate:type_name -> google.protobuf.Timestamp - 59, // 43: sa.LeaseCRLShardRequest.until:type_name -> google.protobuf.Timestamp - 59, // 44: sa.UpdateCRLShardRequest.thisUpdate:type_name -> google.protobuf.Timestamp - 59, // 45: sa.UpdateCRLShardRequest.nextUpdate:type_name -> google.protobuf.Timestamp - 58, // 46: sa.Identifiers.identifiers:type_name -> core.Identifier - 58, // 47: sa.PauseRequest.identifiers:type_name -> core.Identifier - 60, // 48: sa.RateLimitOverride.period:type_name -> google.protobuf.Duration - 49, // 49: sa.AddRateLimitOverrideRequest.override:type_name -> sa.RateLimitOverride - 49, // 50: sa.AddRateLimitOverrideResponse.existing:type_name -> sa.RateLimitOverride - 49, // 51: sa.RateLimitOverrideResponse.override:type_name -> sa.RateLimitOverride - 59, // 52: sa.RateLimitOverrideResponse.updatedAt:type_name -> google.protobuf.Timestamp - 58, // 53: sa.RevokeAuthorizationsForRequest.identifier:type_name -> core.Identifier + 62, // 21: sa.SetOrderErrorRequest.error:type_name -> core.ProblemDetails + 59, // 22: sa.GetOrderForNamesRequest.identifiers:type_name -> core.Identifier + 59, // 23: sa.GetAuthorizationsRequest.identifiers:type_name -> core.Identifier + 60, // 24: sa.GetAuthorizationsRequest.validUntil:type_name -> google.protobuf.Timestamp + 63, // 25: sa.Authorizations.authzs:type_name -> core.Authorization + 60, // 26: sa.RevokeCertificateRequest.date:type_name -> google.protobuf.Timestamp + 60, // 27: sa.RevokeCertificateRequest.backdate:type_name -> google.protobuf.Timestamp + 60, // 28: sa.FinalizeAuthorizationRequest.expires:type_name -> google.protobuf.Timestamp + 64, // 29: sa.FinalizeAuthorizationRequest.validationRecords:type_name -> core.ValidationRecord + 62, // 30: sa.FinalizeAuthorizationRequest.validationError:type_name -> core.ProblemDetails + 60, // 31: sa.FinalizeAuthorizationRequest.attemptedAt:type_name -> google.protobuf.Timestamp + 60, // 32: sa.AddBlockedKeyRequest.added:type_name -> google.protobuf.Timestamp + 60, // 33: sa.Incident.renewBy:type_name -> google.protobuf.Timestamp + 32, // 34: sa.Incidents.incidents:type_name -> sa.Incident + 60, // 35: sa.CreateIncidentRequest.renewBy:type_name -> google.protobuf.Timestamp + 60, // 36: sa.UpdateIncidentRequest.renewBy:type_name -> google.protobuf.Timestamp + 38, // 37: sa.AddSerialsToIncidentRequest.metadata:type_name -> sa.AddSerialsToIncidentMetadata + 39, // 38: sa.AddSerialsToIncidentRequest.batch:type_name -> sa.AddSerialsToIncidentBatch + 60, // 39: sa.IncidentSerial.lastNoticeSent:type_name -> google.protobuf.Timestamp + 60, // 40: sa.GetRevokedCertsByShardRequest.revokedBefore:type_name -> google.protobuf.Timestamp + 60, // 41: sa.GetRevokedCertsByShardRequest.expiresAfter:type_name -> google.protobuf.Timestamp + 60, // 42: sa.RevocationStatus.revokedDate:type_name -> google.protobuf.Timestamp + 60, // 43: sa.LeaseCRLShardRequest.until:type_name -> google.protobuf.Timestamp + 60, // 44: sa.UpdateCRLShardRequest.thisUpdate:type_name -> google.protobuf.Timestamp + 60, // 45: sa.UpdateCRLShardRequest.nextUpdate:type_name -> google.protobuf.Timestamp + 59, // 46: sa.Identifiers.identifiers:type_name -> core.Identifier + 59, // 47: sa.PauseRequest.identifiers:type_name -> core.Identifier + 61, // 48: sa.RateLimitOverride.period:type_name -> google.protobuf.Duration + 50, // 49: sa.AddRateLimitOverrideRequest.override:type_name -> sa.RateLimitOverride + 50, // 50: sa.AddRateLimitOverrideResponse.existing:type_name -> sa.RateLimitOverride + 50, // 51: sa.RateLimitOverrideResponse.override:type_name -> sa.RateLimitOverride + 60, // 52: sa.RateLimitOverrideResponse.updatedAt:type_name -> google.protobuf.Timestamp + 59, // 53: sa.RevokeAuthorizationsForRequest.identifier:type_name -> core.Identifier 11, // 54: sa.StorageAuthorityReadOnly.FQDNSetTimestampsForWindow:input_type -> sa.CountFQDNSetsRequest 26, // 55: sa.StorageAuthorityReadOnly.GetAuthorization2:input_type -> sa.AuthorizationID2 3, // 56: sa.StorageAuthorityReadOnly.GetCertificate:input_type -> sa.Serial @@ -4401,23 +4463,23 @@ var file_sa_proto_depIdxs = []int32{ 0, // 61: sa.StorageAuthorityReadOnly.GetRegistration:input_type -> sa.RegistrationID 1, // 62: sa.StorageAuthorityReadOnly.GetRegistrationByKey:input_type -> sa.JSONWebKey 3, // 63: sa.StorageAuthorityReadOnly.GetRevocationStatus:input_type -> sa.Serial - 40, // 64: sa.StorageAuthorityReadOnly.GetRevokedCertsByShard:input_type -> sa.GetRevokedCertsByShardRequest + 41, // 64: sa.StorageAuthorityReadOnly.GetRevokedCertsByShard:input_type -> sa.GetRevokedCertsByShardRequest 3, // 65: sa.StorageAuthorityReadOnly.GetSerialMetadata:input_type -> sa.Serial 4, // 66: sa.StorageAuthorityReadOnly.GetSerialsMetadata:input_type -> sa.Serials 0, // 67: sa.StorageAuthorityReadOnly.GetSerialsByAccount:input_type -> sa.RegistrationID - 30, // 68: sa.StorageAuthorityReadOnly.GetSerialsByKey:input_type -> sa.SPKIHash + 31, // 68: sa.StorageAuthorityReadOnly.GetSerialsByKey:input_type -> sa.SPKIHash 2, // 69: sa.StorageAuthorityReadOnly.GetValidAuthorizations2:input_type -> sa.GetValidAuthorizationsRequest 20, // 70: sa.StorageAuthorityReadOnly.GetValidOrderAuthorizations2:input_type -> sa.GetOrderAuthorizationsRequest 20, // 71: sa.StorageAuthorityReadOnly.GetOrderAuthorizations:input_type -> sa.GetOrderAuthorizationsRequest 3, // 72: sa.StorageAuthorityReadOnly.IncidentsForSerial:input_type -> sa.Serial - 30, // 73: sa.StorageAuthorityReadOnly.KeyBlocked:input_type -> sa.SPKIHash - 64, // 74: sa.StorageAuthorityReadOnly.ListIncidents:input_type -> google.protobuf.Empty + 31, // 73: sa.StorageAuthorityReadOnly.KeyBlocked:input_type -> sa.SPKIHash + 65, // 74: sa.StorageAuthorityReadOnly.ListIncidents:input_type -> google.protobuf.Empty 3, // 75: sa.StorageAuthorityReadOnly.ReplacementOrderExists:input_type -> sa.Serial - 33, // 76: sa.StorageAuthorityReadOnly.SerialsForIncident:input_type -> sa.SerialsForIncidentRequest - 46, // 77: sa.StorageAuthorityReadOnly.CheckIdentifiersPaused:input_type -> sa.PauseRequest + 34, // 76: sa.StorageAuthorityReadOnly.SerialsForIncident:input_type -> sa.SerialsForIncidentRequest + 47, // 77: sa.StorageAuthorityReadOnly.CheckIdentifiersPaused:input_type -> sa.PauseRequest 0, // 78: sa.StorageAuthorityReadOnly.GetPausedIdentifiers:input_type -> sa.RegistrationID - 54, // 79: sa.StorageAuthorityReadOnly.GetRateLimitOverride:input_type -> sa.GetRateLimitOverrideRequest - 64, // 80: sa.StorageAuthorityReadOnly.GetEnabledRateLimitOverrides:input_type -> google.protobuf.Empty + 55, // 79: sa.StorageAuthorityReadOnly.GetRateLimitOverride:input_type -> sa.GetRateLimitOverrideRequest + 65, // 80: sa.StorageAuthorityReadOnly.GetEnabledRateLimitOverrides:input_type -> google.protobuf.Empty 11, // 81: sa.StorageAuthority.FQDNSetTimestampsForWindow:input_type -> sa.CountFQDNSetsRequest 26, // 82: sa.StorageAuthority.GetAuthorization2:input_type -> sa.AuthorizationID2 3, // 83: sa.StorageAuthority.GetCertificate:input_type -> sa.Serial @@ -4428,63 +4490,63 @@ var file_sa_proto_depIdxs = []int32{ 0, // 88: sa.StorageAuthority.GetRegistration:input_type -> sa.RegistrationID 1, // 89: sa.StorageAuthority.GetRegistrationByKey:input_type -> sa.JSONWebKey 3, // 90: sa.StorageAuthority.GetRevocationStatus:input_type -> sa.Serial - 40, // 91: sa.StorageAuthority.GetRevokedCertsByShard:input_type -> sa.GetRevokedCertsByShardRequest + 41, // 91: sa.StorageAuthority.GetRevokedCertsByShard:input_type -> sa.GetRevokedCertsByShardRequest 3, // 92: sa.StorageAuthority.GetSerialMetadata:input_type -> sa.Serial 4, // 93: sa.StorageAuthority.GetSerialsMetadata:input_type -> sa.Serials 0, // 94: sa.StorageAuthority.GetSerialsByAccount:input_type -> sa.RegistrationID - 30, // 95: sa.StorageAuthority.GetSerialsByKey:input_type -> sa.SPKIHash + 31, // 95: sa.StorageAuthority.GetSerialsByKey:input_type -> sa.SPKIHash 2, // 96: sa.StorageAuthority.GetValidAuthorizations2:input_type -> sa.GetValidAuthorizationsRequest 20, // 97: sa.StorageAuthority.GetValidOrderAuthorizations2:input_type -> sa.GetOrderAuthorizationsRequest 20, // 98: sa.StorageAuthority.GetOrderAuthorizations:input_type -> sa.GetOrderAuthorizationsRequest 3, // 99: sa.StorageAuthority.IncidentsForSerial:input_type -> sa.Serial - 30, // 100: sa.StorageAuthority.KeyBlocked:input_type -> sa.SPKIHash - 64, // 101: sa.StorageAuthority.ListIncidents:input_type -> google.protobuf.Empty + 31, // 100: sa.StorageAuthority.KeyBlocked:input_type -> sa.SPKIHash + 65, // 101: sa.StorageAuthority.ListIncidents:input_type -> google.protobuf.Empty 3, // 102: sa.StorageAuthority.ReplacementOrderExists:input_type -> sa.Serial - 33, // 103: sa.StorageAuthority.SerialsForIncident:input_type -> sa.SerialsForIncidentRequest - 46, // 104: sa.StorageAuthority.CheckIdentifiersPaused:input_type -> sa.PauseRequest + 34, // 103: sa.StorageAuthority.SerialsForIncident:input_type -> sa.SerialsForIncidentRequest + 47, // 104: sa.StorageAuthority.CheckIdentifiersPaused:input_type -> sa.PauseRequest 0, // 105: sa.StorageAuthority.GetPausedIdentifiers:input_type -> sa.RegistrationID - 54, // 106: sa.StorageAuthority.GetRateLimitOverride:input_type -> sa.GetRateLimitOverrideRequest - 64, // 107: sa.StorageAuthority.GetEnabledRateLimitOverrides:input_type -> google.protobuf.Empty - 29, // 108: sa.StorageAuthority.AddBlockedKey:input_type -> sa.AddBlockedKeyRequest + 55, // 106: sa.StorageAuthority.GetRateLimitOverride:input_type -> sa.GetRateLimitOverrideRequest + 65, // 107: sa.StorageAuthority.GetEnabledRateLimitOverrides:input_type -> google.protobuf.Empty + 30, // 108: sa.StorageAuthority.AddBlockedKey:input_type -> sa.AddBlockedKeyRequest 14, // 109: sa.StorageAuthority.AddCertificate:input_type -> sa.AddCertificateRequest 14, // 110: sa.StorageAuthority.AddPrecertificate:input_type -> sa.AddCertificateRequest 13, // 111: sa.StorageAuthority.AddSerial:input_type -> sa.AddSerialRequest 26, // 112: sa.StorageAuthority.DeactivateAuthorization2:input_type -> sa.AuthorizationID2 0, // 113: sa.StorageAuthority.DeactivateRegistration:input_type -> sa.RegistrationID - 28, // 114: sa.StorageAuthority.FinalizeAuthorization2:input_type -> sa.FinalizeAuthorizationRequest + 29, // 114: sa.StorageAuthority.FinalizeAuthorization2:input_type -> sa.FinalizeAuthorizationRequest 22, // 115: sa.StorageAuthority.FinalizeOrder:input_type -> sa.FinalizeOrderRequest 23, // 116: sa.StorageAuthority.FinalizeMTCOrder:input_type -> sa.FinalizeMTCOrderRequest 18, // 117: sa.StorageAuthority.NewOrderAndAuthzs:input_type -> sa.NewOrderAndAuthzsRequest - 65, // 118: sa.StorageAuthority.NewRegistration:input_type -> core.Registration - 27, // 119: sa.StorageAuthority.RevokeCertificate:input_type -> sa.RevokeCertificateRequest - 26, // 120: sa.StorageAuthority.SetAuthzProcessing:input_type -> sa.AuthorizationID2 + 66, // 118: sa.StorageAuthority.NewRegistration:input_type -> core.Registration + 28, // 119: sa.StorageAuthority.RevokeCertificate:input_type -> sa.RevokeCertificateRequest + 27, // 120: sa.StorageAuthority.SetAuthzProcessing:input_type -> sa.SetAuthzProcessingRequest 19, // 121: sa.StorageAuthority.SetOrderError:input_type -> sa.SetOrderErrorRequest 15, // 122: sa.StorageAuthority.SetOrderProcessing:input_type -> sa.OrderRequest - 48, // 123: sa.StorageAuthority.UpdateRegistrationKey:input_type -> sa.UpdateRegistrationKeyRequest - 27, // 124: sa.StorageAuthority.UpdateRevokedCertificate:input_type -> sa.RevokeCertificateRequest - 42, // 125: sa.StorageAuthority.LeaseCRLShard:input_type -> sa.LeaseCRLShardRequest - 44, // 126: sa.StorageAuthority.UpdateCRLShard:input_type -> sa.UpdateCRLShardRequest - 46, // 127: sa.StorageAuthority.PauseIdentifiers:input_type -> sa.PauseRequest + 49, // 123: sa.StorageAuthority.UpdateRegistrationKey:input_type -> sa.UpdateRegistrationKeyRequest + 28, // 124: sa.StorageAuthority.UpdateRevokedCertificate:input_type -> sa.RevokeCertificateRequest + 43, // 125: sa.StorageAuthority.LeaseCRLShard:input_type -> sa.LeaseCRLShardRequest + 45, // 126: sa.StorageAuthority.UpdateCRLShard:input_type -> sa.UpdateCRLShardRequest + 47, // 127: sa.StorageAuthority.PauseIdentifiers:input_type -> sa.PauseRequest 0, // 128: sa.StorageAuthority.UnpauseAccount:input_type -> sa.RegistrationID - 50, // 129: sa.StorageAuthority.AddRateLimitOverride:input_type -> sa.AddRateLimitOverrideRequest - 53, // 130: sa.StorageAuthority.DisableRateLimitOverride:input_type -> sa.DisableRateLimitOverrideRequest - 52, // 131: sa.StorageAuthority.EnableRateLimitOverride:input_type -> sa.EnableRateLimitOverrideRequest - 56, // 132: sa.StorageAuthority.RevokeAuthorizationsFor:input_type -> sa.RevokeAuthorizationsForRequest - 40, // 133: sa.StorageAuthority.GetLatestRevokedCertByShard:input_type -> sa.GetRevokedCertsByShardRequest - 34, // 134: sa.StorageAuthorityAdmin.CreateIncident:input_type -> sa.CreateIncidentRequest - 35, // 135: sa.StorageAuthorityAdmin.UpdateIncident:input_type -> sa.UpdateIncidentRequest - 36, // 136: sa.StorageAuthorityAdmin.AddSerialsToIncident:input_type -> sa.AddSerialsToIncidentRequest + 51, // 129: sa.StorageAuthority.AddRateLimitOverride:input_type -> sa.AddRateLimitOverrideRequest + 54, // 130: sa.StorageAuthority.DisableRateLimitOverride:input_type -> sa.DisableRateLimitOverrideRequest + 53, // 131: sa.StorageAuthority.EnableRateLimitOverride:input_type -> sa.EnableRateLimitOverrideRequest + 57, // 132: sa.StorageAuthority.RevokeAuthorizationsFor:input_type -> sa.RevokeAuthorizationsForRequest + 41, // 133: sa.StorageAuthority.GetLatestRevokedCertByShard:input_type -> sa.GetRevokedCertsByShardRequest + 35, // 134: sa.StorageAuthorityAdmin.CreateIncident:input_type -> sa.CreateIncidentRequest + 36, // 135: sa.StorageAuthorityAdmin.UpdateIncident:input_type -> sa.UpdateIncidentRequest + 37, // 136: sa.StorageAuthorityAdmin.AddSerialsToIncident:input_type -> sa.AddSerialsToIncidentRequest 9, // 137: sa.StorageAuthorityReadOnly.FQDNSetTimestampsForWindow:output_type -> sa.Timestamps - 62, // 138: sa.StorageAuthorityReadOnly.GetAuthorization2:output_type -> core.Authorization - 66, // 139: sa.StorageAuthorityReadOnly.GetCertificate:output_type -> core.Certificate - 66, // 140: sa.StorageAuthorityReadOnly.GetLintPrecertificate:output_type -> core.Certificate - 67, // 141: sa.StorageAuthorityReadOnly.GetCertificateStatus:output_type -> core.CertificateStatus - 68, // 142: sa.StorageAuthorityReadOnly.GetOrder:output_type -> core.Order - 68, // 143: sa.StorageAuthorityReadOnly.GetOrderForNames:output_type -> core.Order - 65, // 144: sa.StorageAuthorityReadOnly.GetRegistration:output_type -> core.Registration - 65, // 145: sa.StorageAuthorityReadOnly.GetRegistrationByKey:output_type -> core.Registration - 41, // 146: sa.StorageAuthorityReadOnly.GetRevocationStatus:output_type -> sa.RevocationStatus - 69, // 147: sa.StorageAuthorityReadOnly.GetRevokedCertsByShard:output_type -> core.CRLEntry + 63, // 138: sa.StorageAuthorityReadOnly.GetAuthorization2:output_type -> core.Authorization + 67, // 139: sa.StorageAuthorityReadOnly.GetCertificate:output_type -> core.Certificate + 67, // 140: sa.StorageAuthorityReadOnly.GetLintPrecertificate:output_type -> core.Certificate + 68, // 141: sa.StorageAuthorityReadOnly.GetCertificateStatus:output_type -> core.CertificateStatus + 69, // 142: sa.StorageAuthorityReadOnly.GetOrder:output_type -> core.Order + 69, // 143: sa.StorageAuthorityReadOnly.GetOrderForNames:output_type -> core.Order + 66, // 144: sa.StorageAuthorityReadOnly.GetRegistration:output_type -> core.Registration + 66, // 145: sa.StorageAuthorityReadOnly.GetRegistrationByKey:output_type -> core.Registration + 42, // 146: sa.StorageAuthorityReadOnly.GetRevocationStatus:output_type -> sa.RevocationStatus + 70, // 147: sa.StorageAuthorityReadOnly.GetRevokedCertsByShard:output_type -> core.CRLEntry 6, // 148: sa.StorageAuthorityReadOnly.GetSerialMetadata:output_type -> sa.SerialMetadata 5, // 149: sa.StorageAuthorityReadOnly.GetSerialsMetadata:output_type -> sa.SerialsMetadata 3, // 150: sa.StorageAuthorityReadOnly.GetSerialsByAccount:output_type -> sa.Serial @@ -4492,26 +4554,26 @@ var file_sa_proto_depIdxs = []int32{ 25, // 152: sa.StorageAuthorityReadOnly.GetValidAuthorizations2:output_type -> sa.Authorizations 25, // 153: sa.StorageAuthorityReadOnly.GetValidOrderAuthorizations2:output_type -> sa.Authorizations 25, // 154: sa.StorageAuthorityReadOnly.GetOrderAuthorizations:output_type -> sa.Authorizations - 32, // 155: sa.StorageAuthorityReadOnly.IncidentsForSerial:output_type -> sa.Incidents + 33, // 155: sa.StorageAuthorityReadOnly.IncidentsForSerial:output_type -> sa.Incidents 12, // 156: sa.StorageAuthorityReadOnly.KeyBlocked:output_type -> sa.Exists - 32, // 157: sa.StorageAuthorityReadOnly.ListIncidents:output_type -> sa.Incidents + 33, // 157: sa.StorageAuthorityReadOnly.ListIncidents:output_type -> sa.Incidents 12, // 158: sa.StorageAuthorityReadOnly.ReplacementOrderExists:output_type -> sa.Exists - 39, // 159: sa.StorageAuthorityReadOnly.SerialsForIncident:output_type -> sa.IncidentSerial - 45, // 160: sa.StorageAuthorityReadOnly.CheckIdentifiersPaused:output_type -> sa.Identifiers - 45, // 161: sa.StorageAuthorityReadOnly.GetPausedIdentifiers:output_type -> sa.Identifiers - 55, // 162: sa.StorageAuthorityReadOnly.GetRateLimitOverride:output_type -> sa.RateLimitOverrideResponse - 55, // 163: sa.StorageAuthorityReadOnly.GetEnabledRateLimitOverrides:output_type -> sa.RateLimitOverrideResponse + 40, // 159: sa.StorageAuthorityReadOnly.SerialsForIncident:output_type -> sa.IncidentSerial + 46, // 160: sa.StorageAuthorityReadOnly.CheckIdentifiersPaused:output_type -> sa.Identifiers + 46, // 161: sa.StorageAuthorityReadOnly.GetPausedIdentifiers:output_type -> sa.Identifiers + 56, // 162: sa.StorageAuthorityReadOnly.GetRateLimitOverride:output_type -> sa.RateLimitOverrideResponse + 56, // 163: sa.StorageAuthorityReadOnly.GetEnabledRateLimitOverrides:output_type -> sa.RateLimitOverrideResponse 9, // 164: sa.StorageAuthority.FQDNSetTimestampsForWindow:output_type -> sa.Timestamps - 62, // 165: sa.StorageAuthority.GetAuthorization2:output_type -> core.Authorization - 66, // 166: sa.StorageAuthority.GetCertificate:output_type -> core.Certificate - 66, // 167: sa.StorageAuthority.GetLintPrecertificate:output_type -> core.Certificate - 67, // 168: sa.StorageAuthority.GetCertificateStatus:output_type -> core.CertificateStatus - 68, // 169: sa.StorageAuthority.GetOrder:output_type -> core.Order - 68, // 170: sa.StorageAuthority.GetOrderForNames:output_type -> core.Order - 65, // 171: sa.StorageAuthority.GetRegistration:output_type -> core.Registration - 65, // 172: sa.StorageAuthority.GetRegistrationByKey:output_type -> core.Registration - 41, // 173: sa.StorageAuthority.GetRevocationStatus:output_type -> sa.RevocationStatus - 69, // 174: sa.StorageAuthority.GetRevokedCertsByShard:output_type -> core.CRLEntry + 63, // 165: sa.StorageAuthority.GetAuthorization2:output_type -> core.Authorization + 67, // 166: sa.StorageAuthority.GetCertificate:output_type -> core.Certificate + 67, // 167: sa.StorageAuthority.GetLintPrecertificate:output_type -> core.Certificate + 68, // 168: sa.StorageAuthority.GetCertificateStatus:output_type -> core.CertificateStatus + 69, // 169: sa.StorageAuthority.GetOrder:output_type -> core.Order + 69, // 170: sa.StorageAuthority.GetOrderForNames:output_type -> core.Order + 66, // 171: sa.StorageAuthority.GetRegistration:output_type -> core.Registration + 66, // 172: sa.StorageAuthority.GetRegistrationByKey:output_type -> core.Registration + 42, // 173: sa.StorageAuthority.GetRevocationStatus:output_type -> sa.RevocationStatus + 70, // 174: sa.StorageAuthority.GetRevokedCertsByShard:output_type -> core.CRLEntry 6, // 175: sa.StorageAuthority.GetSerialMetadata:output_type -> sa.SerialMetadata 5, // 176: sa.StorageAuthority.GetSerialsMetadata:output_type -> sa.SerialsMetadata 3, // 177: sa.StorageAuthority.GetSerialsByAccount:output_type -> sa.Serial @@ -4519,44 +4581,44 @@ var file_sa_proto_depIdxs = []int32{ 25, // 179: sa.StorageAuthority.GetValidAuthorizations2:output_type -> sa.Authorizations 25, // 180: sa.StorageAuthority.GetValidOrderAuthorizations2:output_type -> sa.Authorizations 25, // 181: sa.StorageAuthority.GetOrderAuthorizations:output_type -> sa.Authorizations - 32, // 182: sa.StorageAuthority.IncidentsForSerial:output_type -> sa.Incidents + 33, // 182: sa.StorageAuthority.IncidentsForSerial:output_type -> sa.Incidents 12, // 183: sa.StorageAuthority.KeyBlocked:output_type -> sa.Exists - 32, // 184: sa.StorageAuthority.ListIncidents:output_type -> sa.Incidents + 33, // 184: sa.StorageAuthority.ListIncidents:output_type -> sa.Incidents 12, // 185: sa.StorageAuthority.ReplacementOrderExists:output_type -> sa.Exists - 39, // 186: sa.StorageAuthority.SerialsForIncident:output_type -> sa.IncidentSerial - 45, // 187: sa.StorageAuthority.CheckIdentifiersPaused:output_type -> sa.Identifiers - 45, // 188: sa.StorageAuthority.GetPausedIdentifiers:output_type -> sa.Identifiers - 55, // 189: sa.StorageAuthority.GetRateLimitOverride:output_type -> sa.RateLimitOverrideResponse - 55, // 190: sa.StorageAuthority.GetEnabledRateLimitOverrides:output_type -> sa.RateLimitOverrideResponse - 64, // 191: sa.StorageAuthority.AddBlockedKey:output_type -> google.protobuf.Empty - 64, // 192: sa.StorageAuthority.AddCertificate:output_type -> google.protobuf.Empty - 64, // 193: sa.StorageAuthority.AddPrecertificate:output_type -> google.protobuf.Empty - 64, // 194: sa.StorageAuthority.AddSerial:output_type -> google.protobuf.Empty - 64, // 195: sa.StorageAuthority.DeactivateAuthorization2:output_type -> google.protobuf.Empty - 65, // 196: sa.StorageAuthority.DeactivateRegistration:output_type -> core.Registration - 64, // 197: sa.StorageAuthority.FinalizeAuthorization2:output_type -> google.protobuf.Empty - 64, // 198: sa.StorageAuthority.FinalizeOrder:output_type -> google.protobuf.Empty - 64, // 199: sa.StorageAuthority.FinalizeMTCOrder:output_type -> google.protobuf.Empty - 68, // 200: sa.StorageAuthority.NewOrderAndAuthzs:output_type -> core.Order - 65, // 201: sa.StorageAuthority.NewRegistration:output_type -> core.Registration - 64, // 202: sa.StorageAuthority.RevokeCertificate:output_type -> google.protobuf.Empty - 64, // 203: sa.StorageAuthority.SetAuthzProcessing:output_type -> google.protobuf.Empty - 64, // 204: sa.StorageAuthority.SetOrderError:output_type -> google.protobuf.Empty - 64, // 205: sa.StorageAuthority.SetOrderProcessing:output_type -> google.protobuf.Empty - 65, // 206: sa.StorageAuthority.UpdateRegistrationKey:output_type -> core.Registration - 64, // 207: sa.StorageAuthority.UpdateRevokedCertificate:output_type -> google.protobuf.Empty - 43, // 208: sa.StorageAuthority.LeaseCRLShard:output_type -> sa.LeaseCRLShardResponse - 64, // 209: sa.StorageAuthority.UpdateCRLShard:output_type -> google.protobuf.Empty - 47, // 210: sa.StorageAuthority.PauseIdentifiers:output_type -> sa.PauseIdentifiersResponse + 40, // 186: sa.StorageAuthority.SerialsForIncident:output_type -> sa.IncidentSerial + 46, // 187: sa.StorageAuthority.CheckIdentifiersPaused:output_type -> sa.Identifiers + 46, // 188: sa.StorageAuthority.GetPausedIdentifiers:output_type -> sa.Identifiers + 56, // 189: sa.StorageAuthority.GetRateLimitOverride:output_type -> sa.RateLimitOverrideResponse + 56, // 190: sa.StorageAuthority.GetEnabledRateLimitOverrides:output_type -> sa.RateLimitOverrideResponse + 65, // 191: sa.StorageAuthority.AddBlockedKey:output_type -> google.protobuf.Empty + 65, // 192: sa.StorageAuthority.AddCertificate:output_type -> google.protobuf.Empty + 65, // 193: sa.StorageAuthority.AddPrecertificate:output_type -> google.protobuf.Empty + 65, // 194: sa.StorageAuthority.AddSerial:output_type -> google.protobuf.Empty + 65, // 195: sa.StorageAuthority.DeactivateAuthorization2:output_type -> google.protobuf.Empty + 66, // 196: sa.StorageAuthority.DeactivateRegistration:output_type -> core.Registration + 65, // 197: sa.StorageAuthority.FinalizeAuthorization2:output_type -> google.protobuf.Empty + 65, // 198: sa.StorageAuthority.FinalizeOrder:output_type -> google.protobuf.Empty + 65, // 199: sa.StorageAuthority.FinalizeMTCOrder:output_type -> google.protobuf.Empty + 69, // 200: sa.StorageAuthority.NewOrderAndAuthzs:output_type -> core.Order + 66, // 201: sa.StorageAuthority.NewRegistration:output_type -> core.Registration + 65, // 202: sa.StorageAuthority.RevokeCertificate:output_type -> google.protobuf.Empty + 65, // 203: sa.StorageAuthority.SetAuthzProcessing:output_type -> google.protobuf.Empty + 65, // 204: sa.StorageAuthority.SetOrderError:output_type -> google.protobuf.Empty + 65, // 205: sa.StorageAuthority.SetOrderProcessing:output_type -> google.protobuf.Empty + 66, // 206: sa.StorageAuthority.UpdateRegistrationKey:output_type -> core.Registration + 65, // 207: sa.StorageAuthority.UpdateRevokedCertificate:output_type -> google.protobuf.Empty + 44, // 208: sa.StorageAuthority.LeaseCRLShard:output_type -> sa.LeaseCRLShardResponse + 65, // 209: sa.StorageAuthority.UpdateCRLShard:output_type -> google.protobuf.Empty + 48, // 210: sa.StorageAuthority.PauseIdentifiers:output_type -> sa.PauseIdentifiersResponse 8, // 211: sa.StorageAuthority.UnpauseAccount:output_type -> sa.Count - 51, // 212: sa.StorageAuthority.AddRateLimitOverride:output_type -> sa.AddRateLimitOverrideResponse - 64, // 213: sa.StorageAuthority.DisableRateLimitOverride:output_type -> google.protobuf.Empty - 64, // 214: sa.StorageAuthority.EnableRateLimitOverride:output_type -> google.protobuf.Empty - 57, // 215: sa.StorageAuthority.RevokeAuthorizationsFor:output_type -> sa.RevokeAuthorizationsForResponse - 69, // 216: sa.StorageAuthority.GetLatestRevokedCertByShard:output_type -> core.CRLEntry - 31, // 217: sa.StorageAuthorityAdmin.CreateIncident:output_type -> sa.Incident - 31, // 218: sa.StorageAuthorityAdmin.UpdateIncident:output_type -> sa.Incident - 64, // 219: sa.StorageAuthorityAdmin.AddSerialsToIncident:output_type -> google.protobuf.Empty + 52, // 212: sa.StorageAuthority.AddRateLimitOverride:output_type -> sa.AddRateLimitOverrideResponse + 65, // 213: sa.StorageAuthority.DisableRateLimitOverride:output_type -> google.protobuf.Empty + 65, // 214: sa.StorageAuthority.EnableRateLimitOverride:output_type -> google.protobuf.Empty + 58, // 215: sa.StorageAuthority.RevokeAuthorizationsFor:output_type -> sa.RevokeAuthorizationsForResponse + 70, // 216: sa.StorageAuthority.GetLatestRevokedCertByShard:output_type -> core.CRLEntry + 32, // 217: sa.StorageAuthorityAdmin.CreateIncident:output_type -> sa.Incident + 32, // 218: sa.StorageAuthorityAdmin.UpdateIncident:output_type -> sa.Incident + 65, // 219: sa.StorageAuthorityAdmin.AddSerialsToIncident:output_type -> google.protobuf.Empty 137, // [137:220] is the sub-list for method output_type 54, // [54:137] is the sub-list for method input_type 54, // [54:54] is the sub-list for extension type_name @@ -4569,8 +4631,8 @@ func file_sa_proto_init() { if File_sa_proto != nil { return } - file_sa_proto_msgTypes[35].OneofWrappers = []any{} - file_sa_proto_msgTypes[36].OneofWrappers = []any{ + file_sa_proto_msgTypes[36].OneofWrappers = []any{} + file_sa_proto_msgTypes[37].OneofWrappers = []any{ (*AddSerialsToIncidentRequest_Metadata)(nil), (*AddSerialsToIncidentRequest_Batch)(nil), } @@ -4580,7 +4642,7 @@ func file_sa_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_sa_proto_rawDesc), len(file_sa_proto_rawDesc)), NumEnums: 0, - NumMessages: 58, + NumMessages: 59, NumExtensions: 0, NumServices: 3, }, diff --git a/sa/proto/sa.proto b/sa/proto/sa.proto index e1077788c31..b8c287c51ed 100644 --- a/sa/proto/sa.proto +++ b/sa/proto/sa.proto @@ -83,7 +83,7 @@ service StorageAuthority { rpc NewOrderAndAuthzs(NewOrderAndAuthzsRequest) returns (core.Order) {} rpc NewRegistration(core.Registration) returns (core.Registration) {} rpc RevokeCertificate(RevokeCertificateRequest) returns (google.protobuf.Empty) {} - rpc SetAuthzProcessing(AuthorizationID2) returns (google.protobuf.Empty) {} + rpc SetAuthzProcessing(SetAuthzProcessingRequest) returns (google.protobuf.Empty) {} rpc SetOrderError(SetOrderErrorRequest) returns (google.protobuf.Empty) {} rpc SetOrderProcessing(OrderRequest) returns (google.protobuf.Empty) {} rpc UpdateRegistrationKey(UpdateRegistrationKeyRequest) returns (core.Registration) {} @@ -308,6 +308,14 @@ message AuthorizationID2 { int64 id = 1; } +message SetAuthzProcessingRequest { + // Next unused field number: 3 + int64 id = 1; + // The type of the challenge being validated. Stored in the authz's + // "attempted" column, so that the challenge can be rendered as "processing". + string attempted = 2; +} + message RevokeCertificateRequest { // Next unused field number: 10 string serial = 1; diff --git a/sa/proto/sa_grpc.pb.go b/sa/proto/sa_grpc.pb.go index 9e3c34d3242..2491ac4c759 100644 --- a/sa/proto/sa_grpc.pb.go +++ b/sa/proto/sa_grpc.pb.go @@ -1234,7 +1234,7 @@ type StorageAuthorityClient interface { NewOrderAndAuthzs(ctx context.Context, in *NewOrderAndAuthzsRequest, opts ...grpc.CallOption) (*proto.Order, error) NewRegistration(ctx context.Context, in *proto.Registration, opts ...grpc.CallOption) (*proto.Registration, error) RevokeCertificate(ctx context.Context, in *RevokeCertificateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - SetAuthzProcessing(ctx context.Context, in *AuthorizationID2, opts ...grpc.CallOption) (*emptypb.Empty, error) + SetAuthzProcessing(ctx context.Context, in *SetAuthzProcessingRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) SetOrderError(ctx context.Context, in *SetOrderErrorRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) SetOrderProcessing(ctx context.Context, in *OrderRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) UpdateRegistrationKey(ctx context.Context, in *UpdateRegistrationKeyRequest, opts ...grpc.CallOption) (*proto.Registration, error) @@ -1694,7 +1694,7 @@ func (c *storageAuthorityClient) RevokeCertificate(ctx context.Context, in *Revo return out, nil } -func (c *storageAuthorityClient) SetAuthzProcessing(ctx context.Context, in *AuthorizationID2, opts ...grpc.CallOption) (*emptypb.Empty, error) { +func (c *storageAuthorityClient) SetAuthzProcessing(ctx context.Context, in *SetAuthzProcessingRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) err := c.cc.Invoke(ctx, StorageAuthority_SetAuthzProcessing_FullMethodName, in, out, cOpts...) @@ -1881,7 +1881,7 @@ type StorageAuthorityServer interface { NewOrderAndAuthzs(context.Context, *NewOrderAndAuthzsRequest) (*proto.Order, error) NewRegistration(context.Context, *proto.Registration) (*proto.Registration, error) RevokeCertificate(context.Context, *RevokeCertificateRequest) (*emptypb.Empty, error) - SetAuthzProcessing(context.Context, *AuthorizationID2) (*emptypb.Empty, error) + SetAuthzProcessing(context.Context, *SetAuthzProcessingRequest) (*emptypb.Empty, error) SetOrderError(context.Context, *SetOrderErrorRequest) (*emptypb.Empty, error) SetOrderProcessing(context.Context, *OrderRequest) (*emptypb.Empty, error) UpdateRegistrationKey(context.Context, *UpdateRegistrationKeyRequest) (*proto.Registration, error) @@ -2023,7 +2023,7 @@ func (UnimplementedStorageAuthorityServer) NewRegistration(context.Context, *pro func (UnimplementedStorageAuthorityServer) RevokeCertificate(context.Context, *RevokeCertificateRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method RevokeCertificate not implemented") } -func (UnimplementedStorageAuthorityServer) SetAuthzProcessing(context.Context, *AuthorizationID2) (*emptypb.Empty, error) { +func (UnimplementedStorageAuthorityServer) SetAuthzProcessing(context.Context, *SetAuthzProcessingRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SetAuthzProcessing not implemented") } func (UnimplementedStorageAuthorityServer) SetOrderError(context.Context, *SetOrderErrorRequest) (*emptypb.Empty, error) { @@ -2754,7 +2754,7 @@ func _StorageAuthority_RevokeCertificate_Handler(srv interface{}, ctx context.Co } func _StorageAuthority_SetAuthzProcessing_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AuthorizationID2) + in := new(SetAuthzProcessingRequest) if err := dec(in); err != nil { return nil, err } @@ -2766,7 +2766,7 @@ func _StorageAuthority_SetAuthzProcessing_Handler(srv interface{}, ctx context.C FullMethod: StorageAuthority_SetAuthzProcessing_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(StorageAuthorityServer).SetAuthzProcessing(ctx, req.(*AuthorizationID2)) + return srv.(StorageAuthorityServer).SetAuthzProcessing(ctx, req.(*SetAuthzProcessingRequest)) } return interceptor(ctx, in, info, handler) } diff --git a/sa/sa.go b/sa/sa.go index 55163d91381..8508e1acae3 100644 --- a/sa/sa.go +++ b/sa/sa.go @@ -664,21 +664,41 @@ func containsDuplicates(ids []int64) bool { return false } -// SetAuthzProcessing sets the "beganProcessing" bool for an authorization. -// This does not affect its public-facing status (unlike orders, authzs do not -// have an RFC 8555 "processing" state), but does prevent further requests to -// the challenge endpoint from kicking off parallel validation attempts. -func (ssa *SQLStorageAuthority) SetAuthzProcessing(ctx context.Context, req *sapb.AuthorizationID2) (*emptypb.Empty, error) { +// SetAuthzProcessing sets the "beganProcessing" bool for an authorization, and +// records the type of the challenge being validated in its "attempted" column. +// This does not affect the authorization's public-facing status (unlike orders, +// authzs do not have an RFC 8555 "processing" state), but it does prevent +// further requests to the challenge endpoint from kicking off parallel +// validation attempts, and it allows the attempted challenge to be rendered +// with the RFC 8555 "processing" status while the authorization is pending. +func (ssa *SQLStorageAuthority) SetAuthzProcessing(ctx context.Context, req *sapb.SetAuthzProcessingRequest) (*emptypb.Empty, error) { if req.Id == 0 { return nil, errIncompleteRequest } + // The attempted challenge type is optional, so that an RA which predates + // this field can still mark authorizations as processing. A pending authz + // which has not begun processing never has an attempted challenge, so + // leaving the column NULL in that case is a no-op. + // TODO(#9032): Require req.Attempted when the SetAuthzProcessing flag is + // removed, since every RA will then set it. + var attempted *uint8 + if req.Attempted != "" { + attemptedType, ok := challTypeToUint[req.Attempted] + if !ok { + return nil, berrors.MalformedError("unrecognized challenge type %q", req.Attempted) + } + attempted = &attemptedType + } + result, err := ssa.dbMap.ExecContext(ctx, ` UPDATE authz2 - SET beganProcessing = ? + SET beganProcessing = ?, + attempted = ? WHERE id = ? AND beganProcessing = ?`, true, + attempted, req.Id, false, ) diff --git a/sa/sa_test.go b/sa/sa_test.go index 7ce5e9f06c8..56d91bbeba7 100644 --- a/sa/sa_test.go +++ b/sa/sa_test.go @@ -1345,19 +1345,85 @@ func TestSetAuthzProcessing(t *testing.T) { authzID := createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.com"), expires) // Set the authz to processing - _, err := sa.SetAuthzProcessing(t.Context(), &sapb.AuthorizationID2{Id: authzID}) + _, err := sa.SetAuthzProcessing(t.Context(), &sapb.SetAuthzProcessingRequest{Id: authzID, Attempted: string(core.ChallengeTypeHTTP01)}) if err != nil { t.Fatalf("SetAuthzProcessing = %q, but want success", err) } + // The authz should still be pending, but the attempted challenge should + // now be rendered as processing. + authz, err := sa.GetAuthorization2(t.Context(), &sapb.AuthorizationID2{Id: authzID}) + if err != nil { + t.Fatalf("GetAuthorization2 = %q, but want success", err) + } + if authz.Status != string(core.StatusPending) { + t.Errorf("authz.Status = %q, but want %q", authz.Status, core.StatusPending) + } + if len(authz.Challenges) != 1 { + t.Fatalf("len(authz.Challenges) = %d, but want 1", len(authz.Challenges)) + } + if authz.Challenges[0].Type != string(core.ChallengeTypeHTTP01) { + t.Errorf("authz.Challenges[0].Type = %q, but want %q", authz.Challenges[0].Type, core.ChallengeTypeHTTP01) + } + if authz.Challenges[0].Status != string(core.StatusProcessing) { + t.Errorf("authz.Challenges[0].Status = %q, but want %q", authz.Challenges[0].Status, core.StatusProcessing) + } + // Try to set the same authz to be processing again. We should get an error. - _, err = sa.SetAuthzProcessing(context.Background(), &sapb.AuthorizationID2{Id: authzID}) + _, err = sa.SetAuthzProcessing(context.Background(), &sapb.SetAuthzProcessingRequest{Id: authzID, Attempted: string(core.ChallengeTypeHTTP01)}) if err == nil { t.Fatal("SetAuthzProcessing again succeeded, but want error") } if !errors.Is(err, berrors.Conflict) { t.Errorf("SetAuthzProcessing = %T, but want berrors.Conflict", err) } + + // Finalizing the authz should still work, and should replace the + // processing status with the final one. + _, err = sa.FinalizeAuthorization2(t.Context(), &sapb.FinalizeAuthorizationRequest{ + Id: authzID, + Status: string(core.StatusValid), + Expires: timestamppb.New(expires), + Attempted: string(core.ChallengeTypeHTTP01), + AttemptedAt: timestamppb.New(fc.Now()), + }) + if err != nil { + t.Fatalf("FinalizeAuthorization2 = %q, but want success", err) + } + authz, err = sa.GetAuthorization2(t.Context(), &sapb.AuthorizationID2{Id: authzID}) + if err != nil { + t.Fatalf("GetAuthorization2 = %q, but want success", err) + } + if authz.Status != string(core.StatusValid) { + t.Errorf("authz.Status = %q, but want %q", authz.Status, core.StatusValid) + } + if authz.Challenges[0].Status != string(core.StatusValid) { + t.Errorf("authz.Challenges[0].Status = %q, but want %q", authz.Challenges[0].Status, core.StatusValid) + } + + // An unrecognized challenge type should be rejected. + authzID = createPendingAuthorization(t, sa, reg.Id, identifier.NewDNS("example.net"), expires) + _, err = sa.SetAuthzProcessing(t.Context(), &sapb.SetAuthzProcessingRequest{Id: authzID, Attempted: "bogus-01"}) + if err == nil { + t.Fatal("SetAuthzProcessing with bogus challenge type succeeded, but want error") + } + if !errors.Is(err, berrors.Malformed) { + t.Errorf("SetAuthzProcessing = %T, but want berrors.Malformed", err) + } + + // An omitted challenge type (as sent by an RA which predates the field) + // should still mark the authz as processing, leaving its challenges pending. + _, err = sa.SetAuthzProcessing(t.Context(), &sapb.SetAuthzProcessingRequest{Id: authzID}) + if err != nil { + t.Fatalf("SetAuthzProcessing without attempted = %q, but want success", err) + } + authz, err = sa.GetAuthorization2(t.Context(), &sapb.AuthorizationID2{Id: authzID}) + if err != nil { + t.Fatalf("GetAuthorization2 = %q, but want success", err) + } + if authz.Challenges[0].Status != string(core.StatusPending) { + t.Errorf("authz.Challenges[0].Status = %q, but want %q", authz.Challenges[0].Status, core.StatusPending) + } } func TestSetOrderProcessing(t *testing.T) { diff --git a/test/inmem/sa/sa.go b/test/inmem/sa/sa.go index f20e786ba99..427e648d882 100644 --- a/test/inmem/sa/sa.go +++ b/test/inmem/sa/sa.go @@ -69,6 +69,10 @@ func (sa SA) SetOrderError(ctx context.Context, req *sapb.SetOrderErrorRequest, return sa.Impl.SetOrderError(ctx, req) } +func (sa SA) SetAuthzProcessing(ctx context.Context, req *sapb.SetAuthzProcessingRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { + return sa.Impl.SetAuthzProcessing(ctx, req) +} + func (sa SA) SetOrderProcessing(ctx context.Context, req *sapb.OrderRequest, _ ...grpc.CallOption) (*emptypb.Empty, error) { return sa.Impl.SetOrderProcessing(ctx, req) } diff --git a/wfe2/wfe_test.go b/wfe2/wfe_test.go index 63ef8abdb65..6415167e5cf 100644 --- a/wfe2/wfe_test.go +++ b/wfe2/wfe_test.go @@ -280,6 +280,17 @@ func (ra *MockRegistrationAuthority) GetAuthorization(_ context.Context, in *rap {Id: 3, Type: "tls-alpn-01", Status: string(core.StatusPending), Token: "token"}, }, }, nil + case 6: // Return a pending authorization with a single processing challenge. + return &corepb.Authorization{ + Id: 6, + RegistrationID: 1, + Identifier: identifier.NewDNS("not-an-example.com").ToProto(), + Status: string(core.StatusPending), + Expires: timestamppb.New(ra.clk.Now().AddDate(100, 0, 0)), + Challenges: []*corepb.Challenge{ + {Id: 1, Type: "http-01", Status: string(core.StatusProcessing), Token: "token"}, + }, + }, nil } return nil, berrors.NotFoundError("no authorization found with id %d", in.Id) @@ -1277,6 +1288,16 @@ func TestChallengeHandler(t *testing.T) { ExpectedStatus: http.StatusOK, ExpectedBody: `{"status": "valid", "type":"http-01", "token":"token", "url": "http://localhost/acme/chall/1/1/7TyhFQ"}`, }, + { + Name: "Processing challenge POST-as-GET", + Request: postAsGet(1, "1/6/7TyhFQ", ""), + ExpectedStatus: http.StatusOK, + ExpectedHeaders: map[string]string{ + "Location": "http://localhost/acme/chall/1/6/7TyhFQ", + "Link": `;rel="up"`, + }, + ExpectedBody: `{"status": "processing", "type":"http-01", "token":"token", "url": "http://localhost/acme/chall/1/6/7TyhFQ"}`, + }, } for _, tc := range testCases { @@ -1841,6 +1862,33 @@ func TestGetAuthorizationHandler(t *testing.T) { } ] }`) + + // Ensure that an authorization whose challenge is being validated is + // rendered as pending, with the challenge rendered as processing. + _, _, jwsBody = signer.byKeyID(1, nil, "http://localhost/1/6", "") + postAsGet = makePostRequestWithPath("1/6", jwsBody) + + responseWriter = httptest.NewRecorder() + wfe.AuthorizationHandler(ctx, newRequestEvent(), responseWriter, postAsGet) + test.AssertEquals(t, responseWriter.Code, http.StatusOK) + body = responseWriter.Body.String() + test.AssertUnmarshaledEquals(t, body, ` + { + "identifier": { + "type": "dns", + "value": "not-an-example.com" + }, + "status": "pending", + "expires": "2070-01-01T00:00:00Z", + "challenges": [ + { + "status": "processing", + "type": "http-01", + "token":"token", + "url": "http://localhost/acme/chall/1/6/7TyhFQ" + } + ] + }`) } // TestAuthorizationHandler500 tests that internal errors on GetAuthorization result in