package host import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostevidence" "github.com/carlosframework/platform/internal/park" "strconv" ) // A root-signed stop receipt is an alternative to the target's own release journal. func (h claimCrossBoxHandover) recoveryStopReceipt(ctx context.Context, o hostclaim.Observed, p hostclaim.AdmissionRecord, key, digest string) error { raw, _, err := h.Evidence.Get(ctx, key) if err != nil { return err } sum := sha256.Sum256(raw) if hex.EncodeToString(sum[:]) != digest { return ErrClaimStopReceipt } var r StopReceipt if json.Unmarshal(raw, &r) != nil { return ErrClaimStopReceipt } canonical, err := json.Marshal(r) if err != nil || !bytes.Equal(raw, canonical) { return ErrClaimStopReceipt } c := o.Claim stamp, err := strconv.ParseUint(c.Op.Journal[hostclaim.JournalTargetStamp], 10, 64) if err != nil || stamp == 0 || r.V != 1 || r.Kind != "stopped" || r.Host != o.Host || r.Box != p.Box || r.OperationID != c.Op.ID || r.Incarnation != c.Holder.Incarnation || r.WriterSession != p.Session || r.InstallationStamp != stamp || !park.SameRegion(c.Op.Intent["home_region"], r.Region, c.Op.Intent["to"]) || r.Enrollment != c.Op.Intent["target_enrollment"] || r.Phase != hostclaim.PhaseReleasing || key != stopEvidenceKey(o.Host, c.Op.ID, p.Box, r.ID) || r.FenceID == "" || r.ObligationID == "" || r.ClaimGen == 0 || r.EvidenceAt.IsZero() || r.RequestLimitNS != int64(hostbox.RequestLimit) || len(r.CgroupIdentities) == 0 || !stopDigest(r.InventorySHA256) || !stopDigest(r.LocalJournalSHA256) || !stopDigest(r.ProducerDrainDigest) { return ErrClaimStopReceipt } roster, err := h.Directory.Read(ctx) if err != nil { return err } box, ok := roster.Roster.Boxes[p.Box] if (c.Op.Intent["home_region"] != "" && !roster.Roster.MatchesHome(c.Op.Intent["home_region"])) || !ok || box.Enrollment != r.Enrollment || !park.SameRegion("", box.Region, r.Region) { return ErrClaimStopIdentity } sig, _, err := h.Evidence.Get(ctx, key+".sig") if err != nil { return err } return hostevidence.Verify(box.CensusPublicKey, key, raw, sig) }