package kmsrotationconformance import ( "bytes" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io" "regexp" "slices" "strings" "time" ) const ( CaptureSchema = "kms-rotation-capture-v1" ReportSchema = "kms-rotation-conformance-report-v1" ) var digestPattern = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`) type Capture struct { SchemaVersion string `json:"schema_version"` EvidenceSource string `json:"evidence_source"` CandidateVersion string `json:"candidate_version"` BuildDigest string `json:"build_digest"` Environment string `json:"environment"` ProviderRef string `json:"provider_ref"` WorkloadIdentityRef string `json:"workload_identity_ref"` ProviderAuditRef string `json:"provider_audit_ref"` AttestedByRef string `json:"attested_by_ref"` AttestedAt time.Time `json:"attested_at"` CollectedAt time.Time `json:"collected_at"` Rotations []RotationEvidence `json:"rotations"` } type RotationEvidence struct { SecretRef string `json:"secret_ref"` ConsumerRef string `json:"consumer_ref"` ConsumptionMode string `json:"consumption_mode"` OldVersionRef string `json:"old_version_ref"` NewVersionRef string `json:"new_version_ref"` StartedAt time.Time `json:"started_at"` CompletedAt time.Time `json:"completed_at"` NewCredentialVerified bool `json:"new_credential_verified"` OldCredentialRejected bool `json:"old_credential_rejected"` BusinessContinuityVerified bool `json:"business_continuity_verified"` SecretValueNotExported bool `json:"secret_value_not_exported"` PreRotationProbeRef string `json:"pre_rotation_probe_ref"` NewCredentialProbeRef string `json:"new_credential_probe_ref"` OldRejectionProbeRef string `json:"old_rejection_probe_ref"` ContinuityProbeRef string `json:"continuity_probe_ref"` AccessAuditRef string `json:"access_audit_ref"` } type Check struct { Code string `json:"code"` Status string `json:"status"` Detail string `json:"detail"` } type Report struct { SchemaVersion string `json:"schema_version"` Result string `json:"result"` GeneratedAt time.Time `json:"generated_at"` CandidateVersion string `json:"candidate_version"` BuildDigest string `json:"build_digest"` EvidenceScope string `json:"evidence_scope"` ExternalValidationRequired bool `json:"external_validation_required"` Environment string `json:"environment"` ProviderRef string `json:"provider_ref"` RotationCount int `json:"rotation_count"` ConsumptionModes []string `json:"consumption_modes"` InputSHA256 map[string]string `json:"input_sha256"` Checks []Check `json:"checks"` Disclaimer string `json:"disclaimer"` } func ParseCapture(content []byte) (Capture, error) { var capture Capture if err := decodeStrict(content, &capture); err != nil { return Capture{}, fmt.Errorf("decode KMS rotation capture: %w", err) } return capture, nil } func ParseReport(content []byte) (Report, error) { var report Report if err := decodeStrict(content, &report); err != nil { return Report{}, fmt.Errorf("decode KMS rotation report: %w", err) } return report, nil } func Evaluate(capture Capture, candidateVersion, buildDigest string, generatedAt time.Time) Report { candidateVersion = strings.TrimSpace(candidateVersion) buildDigest = strings.ToLower(strings.TrimSpace(buildDigest)) report := Report{ SchemaVersion: ReportSchema, Result: "PASS", GeneratedAt: generatedAt.UTC(), CandidateVersion: candidateVersion, BuildDigest: buildDigest, EvidenceScope: "LOCAL_SECRET", ExternalValidationRequired: true, Environment: capture.Environment, ProviderRef: capture.ProviderRef, RotationCount: len(capture.Rotations), ConsumptionModes: []string{}, InputSHA256: map[string]string{}, Checks: []Check{}, Disclaimer: "轮换一致性报告只证明所提供的身份、版本、认证探针、业务连续性和审计引用相互一致;不复制密钥正文,也不替代生产发布审批。", } if capture.EvidenceSource == "KMS_HSM" { report.EvidenceScope = "REAL_KMS_HSM" } add := func(code string, ok bool, detail string) { status := "PASS" if !ok { status, report.Result = "FAIL", "FAIL" } report.Checks = append(report.Checks, Check{Code: code, Status: status, Detail: detail}) } add("CAPTURE_SCHEMA", capture.SchemaVersion == CaptureSchema, "轮换证据必须使用"+CaptureSchema) add("EVIDENCE_SOURCE", capture.EvidenceSource == "LOCAL" || capture.EvidenceSource == "KMS_HSM", "证据来源必须明确为LOCAL或KMS_HSM") add("CANDIDATE_IDENTITY", candidateVersion != "" && digestPattern.MatchString(buildDigest), "候选版本和sha256构建摘要必须完整") add("CAPTURE_CANDIDATE_BINDING", capture.CandidateVersion == candidateVersion && strings.ToLower(capture.BuildDigest) == buildDigest, "轮换采集必须绑定同一候选版本和构建摘要") real := capture.EvidenceSource == "KMS_HSM" providerOK := capture.ProviderRef != "" && capture.AttestedByRef != "" && !capture.AttestedAt.IsZero() && !capture.CollectedAt.IsZero() && !capture.AttestedAt.After(capture.CollectedAt) if real { providerOK = providerOK && (capture.Environment == "STAGING" || capture.Environment == "PRODUCTION") && capture.WorkloadIdentityRef != "" && capture.ProviderAuditRef != "" } else { providerOK = providerOK && capture.Environment == "LOCAL" } add("PROVIDER_IDENTITY", providerOK, "真实证据必须绑定云KMS/HSM、工作负载身份、提供器审计和安全复核引用") rotationsOK, versionOK, newOK, oldOK, continuityOK, refsOK := len(capture.Rotations) > 0, true, true, true, true, true seenSecrets, modes := map[string]bool{}, map[string]bool{} for _, rotation := range capture.Rotations { if rotation.SecretRef == "" || rotation.ConsumerRef == "" || seenSecrets[rotation.SecretRef+"|"+rotation.ConsumerRef] || !slices.Contains([]string{"DYNAMIC_READ", "ROLLING_RESTART"}, rotation.ConsumptionMode) || rotation.StartedAt.IsZero() || rotation.CompletedAt.IsZero() || rotation.CompletedAt.Before(rotation.StartedAt) || rotation.CompletedAt.After(capture.CollectedAt) || !rotation.SecretValueNotExported { rotationsOK = false } seenSecrets[rotation.SecretRef+"|"+rotation.ConsumerRef] = true modes[rotation.ConsumptionMode] = true versionOK = versionOK && rotation.OldVersionRef != "" && rotation.NewVersionRef != "" && rotation.OldVersionRef != rotation.NewVersionRef newOK = newOK && rotation.NewCredentialVerified oldOK = oldOK && rotation.OldCredentialRejected continuityOK = continuityOK && rotation.BusinessContinuityVerified refsOK = refsOK && rotation.PreRotationProbeRef != "" && rotation.NewCredentialProbeRef != "" && rotation.OldRejectionProbeRef != "" && rotation.ContinuityProbeRef != "" && rotation.AccessAuditRef != "" } for mode := range modes { report.ConsumptionModes = append(report.ConsumptionModes, mode) } sortStrings(report.ConsumptionModes) if real { rotationsOK = rotationsOK && modes["DYNAMIC_READ"] && modes["ROLLING_RESTART"] } add("ROTATION_SCOPE", rotationsOK, "真实KMS/HSM必须覆盖逐请求动态读取和受控滚动重启两种消费模式") add("VERSION_TRANSITION", versionOK, "每次轮换必须从非空旧版本切换到不同的新版本") add("NEW_CREDENTIAL_ACCEPTED", newOK, "新凭据必须通过实际认证探针") add("OLD_CREDENTIAL_REJECTED", oldOK, "旧凭据必须在轮换窗口结束后稳定拒绝") add("BUSINESS_CONTINUITY", continuityOK, "轮换期间业务连续性探针必须通过") add("AUDIT_REFERENCES", refsOK, "每次轮换必须保存基线、新凭据、旧凭据拒绝、连续性和访问审计引用") if real && providerOK && rotationsOK && versionOK && newOK && oldOK && continuityOK && refsOK && report.Result == "PASS" { report.ExternalValidationRequired = false } return report } func SHA256(content []byte) string { digest := sha256.Sum256(content) return hex.EncodeToString(digest[:]) } func decodeStrict(content []byte, target any) error { decoder := json.NewDecoder(bytes.NewReader(content)) decoder.DisallowUnknownFields() if err := decoder.Decode(target); err != nil { return err } var trailing any if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) { return errors.New("trailing JSON data") } return nil } func sortStrings(values []string) { slices.Sort(values) }