feat: add authenticated probe evidence boundary

This commit is contained in:
Josh Creek
2026-08-31 21:34:27 +01:00
parent 91e536425d
commit 02a11704ce
4 changed files with 82 additions and 2 deletions
+43
View File
@@ -19,11 +19,13 @@ import (
const maxBodyBytes = 8 << 10
type CandidateProvider func(playerID, ticketID string) (domain.Candidate, error)
type ProbeProvider func(playerID, region string, opaqueLocation, nonce []byte, receivedAt time.Time) (domain.ProbeEvidence, []byte, error)
type Service struct {
Sessions *domain.SessionStore
Queue *domain.Queue
Candidate CandidateProvider
Probe ProbeProvider
Now func() time.Time
Proposals map[string]*domain.Proposal
RankedProfiles map[string]domain.RankedProfile
@@ -38,6 +40,7 @@ func (s *Service) Handler() http.Handler {
mux.HandleFunc("/v1/queue/", s.queueMutation)
mux.HandleFunc("/v1/proposals/", s.proposalMutation)
mux.HandleFunc("/v1/profile/ranked", s.rankedProfile)
mux.HandleFunc("/v1/probes/", s.probe)
return mux
}
@@ -232,6 +235,46 @@ func (s *Service) rankedProfile(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, rankedProfileResponse{Rating: profile.Value, RD: profile.RD, Volatility: profile.Volatility, RankedGames: profile.RankedGames, Tier: string(tier), Provisional: domain.RankedIsProvisional(profile), SeasonID: profile.LastSeasonID})
}
type probeRequest struct {
OpaqueLocation []byte `json:"opaque_location"`
Nonce []byte `json:"nonce"`
}
func (s *Service) probe(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed")
return
}
playerID, ok := s.authenticate(w, r)
if !ok {
return
}
region := strings.TrimPrefix(r.URL.Path, "/v1/probes/")
if (region != "EU" && region != "NA") || strings.Contains(region, "/") {
writeError(w, http.StatusNotFound, "not_found")
return
}
if s.Probe == nil {
writeError(w, http.StatusServiceUnavailable, "probe_unavailable")
return
}
var input probeRequest
if !decodeBody(w, r, &input) {
return
}
receivedAt := s.now()
evidence, expectedNonce, err := s.Probe(playerID, region, input.OpaqueLocation, input.Nonce, receivedAt)
if err != nil {
writeError(w, http.StatusUnprocessableEntity, "probe_unavailable")
return
}
if evidence.Region != region || domain.ValidateProbe(evidence, expectedNonce, receivedAt) != nil {
writeError(w, http.StatusUnprocessableEntity, "invalid_probe")
return
}
writeJSON(w, http.StatusAccepted, map[string]any{"region": region, "server_rtt_ms": evidence.ServerRTT.Milliseconds(), "status": "accepted"})
}
func (s *Service) authenticate(w http.ResponseWriter, r *http.Request) (string, bool) {
if s.Sessions == nil {
writeError(w, http.StatusServiceUnavailable, "auth_unavailable")