From bbe8f2c1b70ce7ed22fe3b472733d43c9e957707 Mon Sep 17 00:00:00 2001 From: Bhaskar Gurram Date: Sat, 10 Oct 2026 19:49:19 +0000 Subject: [PATCH 1/3] ra/sa: render the attempted challenge as "processing" RFC 8555 Section 7.1.6 says a challenge transitions from "pending" to "processing" once the client responds to it. Boulder never rendered that state: because challenges are stored as part of the authorization rather than as separate objects, a challenge stayed "pending" until the authorization was finalized as "valid" or "invalid". SetAuthzProcessing (added in #8838) already marks the authz's beganProcessing column before dispatching validation, but that column has no effect on the rendered objects. Extend it so that the RA also tells the SA which challenge is being validated: - Replace the SetAuthzProcessing request type with a dedicated SetAuthzProcessingRequest carrying the authz ID and the attempted challenge type. Field 1 is unchanged, so the RPC remains wire-compatible with the old AuthorizationID2 request in both directions. - Have the SA write the attempted challenge type into the authz2 "attempted" column in the same UPDATE that sets beganProcessing. The field is optional, so an RA which predates it still works. - When modelToAuthzPB sees an attempted challenge on an authz which is still pending, render that challenge with status "processing" (and no validation results) while leaving the authz status "pending", since RFC 8555 has no "processing" status for authorizations. - Have the RA reject a second attempt at a challenge which is already processing with the same "conflict" error the SA returns, rather than letting CheckPending turn it into a "malformed" error. - Forward SetAuthzProcessing in the in-memory SA used by the RA tests, which previously did not implement it. All of the new behavior remains gated behind the existing SetAuthzProcessing feature flag, so nothing changes until the flag is enabled in the RA. Fixes #9032 --- ra/ra.go | 14 +- ra/ra_test.go | 111 +++ sa/model.go | 29 +- sa/model_test.go | 42 + sa/proto/sa.pb.go | 1850 +++++++++++++++++++++------------------- sa/proto/sa.proto | 10 +- sa/proto/sa_grpc.pb.go | 12 +- sa/sa.go | 30 +- sa/sa_test.go | 70 +- test/inmem/sa/sa.go | 4 + wfe2/wfe_test.go | 48 ++ 11 files changed, 1299 insertions(+), 921 deletions(-) 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..5eb91cbd099 100644 --- a/sa/model.go +++ b/sa/model.go @@ -818,7 +818,9 @@ func modelToAuthzPB(am authzModel) (*corepb.Authorization, error) { // 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. + // fields. If the authorization itself is still pending, the attempted + // challenge is the one currently being validated, so we set its status to + // core.StatusProcessing instead. for pos := range uint8(8) { if (am.Challenges>>pos)&1 == 1 { challType := uintToChallType[pos] @@ -833,16 +835,23 @@ func modelToAuthzPB(am authzModel) (*corepb.Authorization, error) { // 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 + 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) + } else { + 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 } - // 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 { diff --git a/sa/model_test.go b/sa/model_test.go index 8b7bdcd4d45..7424fe757e6 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..2db365af623 100644 --- a/sa/sa.go +++ b/sa/sa.go @@ -664,21 +664,39 @@ 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. + 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 From d96e0d4202da6a7d663c0709aae020498522055c Mon Sep 17 00:00:00 2001 From: Bhaskar Gurram Date: Tue, 20 Oct 2026 01:51:51 +0000 Subject: [PATCH 2/3] sa: render only the attempted challenge once one is attempted Address review feedback: when the authz has an attempted challenge, populate that single challenge directly instead of checking every challenge type in the bitmap loop. Move the "processing" check and the validated time into populateAttemptedFields, and add a TODO to require the attempted challenge type once the SetAuthzProcessing flag is removed. --- sa/model.go | 90 +++++++++++++++++++++++------------------------- sa/model_test.go | 2 ++ sa/sa.go | 2 ++ 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/sa/model.go b/sa/model.go index 5eb91cbd099..c0d6f6b0d00 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,53 +827,35 @@ 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. If the authorization itself is still pending, the attempted - // challenge is the one currently being validated, so we set its status to - // core.StatusProcessing instead. - for pos := range uint8(8) { - if (am.Challenges>>pos)&1 == 1 { - challType := uintToChallType[pos] + // 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 { + if (am.Challenges>>*am.Attempted)&1 == 1 { challenge := &corepb.Challenge{ - Type: challType, - Status: string(core.StatusPending), - Token: base64.RawURLEncoding.EncodeToString(am.Token), + Type: uintToChallType[*am.Attempted], + Token: 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 { - 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) - } else { - 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) + 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 7424fe757e6..6c083fcaaa8 100644 --- a/sa/model_test.go +++ b/sa/model_test.go @@ -328,12 +328,14 @@ func TestPopulateAttemptedFieldsBadJSON(t *testing.T) { { Name: "Bad validation error field", Model: &authzModel{ + Status: statusUint(core.StatusInvalid), ValidationError: badJSON, }, }, { Name: "Bad validation record field", Model: &authzModel{ + Status: statusUint(core.StatusInvalid), ValidationRecord: badJSON, }, }, diff --git a/sa/sa.go b/sa/sa.go index 2db365af623..8508e1acae3 100644 --- a/sa/sa.go +++ b/sa/sa.go @@ -680,6 +680,8 @@ func (ssa *SQLStorageAuthority) SetAuthzProcessing(ctx context.Context, req *sap // 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] From ce3cfeb24c89d415b505209924fd33adbe37dac7 Mon Sep 17 00:00:00 2001 From: Bhaskar Gurram Date: Wed, 21 Oct 2026 01:48:03 +0000 Subject: [PATCH 3/3] sa: drop the redundant challenge bitmap check for the attempted challenge --- sa/model.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sa/model.go b/sa/model.go index c0d6f6b0d00..1c0ac0cb5f8 100644 --- a/sa/model.go +++ b/sa/model.go @@ -837,17 +837,15 @@ func modelToAuthzPB(am authzModel) (*corepb.Authorization, error) { // authorization model. token := base64.RawURLEncoding.EncodeToString(am.Token) if am.Attempted != nil { - if (am.Challenges>>*am.Attempted)&1 == 1 { - 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) + 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 {