package bundle import ( "crypto/sha256" "encoding/hex" "fmt" "regexp" "strconv" "strings" ) var digestPattern = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`) func validateDigest(value string) error { if !digestPattern.MatchString(value) { return fmt.Errorf("must be lowercase sha256:<64 hex>") } return nil } func FileDigest(data []byte) string { sum := sha256.Sum256(data) return "sha256:" + hex.EncodeToString(sum[:]) } func BundleDigest(files []ManifestFile) string { canonical := CanonicalFilePayload(files) sum := sha256.Sum256([]byte(canonical)) return "sha256:" + hex.EncodeToString(sum[:]) } func CanonicalFilePayload(files []ManifestFile) string { var builder strings.Builder builder.WriteByte('[') for index, file := range files { if index > 0 { builder.WriteByte(',') } builder.WriteString(`{"path":`) builder.WriteString(strconv.Quote(file.Path)) builder.WriteString(`,"sha256":`) builder.WriteString(strconv.Quote(file.SHA256)) builder.WriteString(`,"size":`) builder.WriteString(strconv.FormatInt(file.Size, 10)) builder.WriteByte('}') } builder.WriteByte(']') return builder.String() }