Skip to content

Commit 6722e0e

Browse files
Some more tidying.
1 parent f7da26d commit 6722e0e

File tree

2 files changed

+83
-103
lines changed

2 files changed

+83
-103
lines changed

va/va_test.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -737,12 +737,6 @@ func TestLogRemoteDifferentials(t *testing.T) {
737737
}
738738
}
739739

740-
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
741-
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
742-
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
743-
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
744-
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
745-
746740
// setup returns an in-memory VA and a mock logger. The default resolver client
747741
// is MockClient{}, but can be overridden.
748742
func setupVA(srv *httptest.Server, ua string, rvas []RemoteVA, mockDNSClient bdns.Client) (*ValidationAuthorityImpl, *blog.Mock) {

va/vampic.go

Lines changed: 83 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,23 @@ func (va *ValidationAuthorityImpl) observeLatency(op, perspective, challType, pr
4747
va.metrics.validationLatency.With(labels).Observe(latency.Seconds())
4848
}
4949

50-
func (va *ValidationAuthorityImpl) onPrimaryVA() bool {
50+
// isPrimaryVA returns true if the VA is the primary validation perspective.
51+
func (va *ValidationAuthorityImpl) isPrimaryVA() bool {
5152
return va.perspective == PrimaryPerspective
5253
}
5354

5455
// mpicSummary contains multiple fields that are exported for logging purposes.
56+
// To initialize an empty mpicSummary, use newSummary(). The prepare a final
57+
// summary, use prepareSummary().
5558
type mpicSummary struct {
56-
// Passed is the list of distinct perspectives that Passed validation.
59+
// Passed are the distinct perspectives that Passed validation.
5760
Passed []string `json:"passedPerspectives"`
5861

59-
// Failed is the list of distinct perspectives that Failed validation.
62+
// Failed are the disctint perspectives that Failed validation.
6063
Failed []string `json:"failedPerspectives"`
6164

62-
// RIRs is the list of distinct RIRs that passing perspectives belonged to.
65+
// RIRs are the distinct Regional Internet Registries that passing
66+
// perspectives belonged to.
6367
RIRs []string `json:"passedRIRs"`
6468

6569
// QuorumResult is the Multi-Perspective Issuance Corroboration quorum
@@ -70,14 +74,10 @@ type mpicSummary struct {
7074
QuorumResult string `json:"quorumResult"`
7175
}
7276

73-
// newSummary returns a new mpicSummary with empty slices to avoid "null" in
74-
// JSON output.
77+
// newSummary returns a new mpicSummary with empty slices to avoid "null" in the
78+
// JSON output. Fields are exported for logging purposes.
7579
func newSummary() mpicSummary {
76-
return mpicSummary{
77-
Passed: []string{},
78-
Failed: []string{},
79-
RIRs: []string{},
80-
}
80+
return mpicSummary{[]string{}, []string{}, []string{}, ""}
8181
}
8282

8383
// prepareSummary prepares a summary of the validation results for logging
@@ -107,7 +107,7 @@ type validateChallengeAuditLog struct {
107107
Error string `json:",omitempty"`
108108
InternalError string `json:",omitempty"`
109109
Latency float64 `json:",omitempty"`
110-
MPICSummary mpicSummary `json:",omitempty"`
110+
MPICSummary mpicSummary
111111
}
112112

113113
// determineMaxAllowedFailures returns the maximum number of allowed failures
@@ -118,11 +118,11 @@ type validateChallengeAuditLog struct {
118118
// | --- | --- |
119119
// | 2-5 | 1 |
120120
// | 6+ | 2 |
121-
func determineMaxAllowedFailures(perspectives int) int {
122-
if perspectives < 2 {
121+
func determineMaxAllowedFailures(perspectiveCount int) int {
122+
if perspectiveCount < 2 {
123123
return 0
124124
}
125-
if perspectives < 6 {
125+
if perspectiveCount < 6 {
126126
return 1
127127
}
128128
return 2
@@ -133,70 +133,62 @@ func determineMaxAllowedFailures(perspectives int) int {
133133
// validation results and a problem if the validation failed. The summary is
134134
// mandatory and must be returned even if the validation failed.
135135
func (va *ValidationAuthorityImpl) remoteValidateChallenge(ctx context.Context, req *vapb.ValidationRequest) (mpicSummary, *probs.ProblemDetails) {
136+
// Mar 15, 2026: MUST implement using at least 3 perspectives
137+
// Jun 15, 2026: MUST implement using at least 4 perspectives
138+
// Dec 15, 2026: MUST implement using at least 5 perspectives
136139
remoteVACount := len(va.remoteVAs)
137140
if remoteVACount < 3 {
138141
return mpicSummary{}, probs.ServerInternal("Insufficient remote perspectives: need at least 3")
139142
}
140143

141-
type remoteResult struct {
142-
// rvaAddr is only used for logging.
143-
rvaAddr string
144-
response *vapb.ValidationResult
145-
err error
144+
type response struct {
145+
addr string
146+
result *vapb.ValidationResult
147+
err error
146148
}
147149

148-
responses := make(chan *remoteResult, remoteVACount)
150+
responses := make(chan *response, remoteVACount)
149151
for _, i := range rand.Perm(remoteVACount) {
150-
rva := va.remoteVAs[i]
151-
152152
go func(rva RemoteVA) {
153153
res, err := rva.ValidateChallenge(ctx, req)
154-
responses <- &remoteResult{
155-
rvaAddr: rva.Address,
156-
response: res,
157-
err: err,
158-
}
159-
}(rva)
154+
responses <- &response{rva.Address, res, err}
155+
}(va.remoteVAs[i])
160156
}
161157

162-
passed := []string{}
163-
failed := []string{}
158+
var passed []string
159+
var failed []string
164160
passedRIRs := make(map[string]struct{})
165161

166-
maxRemoteFailures := determineMaxAllowedFailures(remoteVACount)
167-
required := remoteVACount - maxRemoteFailures
168-
169162
var firstProb *probs.ProblemDetails
170163
for i := 0; i < remoteVACount; i++ {
171-
res := <-responses
164+
resp := <-responses
172165

173166
var currProb *probs.ProblemDetails
174-
if res.err != nil {
175-
// The remote VA failed to respond. With no response, we cannot know
176-
// the perspective name, so we use the remote VA address.
177-
failed = append(failed, res.rvaAddr)
178-
if errors.Is(res.err, context.Canceled) {
167+
if resp.err != nil {
168+
// Failed to communicate with the remote VA.
169+
failed = append(failed, resp.addr)
170+
if errors.Is(resp.err, context.Canceled) {
179171
currProb = probs.ServerInternal("Secondary domain validation RPC canceled")
180172
} else {
181-
va.log.Errf("Remote VA %q.ValidateChallenge failed: %s", res.rvaAddr, res.err)
173+
va.log.Errf("Remote VA %q.ValidateChallenge failed: %s", resp.addr, resp.err)
182174
currProb = probs.ServerInternal("Secondary domain validation RPC failed")
183175
}
184176

185-
} else if res.response.Problems != nil {
177+
} else if resp.result.Problems != nil {
186178
// The remote VA returned a problem.
187-
failed = append(failed, res.response.Perspective)
179+
failed = append(failed, resp.result.Perspective)
188180

189181
var err error
190-
currProb, err = bgrpc.PBToProblemDetails(res.response.Problems)
182+
currProb, err = bgrpc.PBToProblemDetails(resp.result.Problems)
191183
if err != nil {
192-
va.log.Errf("Remote VA %q.ValidateChallenge returned malformed problem: %s", res.rvaAddr, err)
184+
va.log.Errf("Remote VA %q.ValidateChallenge returned a malformed problem: %s", resp.addr, err)
193185
currProb = probs.ServerInternal("Secondary domain validation RPC returned malformed result")
194186
}
195187

196188
} else {
197-
// The remote VA returned a successful response.
198-
passed = append(passed, res.response.Perspective)
199-
passedRIRs[res.response.Rir] = struct{}{}
189+
// The remote VA returned a successful result.
190+
passed = append(passed, resp.result.Perspective)
191+
passedRIRs[resp.result.Rir] = struct{}{}
200192
}
201193

202194
if firstProb == nil && currProb != nil {
@@ -208,30 +200,36 @@ func (va *ValidationAuthorityImpl) remoteValidateChallenge(ctx context.Context,
208200
// Prepare the summary, this MUST be returned even if the validation failed.
209201
summary := prepareSummary(passed, failed, passedRIRs, remoteVACount)
210202

211-
if len(passed) >= required {
212-
// We may have enough successful responses.
213-
if len(passedRIRs) < 2 {
214-
if firstProb != nil {
215-
firstProb.Detail = fmt.Sprintf("During secondary domain validation: %s", firstProb.Detail)
216-
return summary, firstProb
217-
}
218-
return summary, probs.Unauthorized("Secondary domain validation failed to receive enough responses from disctinct RIRs")
203+
maxRemoteFailures := determineMaxAllowedFailures(remoteVACount)
204+
if len(failed) > maxRemoteFailures {
205+
// Too many failures to reach quorum.
206+
if firstProb != nil {
207+
firstProb.Detail = fmt.Sprintf("During secondary domain validation: %s", firstProb.Detail)
208+
return summary, firstProb
219209
}
220-
// We have enough successful responses from distinct perspectives.
221-
return summary, nil
210+
return summary, probs.ServerInternal("Secondary domain validation failed due to too many failures")
222211
}
223212

224-
if len(failed) > maxRemoteFailures {
225-
// We have too many failed responses.
213+
if len(passed) < (remoteVACount - maxRemoteFailures) {
214+
// Too few successful responses to reach quorum.
215+
if firstProb != nil {
216+
firstProb.Detail = fmt.Sprintf("During secondary domain validation: %s", firstProb.Detail)
217+
return summary, firstProb
218+
}
219+
return summary, probs.ServerInternal("Secondary domain validation failed due to insufficient successful responses")
220+
}
221+
222+
if len(passedRIRs) < 2 {
223+
// Too few successful responses from distinct RIRs to reach quorum.
226224
if firstProb != nil {
227225
firstProb.Detail = fmt.Sprintf("During secondary domain validation: %s", firstProb.Detail)
228226
return summary, firstProb
229227
}
228+
return summary, probs.Unauthorized("Secondary domain validation failed to receive enough corroborations from distinct RIRs")
230229
}
231-
// This return is unreachable because for any number of remote VAs (n),
232-
// either at least (n - maxFailures) perspectives pass, or more than
233-
// maxFailures fail. Thus, one of the above conditions is always satisfied.
234-
return summary, probs.ServerInternal("Secondary domain validation failed to receive all responses")
230+
231+
// Enough successful responses from distinct RIRs to reach quorum.
232+
return summary, nil
235233
}
236234

237235
// ValidateChallenge performs a local validation of a challenge using the
@@ -254,81 +252,69 @@ func (va *ValidationAuthorityImpl) ValidateChallenge(ctx context.Context, req *v
254252
return nil, berrors.MalformedError("challenge failed consistency check: %s", err)
255253
}
256254

257-
var prob *probs.ProblemDetails
258-
var localLatency time.Duration
259-
var latency time.Duration
260-
var summary = newSummary()
261-
start := va.clk.Now()
262-
263255
auditLog := validateChallengeAuditLog{
264256
AuthzID: req.AuthzID,
265257
Requester: req.RegID,
266258
Identifier: req.Identifier.Value,
267259
Challenge: chall,
268260
}
269261

262+
var prob *probs.ProblemDetails
263+
var localLatency time.Duration
264+
var summary = newSummary()
265+
start := va.clk.Now()
266+
270267
defer func() {
271268
probType := ""
272269
outcome := fail
273270
if prob != nil {
274-
// Validation failed.
271+
// Failed to validate the challenge.
275272
probType = string(prob.Type)
276273
auditLog.Error = prob.Error()
277274
auditLog.Challenge.Error = prob
278275
auditLog.Challenge.Status = core.StatusInvalid
279-
280276
} else {
281-
// Validation passed.
277+
// Successfully validated the challenge.
282278
outcome = pass
283279
auditLog.Challenge.Status = core.StatusValid
284280
}
285-
// Always observe local latency (primary|remote).
281+
// Observe local validation latency (primary|remote).
286282
va.observeLatency(challenge, va.perspective, string(chall.Type), probType, outcome, localLatency)
287-
if va.onPrimaryVA() {
288-
// Log the MPIC summary.
283+
if va.isPrimaryVA() {
284+
// Observe total validation latency (primary+remote).
285+
va.observeLatency(challenge, all, string(chall.Type), probType, outcome, va.clk.Since(start))
289286
auditLog.MPICSummary = summary
290-
291-
if latency > 0 {
292-
// Observe total latency (primary+remote).
293-
va.observeLatency(challenge, all, string(chall.Type), probType, outcome, va.clk.Since(start))
294-
}
295287
}
296-
297-
// No matter what, log the audit log.
288+
// Log the total validation latency.
298289
auditLog.Latency = va.clk.Since(start).Round(time.Millisecond).Seconds()
299290
va.log.AuditObject("Challenge validation result", auditLog)
300291
}()
301292

302-
// Perform local validation.
293+
// Validate the challenge locally.
303294
records, localErr := va.validateChallenge(ctx, identifier, chall.Type, chall.Token, req.KeyAuthorization)
304295

305-
// Stop the clock for local validation latency (this may be remote).
296+
// Stop the clock for local validation latency.
306297
localLatency = va.clk.Since(start)
307298

308-
// Log the validation records, even if validation failed.
309-
auditLog.Challenge.ValidationRecord = records
310-
311-
// The following checks are in a specific order to ensure that the most
312-
// pertinent problems are returned first.
299+
// The following checks are performed in a specific order to ensure that the
300+
// most relevant problem is returned to the subscriber.
313301

302+
auditLog.Challenge.ValidationRecord = records
314303
if localErr == nil && !auditLog.Challenge.RecordsSane() {
315-
// Validation was successful, but the records failed sanity check.
316304
localErr = errors.New("records from local validation failed sanity check")
317305
}
318306

319307
if localErr != nil {
320-
// Validation failed locally.
308+
// Failed to validate the challenge locally.
321309
auditLog.InternalError = localErr.Error()
322310
prob = detailedError(localErr)
323311
return bgrpc.ValidationResultToPB(records, filterProblemDetails(prob), va.perspective, va.rir)
324312
}
325313

326-
if va.onPrimaryVA() {
327-
// Perform remote validation.
314+
if va.isPrimaryVA() {
315+
// Attempt to validate the challenge remotely.
328316
summary, prob = va.remoteValidateChallenge(ctx, req)
329-
330-
// Stop the clock for total validation latency.
331-
latency = va.clk.Since(start)
332317
}
318+
333319
return bgrpc.ValidationResultToPB(records, filterProblemDetails(prob), va.perspective, va.rir)
334320
}

0 commit comments

Comments
 (0)